65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (29投票s)

2010年2月1日

CPOL

5分钟阅读

viewsIcon

120546

正确使用提供程序工厂来创建独立于数据库的代码。

引言

我厌倦了看到人们编写自己的数据访问层和包装器来“简化”代码。问题是,它们很少真正简化代码。相反,我看到新的配置节、整个配置文件以及全新的对象结构,试图让事情变得更好,而 .NET Framework 已经提供了大部分我一遍又一遍看到的功能。

所以,本文旨在让事情更清晰,真正简化。

配置文件

App.ConfigWeb.Config 中,有一个名为connectionStrings的可用节。里面有add元素,这些是单独的数据库连接。每个add元素都有三个重要部分:nameconnectionStringproviderName

  • 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() 

最后一个技巧

我看到许多工具包、文章和技巧,内容是如何编写InsertUpdate语句,但 .NET 也为您提供了该功能;代码就在这里。利用DbCommandBuilder对象,连接的DataTable可以使用这些对象为InsertUpdateDelete创建 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 日:初始版本
© . All rights reserved.