从非控制情况使用SqlDataSource

时间:2013-12-18 21:26:57

标签: c# asp.net .net sqldatasource

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

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是否有缺点?

3 个答案:

答案 0 :(得分:1)

嗯,没有硬性规则可以阻止任何人进行此类实施。

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

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

答案 1 :(得分:1)

考虑到替换此代码是多么容易,同时消除了使用动态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;--");

修改
this answer的帮助下更新以支持不同提供商的参数。

答案 2 :(得分:-1)

我看到的唯一问题是

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

使用EL,您可以拨打电话,并在Dataset中加载2,3,4个表格。使用您的方法,这是不可能的,当时只有一个。

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

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

看看这张照片:

enter image description here

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

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

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