作为我在所有业务应用程序中使用的常用实用程序的一部分,我有这段代码......
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是否有缺点?
答案 0 :(得分:1)
嗯,没有硬性规则可以阻止任何人进行此类实施。
但是,在执行此实施之前,需要回答以下几个问题。
答案 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和客户端相关的东西。在分层蛋糕设计中,这就像它的顶部,数据访问在底部。所有参考文献[另存为“普通”]应该从底部到顶部移动,而你的方向相反。
看看这张照片:
这是来自微软。你看到了“蛋糕”的层次。所有参考文献都在上升。你做了什么 - 你使用了与UI相关的组件并在其中编写了数据访问。
您可以将其称为基于意见的 - 但这种观点是软件开发中的标准做法和模式。你的问题也是基于意见的。因为您可以将所有内容编写为单个文件,单个类,并且它将起作用。如果您愿意,可以在Asp.net应用程序中设置对System.Windows.Forms
的引用。 从技术上讲,这是可能的,但这是非常糟糕的做法。
您的应用程序现在具有有限的可重用性。如果您编写需要使用相同数据访问的WPF组件或服务,该怎么办?您必须将所有System.Web
拖入其中吗?