不要硬编码您的数据提供程序






4.81/5 (29投票s)
正确使用提供程序工厂来创建独立于数据库的代码。
引言
我厌倦了看到人们编写自己的数据访问层和包装器来“简化”代码。问题是,它们很少真正简化代码。相反,我看到新的配置节、整个配置文件以及全新的对象结构,试图让事情变得更好,而 .NET Framework 已经提供了大部分我一遍又一遍看到的功能。
所以,本文旨在让事情更清晰,真正简化。
位
配置文件
在App.Config 或 Web.Config 中,有一个名为connectionStrings
的可用节。里面有add
元素,这些是单独的数据库连接。每个add
元素都有三个重要部分:name
、connectionString
和providerName
。
name
不言而喻,这是连接的名称。connectionString
也显而易见,这是连接字符串。providerName
并不明显,似乎每个人都忽略了它。这使我们能够动态创建所需的对象,使用任何正确安装的DataProvider
。我稍后会回到这一点。
代码片段
从 .NET 2.0 开始,所有 .NET DataProvider
都继承自System.Data.Common
中的一组特定类。所有这些都可以通过提供程序工厂动态创建。下表是更熟悉的对象与我们需要的引用的基类之间的快速交叉引用。
基对象 | SqlClient 对象 | OleDb 对象 |
DbConnection | SqlConnection | OleDbConnection |
DbCommand | SqlCommand | OleDbCommand |
DbParameter | SqlParameter | OleDbParameter |
DbConnectionStringBuilder | SqlConnectionStringBuilder | OleDbConnection StringBuilder |
DbProviderFactory | SqlClientFactory | OleDbFactory |
还有其他类,但这些是我将要介绍的。让我们开始吧。
使用代码片段
配置
这是一个简单的App.Config,其中包含大多数流行数据库的示例连接:SQL Server、Oracle、PostgresSQL、MySQL 和 MS Access。
<?xml version="1.0"?>
<configuration>
<appSettings>
<!-- all your application settings -->
</appSettings>
<connectionStrings>
<add name="blah" providerName="System.Data.SqlClient"
connectionString="Data Source=(local);Initial Catalog=cars;Integrated Security=SSPI"/>
<!-- examples of other providers OleDb, Oracle (using TNS.ORA),
MySQL and Npgsql with sample connectionstrings from connectionstrings.com -->
<!-- <add name="blah" providerName="Npgsql"
connectionString="Server=127.0.0.1;Port=5432;Database=myDataBase;
User Id=myUsername;Password=myPassword;"/> -->
<!-- <add name="blah" providerName="System.Data.OleDb"
connectionString="Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=C:\mydatabase.mdb;User Id=admin;Password=;"/> -->
<!-- <add name="blah" providerName="MySql.Data.MySqlClient"
connectionString="User ID=root;Password=myPassword;Host=localhost;Port=3306;
Database=myDataBase; Direct=true;Protocol=TCP;Compress=false;Pooling=true;
Min Pool Size=0;Max Pool Size=100;Connection Lifetime=0;"/> -->
<!-- <add name="blah" providerName="Oracle.DataAccess.Client"
connectionString="Data Source=TORCL;User Id=myUsername;Password=myPassword;"/> -->
</connectionStrings>
</configuration>
正如您所见,通过更改连接字符串,我们可以连接到不同的数据库。这里没有新东西。我们可以使用 OLEDB 和 ODBC 来完成,但使用这两种技术,我们都要经过代码层。使用托管提供程序,它们很可能是 100% 托管代码,这意味着没有层可以穿过,因此访问速度更快。此外,通过使用托管提供程序,我们将从数据库中获得最大的性能和灵活性(如命名参数)。
这里需要注意的一点是,其中一些提供程序是第三方提供程序,并且必须安装。如果您使用它们,您可能需要自己进行一些安装。
重要提示:请不要使用 Microsoft Oracle Provider (System.Data.OracleClient
),它正在被淘汰。尽管如此,如果您使用本文中的信息,只需更改配置文件即可。
建立连接
现在我们有了一个可用的配置,让我们创建一个到已配置数据库的连接。
//get the information out of the configuration file.
ConnectionStringSettings connectionStringSettings =
ConfigurationManager.ConnectionStrings["blah"];
//get the proper factory
DbProviderFactory factory =
DbProviderFactories.GetFactory(connectionStringSettings.ProviderName);
//create a command of the proper type.
DbConnection conn = factory.CreateConnection();
//set the connection string
conn.ConnectionString = connectionStringSettings.ConnectionString;
//open the connection
conn.Open();
现在,我可以连接到任何数据库,只需提供正确的提供程序和连接字符串。
修改和验证连接字符串
让我们回到之前的代码。是的,它有效,但如何确保某些功能已打开呢?在此示例中,我将重点介绍 SQL Server,以及我喜欢的一个功能:MultipleActiveRecordSets
。
//get the information out of the configuration file.
ConnectionStringSettings connectionStringSettings =
ConfigurationManager.ConnectionStrings["blah"];
//get the proper factory
DbProviderFactory factory =
DbProviderFactories.GetFactory(connectionStringSettings.ProviderName);
string connectionString = connectionStringSettings.ConnectionString;
DbConnectionStringBuilder csb = factory.CreateConnectionStringBuilder();
//make sure it got created
if (csb != null)
{
//let the ConnectionStringBuilder parse the current connection string
csb.ConnectionString = connectionString;
//if this provider supports this setting, set it.
if (csb.ContainsKey("MultipleActiveResultSets"))
{
csb["MultipleActiveResultSets"] = true;
}
//use the new modified connection string
connectionString = csb.ConnectionString;
}
//create a command of the proper type.
DbConnection conn = factory.CreateConnection();
//set the connection string
conn.ConnectionString = connectionString;
//open the connection
conn.Open();
现在,如果我的连接支持它,MultipleActiveRecordSets
将被设置为true
。这与我们可能使用的任何提供程序都无关。不幸的是,一些提供程序不支持正确的键,而像“Microsoft SQL Server Compact Data Provider”这样的提供程序根本不提供连接字符串生成器对象。
执行查询
为求清晰起见,我将继续使用上面的函数。
//by using the CreateCommand function on the connection rather than
//using factory.CreateCommand()
//the connection is automatically associated with the new command
DbCommand cmd = conn.CreateCommand();
cmd.CommandText = "select * from Vehicle";
//execute a datareader, closing the connection when all the data is read from it
using(DbDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
while(dr.Read())
{
//do something with the data
}
}
创建/使用参数
当我们使用factory.CreateParameter()
创建一个参数时,我们会得到一个DbParameter
对象。嗯,这没问题,但有些人会对此感到困惑。您看到,您不再能够访问特定于提供程序的DbType
,并且即使有一些技巧可以获取正确的设置,它的帮助也是有限的,因为每个提供程序使用不同的属性名来设置其自身的enum
。相反,我们需要做的是使用基类中定义的DbType
,如下所示:
//create a 16 byte random "salt" to put into the database
byte[] b = new byte[16];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(b);
//createa parameter and fill it
p = factory.CreateParameter();
p.DbType = DbType.Binary;
p.Value = b;
p.ParameterName = "salt";
获取所有已安装的数据提供程序
这里有一行简单的代码可以查看已安装了哪些提供程序,以防您想通过让用户输入连接信息来动态创建连接,或者也许是双重检查已配置的提供程序是否已安装。
DataTable dt = System.Data.Common.DbProviderFactories.GetFactoryClasses()
最后一个技巧
我看到许多工具包、文章和技巧,内容是如何编写Insert
和Update
语句,但 .NET 也为您提供了该功能;代码就在这里。利用DbCommandBuilder
对象,连接的DataTable
可以使用这些对象为Insert
、Update
和Delete
创建 SQL 语句。
using (DbCommand cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = "select * from " + tableName;
cmd.CommandTimeout = 10;
using (DbDataAdapter da = factory.CreateDataAdapter())
{
DbCommandBuilder cb = factory.CreateCommandBuilder();
da.SelectCommand = cmd;
DataTable dt = new DataTable();
da.FillSchema(dt, SchemaType.Source);
cb.DataAdapter = da;
DbCommand[] cmds = new DbCommand[3]
cmds[0] = cb.GetUpdateCommand();
cmds[1] = cb.GetDeleteCommand();
cmds[2] = cb.GetInsertCommand();
}
}
关注点
以这种方式正确使用工厂模式可以防止硬编码提供程序。更好的是,如果您使用的是 Oracle、MySQL 或 Npgsql 等第三方提供程序,如果我们不直接引用该程序集(DLL),我们就可以在不破坏代码的情况下升级该提供程序。
在machine.config中有一个名为“LocalSqlServer
”的连接字符串;默认提供程序(如预期的那样)是“System.Data.SqlClient
”,连接字符串是“data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true
”,这对于许多 Web 应用程序来说非常好。实际上,没有任何理由不能为 Windows 应用程序使用该连接字符串。在尝试执行某些操作之前,您需要注意此额外连接字符串以及您继承的其他连接字符串(尤其是在 Web 应用程序下)。
foreach(var cs in ConfigurationManager.ConnectionStrings)
{
//create and test each connection
}
显然,这并不是让您的代码实现数据库独立的唯一必要部分,但它是第一部分。其他部分可以在我的一些其他文章中看到,这里、这里和这里。
如果您需要帮助创建连接字符串,http://connectionstrings.com 是一个很好的资源。
历史
- 2010 年 2 月 1 日:初始版本