繁体   English   中英

从非控制情况使用SqlDataSource

[英]Use of SqlDataSource From Non-Control Situations

作为我在所有业务应用程序中使用的常用实用程序的一部分,我有这个代码......

using System.Web.UI.WebControls;

public class Database
    {
    /// <summary>
    /// Creates a DataView object using the provided query and an SqlDataSource object.
    /// </summary>
    /// <param name="query">The select command to perform.</param>
    /// <returns>A DataView with data results from executing the query.</returns>
    public static DataView GetDataView(string query)
        {
        SqlDataSource ds = GetDBConnection();
        ds.SelectCommand = query;
        DataView dv = (DataView)ds.Select(DataSourceSelectArguments.Empty);
        return dv;
        }

     /// <summary>
     /// Creates a SqlDataSource object with initialized connection string and provider
     /// </summary>
    /// <returns>An SqlDataSource that has been initialized.</returns>
    public static SqlDataSource GetDBConnection()
        {
        SqlDataSource db = new SqlDataSource();
        db.ConnectionString = GetDefaultConnectionString(); //retrieves connection string from .config file
        db.ProviderName = GetDefaultProviderName(); //retrieves provider name from .config file
        return db;
        }
   }

然后,在我的项目中,要从数据库中检索数据,我会有一些代码,如...

DataView dv=Database.GetDataView("select mycolumn from my table");
//loop through data and make use of it

我以这种方式从人们那里获得了一些使用SqlDataSource的热量。 人们似乎并不喜欢我纯粹从代码中使用Web控件而不是将其放在ASPX页面上。 它看起来并不正确,但他们无法告诉我一个缺点。 那么,有缺点吗? 这是我的主要问题。 因为如果有很多缺点,我可能不得不改变我开发的许多内部应用程序的方式。

我的数据库类甚至可以在非ASP.NET的情况下工作,只要我添加System.Web程序集即可。 我知道它的封装尺寸略有增加,但我觉得这对于我正在编写的应用类型是值得的。 说WPF / Windows Forms / Console程序使用SqlDataSource是否有缺点?

好吧,没有硬性规则阻止任何人做这样的实现。

但是,在执行该实现之前,需要回答以下几个问题。

  1. 这个用法线程安全吗? (因为很有可能多个消费应用程序可以进行相同的调用。
  2. 是否存在分层差异(UI.Control在数据层中使用)?
  3. 如果在下一个框架版本中该控件变得过时/受限制怎么办?

考虑到替换此代码是多么容易,同时消除了使用动态SQL查询传递参数的诱惑,我认为问题应该是:保持代码原样是否有任何好处?

例如:

public static class Database
{
    private static readonly Func<DbCommandBuilder, int, string> getParameterName = CreateDelegate("GetParameterName");
    private static readonly Func<DbCommandBuilder, int, string> getParameterPlaceholder = CreateDelegate("GetParameterPlaceholder");

    private static Func<DbCommandBuilder, int, string> CreateDelegate(string methodName)
    {
        MethodInfo method = typeof(DbCommandBuilder).GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder, new Type[] { typeof(Int32) }, null);
        return (Func<DbCommandBuilder, int, string>)Delegate.CreateDelegate(typeof(Func<DbCommandBuilder, int, string>), method);
    }

    private static string GetDefaultProviderName()
    {
        ...
    }

    private static string GetDefaultConnectionString()
    {
        ...
    }

    public static DbProviderFactory GetProviderFactory()
    {
        string providerName = GetDefaultProviderName();
        return DbProviderFactories.GetFactory(providerName);
    }

    private static DbConnection GetDBConnection(DbProviderFactory factory)
    {
        DbConnection connection = factory.CreateConnection();
        connection.ConnectionString = GetDefaultConnectionString();
        return connection;
    }

    public static DbConnection GetDBConnection()
    {
        DbProviderFactory factory = GetProviderFactory();
        return GetDBConnection(factory);
    }

    private static void ProcessParameters(
        DbProviderFactory factory, 
        DbCommand command, 
        string query, 
        object[] queryParameters)
    {
        if (queryParameters == null && queryParameters.Length == 0)
        {
            command.CommandText = query;
        }
        else
        {
            IFormatProvider formatProvider = CultureInfo.InvariantCulture;
            DbCommandBuilder commandBuilder = factory.CreateCommandBuilder();
            StringBuilder queryText = new StringBuilder(query);

            for (int index = 0; index < queryParameters.Length; index++)
            {
                string name = getParameterName(commandBuilder, index);
                string placeholder = getParameterPlaceholder(commandBuilder, index);
                string i = index.ToString("D", formatProvider);

                command.Parameters.AddWithValue(name, queryParameters[index]);
                queryText = queryText.Replace("{" + i + "}", placeholder);
            }

            command.CommandText = queryText.ToString();
        }
    }

    public static DataView GetDataView(string query, params object[] queryParameters)
    {
        DbProviderFactory factory = GetProviderFactory();

        using (DbConnection connection = GetDBConnection(factory))
        using (DbCommand command = connection.CreateCommand())
        {
            command.CommandType = CommandType.Text;
            ProcessParameters(factory, command, query, queryParameters);

            DbDataAdapter adapter = factory.CreateDataAdapter();
            adapter.SelectCommand = command;

            DataTable table = new DataTable();
            adapter.Fill(table);
            return table.DefaultView;
        }
    }
}

使用此版本,您现在可以简单安全地传递参数,而无需依赖自定义代码来尝试阻止SQL注入:

DataView dv = Database.GetDataView(
   "select mycolumn from my table where id = {0} and name = {1}",
   1234, "Robert');DROP TABLE Students;--");

编辑
此答案的帮助下更新以支持不同提供商的参数。

我看到的唯一问题是

(1)这就像重新发明轮子一样。 FW3.5有企业库v5,FW4.5有v6,它有数据访问组件。 用那个。

使用EL,您可以进行调用并在Dataset集中加载2,3,4个表。 使用您的方法,这是不可能的,当时只有一个。

企业库是Microsoft提供的完整数据访问套件。 它会处理所有细节,您只需要调用数据即可。 这是完整的数据访问层。 如果你看得更深,EL允许集成数据和缓存,以及其他东西。 但是你不必使用你不需要的东西。 如果您需要数据访问,则只能使用它。

并且(2)通常,在参考中编写具有高级组件的低级组件不是一个好主意。 任何System.Web....都是UI和客户端相关的东西。 在分层蛋糕设计中,这就像它的顶部,数据访问在底部。 所有参考文献[另存为“普通”]应该从底部到顶部,并且它在相反的方向上。

看这张图片:

在此输入图像描述

这是来自微软。 你看到了“蛋糕”的层次。 所有参考文献都在上升。 你做了什么 - 你采用了与UI相关的组件并在其中编写了数据访问。

您可以将其称为基于意见的 - 但这种观点是软件开发中的标准实践和模式。 你的问题也是基于意见的。 因为您可以将所有内容编写为单个文件,单个类,并且它将起作用。 如果需要,可以在Asp.net应用程序中设置对System.Windows.Forms引用。 从技术上讲,这是可能的,但这是非常糟糕的做法。

您的应用程序现在具有有限的可重用性。 如果您编写需要使用相同数据访问的WPF组件或服务,该怎么办? 你必须将所有System.Web拖入其中?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM