Enterprise Library 数据访问应用程序块简化了实现常规数据访问功能的开发任务。应用程序可以在各种场景中使用此应用程序块,例如为显示而读取数据、传递数据穿过应用程序层( application layers)、以及将修改的数据提交回数据库系统。应用程序块包含对存储过程和内联 SQL 的支持。常规内部(housekeep)处理,如管理连接、创建并缓存参数,都封装在应用程序块的方法中。换句话说,数据访问应用程序块在简单易用的类中提供了对 ADO.NET 的最常用的特性的访问;这提高了开发人员的工作效率。
ADO.NET 2.0 提供了如 DbCommand 类和 DbConnection 这样的类,这些类有助于从任何特定数据库实现中抽象出数据提供程序。数据访问应用程序块利用了这些类,并且提供了加强支持数据库特定特性封装的模型,例如参数发现和类型转换。因此,应用程序可以在不修改客户代码的情况下从一个数据库移植到另一个数据库。数据访问应用程序块包括一个抽象基类,它定义了一个通用的接口,并提供了许多在 ADO.NET 2.0 中可用的数据访问方法所需要的实现。
应用程序块还包含了专用于 Microsfot SQL Server、Microsoft SQL Server CE、和 Oracel 的类。这些类完成对特定数据库类型的操作。应用程序的代码只为一种数据库而编写,例如 SQL Server,可以看到有许多为另一种数据库编写的代码是一样的,例如 Oracle 。
数据访问应用程序块的另一个特性是,应用程序代码可以由一个 ADO.NET 连接字符串的名字,如"Customer" 或者 "Inventory" ,而引向一个特定的数据。应用程序代码可以指定一个数据库命名实例,并传递此参数到 DatabaseFactory.CreateDatabase 方法。每个命名数据库都有连接字符串保存在配置文件中。通过修改配置文件中的设置,开发人员可以在不同的数据库配置下使用应用程序而不需要重新编译代码。
数据访问应用程序块提供了下列好处:
普通场景
开发人员经常编写使用数据库的应用程序。因为它太普遍了,开发人员可能会发现他们为每个应用程序在重复编写同样的代码。另外,这些应用程序可能需要与不同的数据库一起工作。尽管任务是相同的,代码也必须适配以适应每个数据库的编程模型。数据访问应用程序块通过提供完成最常用的数据访问任务的逻辑来解决这些问题。开发人员仅需要做如下事情:
示例应用程序代码
下列代码展示了如何调用一个存储过程并返回一个 DataSet。
Database db = DatabaseFactory.CreateDatabase();
DbCommand dbCommand = db.GetStoredProcCommand("GetProductsByCategory");
// Retrieve products from category 7.
db.AddInParameter(dbCommand, "CategoryID", DbType.Int32, 7);
DataSet productDataSet = db.ExecuteDataSet(dbCommand);
从3.1版本之后修改的特性
一般情况下,使用数据访问应用程序块的早期版本发布构建的应用程序不需要修改任何代码就能使用 May 2008 发行的功能。可能需要更新引用以指向新的程序集,并更新配置文件以引用正确的应用程序版本。然而在version 3.1 (May 2007)所做的一些修改会影响到你从早期版本迁移到现在的版.下面描述这些改变:
.NET Framework 2.0 TransactionScope 类
已经修改某些 Database 类的方法以使用 .NET Framework 2.0 的 TransactionScope 类。这些方法,例如 ExecuteNonQuery ,已通过用 GetOpenConnection 方法替换掉 GetConnection 方法来修改为识别 TransactionScop 实例的有效时机。如果编写了一个继承自 Database 类的类,将需要考虑这些变化来重写代码。如果继续使用 GetConnection 方法,将会收到一个编译错误。
另外,如果应用程序使用了 ExecuteXmlReader 方法,可能需要重写代码以测试查看在关闭连接前 TransactionScope 实例是否是有效的。
SQL Server Compact Edition
Enterprise Library 3.1 – May 2007 和之后版本支持 SQL Server Compact Edition (CE)。SQL Server CE 提供了精减的关系数据库的特性,以用于桌面和移动应用程序,这些程序需要本地数据库存储但又不需要完整的 SQL Server 的功能。
何时使用数据访问应用程序块
数据访问应用程序块包含少量简化绝大多数访问数据库的普通方法的方法。每个方法都封装了获取数据所需要的逻辑以及管理数据库连接。如果应用程序中使用标准的数据访问技术就可以考虑使用应用程序块。
应用程序块补充了 ADO.NET 2.0 中的代码,以让你在不同的数据库类型中使用同样的代码。它包含了用于 SQL Server 和 Oracle 数据库的类。这些类包含了提供特定数据库特性如参数处理和游标的实现的代码。另外,GenericDatabase 类允许使用应用程序块与任何配置的 ADO.NET 2.0 DbProviderFactory 对象一起使用。可以通过添加新的惟数据库特定特性或者提供已有数据库自定义实现的数据库类型来扩展应用程序块。仅仅需要在在一个用于目标数据库的 ADO.NET 2.0 DbProviderFactory 类。
何时直接使用 ADO.NET
数据访问应用程序块是 ADO.NET 的一个补充;而不是替换。应用程序块提供了简化和方便,同时帮助开发人员以最佳实践使用 ADO.NET 。如果应用程序需要以特殊的方法获取数据,或者代码需要定制以利用特定于特定数据库的特性,使用 ADO.NET 可能更适合。
使用数据访问应用程序块开发应用程序
首先解释了如何配置应用程序块并将它添加到应用程序中。然后,在关键场景中,解释了如何在特定场景中使用应用程序块,例如获取单个项或者使用 DataSet 对象获取多行。最后,在开发任何细节中,给出了关于如连接管理、参数处理和处理异常等方面的更多信息。本主题假设使用的是原始的应用程序块,即没有扩展的。要学习如何添加功能,请参见扩展和修改数据访问应用程序块。
Database=Database;Server=(local)\SQLEXPRESS;Integrated Security=SSPI;
Database=Database;Server=(local)\SQLEXPRESS;Integrated Security=SSPI
Data Source='C:\MyApp\MyDatabase.sdf'
下一过程解释了如何通过关联提供程序和数据库全名称来添加自定义的提供程序映射。
配置自定义的提供程序
可以手工编辑 XML 数据,但 Enterprise Library 极大的简化了此任务。如果选择手工编辑 XML ,则要使用包含在本主题中的模式信息。
配置文件有如下的节处理程序声明:
<configSections> <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> <section name="oracleConnectionSettings" type="Microsoft.Practices.EnterpriseLibrary.Data.Oracle.Configuration.OracleConnectionSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </configSections>
节处理程序声明包括配置设置节的名称和处理节中配置数据的节处理程序的类名。第一个配置设计节的名称为 dataConfiguration ,节处理程序的类名为 DatabaseSettings (在 Microsoft.Practices.EnterpriseLibrary.Data.Configuration 命名空间中)。
第二个配置设置节是 oracleConnectionSettings 。节处理程序类的名称为 OracleConnectionSettings (在 Microsoft.Practices.EnterpriseLibrary.Data.Oracle.Configuration 命名空间中 )。
connectionStrings 元素
connectionStrings 元素列出了可被应用程序使用的数据库连接,此元素不是必须的。
属性和子元素
下面的节描述了 connectionStrings 元素的属性和子元素。
add 子元素
add 元素是 connectionStrings 元素的子元素。add 元素添加一个数据库连接,此元素不是必须的,可以有多个 add 元素。
属性 | 描述 |
name | 由应用程序访问的数据库实例的逻辑名称。在节中,名称必须是唯一的。此属性是必须的。 |
providerName | 提供程序的名称。默认情况下,提供程序的名称定义在 Machine.config 文件中。providerName 名称必须是一个在 DBProviderFactory 类中指定的提供程序的名称。此属性是必须的。 |
connectionString | 可用于被选的提供程序的连接字符串,此属性是必须的。 |
dataConfiguration 元素
元素仅在需要指定一个默认数据库或者自定义的提供程序的映射时有用。
表 2 列出了 dataConfiguration 元素的属性。
属性 | 描述 |
defaultDatabase | 连接字符串实例的名称,此实例仅用于应用程序调用不带实例名的 DatabaseFactory.CreateDatabase 方法。 |
providerMappings 子元素
这是一个 dataConfiguration 元素的子元素,只有在通过派生自 ADO.NET 的 Database 类而不是 GenericDatabase 类的提供程序时才需要指定提供程序的映射。SQL Server 和 Oracle 数据库默认已配置,所以不需要再在此节中指定。指定在此节中的一个数据库示例是 SQL Server CE 。
add 子元素
add 是 providerMappings 元素的子元素。add 元素添加一个数据库连接。此元素不是必须的,可以有多个 add 元素。
表3 列出了素的属性
属性 | 描述 |
databaseType | 派生自 Database 类的类型名,此属性是必须的。 |
name | 使用的 ADO.NET 提供程序类型名称。名称必须在 DBProviderFactory 类中指定。此属性是必须的。 |
oracleConnectionSettings | 只有在需要指定 Oracle 数据库包映射时才需要此元素。 |
add 子元素
add 元素是 oracleConnectionSettings 元素的子元素。add 元素添加一个 Oracle 连接字符串实例。此元素不是必须的。可以有多个 add 元素。
属性
表 4 列出了 add 元素的属性
属性 | 描述 |
name | Oracle 连接字符串实例的名称。此属性是必须的。 |
packages 子元素
这是 add 元素的一个子元素,指定一个 Oracle 的包。此元素是必须的。
add 子元素
这是 packages 元素的一个子元素。 add 元素添加一个 Oracle 的包。此元素不是必须的。可以有多个 add 元素。
属性
表 5 列出了 add 子元素的属性。
属性 | 描述 |
Name | Oracle 包的名称。此属性是必须的。 |
Prefix | Oracle 包的前缀。此属性是必须的。 |
using Microsoft.Practices.EnterpriseLibrary.Data; using System.Data;
创建 Database 对象
所有数据访问方法都相对于 Database 的对象。可以使用 DatabaseFactory 来创建 Database 对象。由工厂生成的 Database 对象的特定类型是由应用程序的配置信息决定的。
可以使用配置控制台指定一个默认的数据库实例。在不传递数据库实例名调用 CreateDatabase 方法时,DatabaseFactory 创建由默认实例指定的 database 。下列应用程序代码展示了如何创建一个默认实例的 Database 对象。
Database db = DatabaseFactory.CreateDatabase();
另一种方法是,应用程序代码可以指定一个命名的数据库实例。例如,如果使用配置控制台创建了名为 "Sales" 的实例,则为特定实例创建 Database 对象的代码可能如下。
Database db = DatabaseFactory.CreateDatabase("Sales");
如果要创建的数据库的连接字符串是已知的,也要以放弃应用程序的配置信息,而是使用构造函数直接创建 Database 对象。因为 Database 类是一个抽象基类,所以必须构建一个它的派生类型。派生的 Database 类型决定了 ADO.NET 数据提供程序。例如,SqlDatabase 类使用 SqlClientFactory 提供程序,SqlCeDatabase 使用 SqlCeProviderFactory 提供程序,以及 OracleDatabase 使用 OracleClientFactory 提供程序。为连接字符串构建正确类型的 Database 类是你的责任。
下列代码使用提供的连接字符串创建了一个 SqlDatabase 对象。
// Assume the method GetConnectionString exists in your application and // returns a valid connection string. string myConnectionString = GetConnectionString(); SqlDatabase sqlDatabase = new SqlDatabase(myConnectionString);
如果通过不是 ADO.NET SQL 数据提供程序或者 Oracle 数据提供程序的数据提供程序使用一个连接字符串,可以创建一个 GenericDatabase 对象。在创建 GenericDatabase 对象时,必须支持 DbProviderFactory 对象。
选择适当的重载方法
每个数据访问方法都有多个重载。下列描述和指南可以帮助你选择合适的重载:
创建 Database 对象的细节
可以使用工厂创建一个 Database 对象或者直接构建一个。工厂使用配置信息决定连接字符串、ADO.NET 数据提供程序和要构建的适当的派生自数据访问应用程序块 Database 的对象。另一种时,传递所有需要的信息给对象的构造函数直接创建 Database 对象。
在要使用由 Enterprise Library 支持的保存在某个位置的配置信息时或者使用由 ADO.NET 管理的连接字符串时使用工厂。例如,使用工厂用保存在应用程序配置文件中的 <connectionStrings> 节中的连接字符串信息创建 Database 对象。也可以使用工厂用保存在另一个配置源中的连接字符串构建一个 Database 对象。必须使用另一个默认配置源来配置应用程序,以允许工厂用保存在那个配置源中的连接字符串创建对象。在从不是默认配置源中的某些源中获取连接字符串时,可以使用构建函数。
CreateDatabase 方法是 DatabaseFactory 类的一个静态方法。工厂基于配置文件中的信息创建一个正确的 database 类,并返回基类的子类的对象:Database 到客户代码。除非需要特定于特殊数据库类型的命令,例如 SQL Server ,否则应该仅使用 Database 基类的的可用方法以保持应用程序所使用的数据库是不可知的。特定的数据库派生类型的创建对应用程序代码而言是透明的,因此,同样可以不需要考虑所使用的数据库类型。
可以使用 CreateDatabase 方法来基于默认配置指定创建的数据库类型。通过修改默认配置,可以使未经修改的应用程序运行于不同的数据库。
也可以使用命名的数据库实例,例如在应用程序中的 "Customers" 。工厂使用配置文件中的连接字符串来查找与特定命名实例相关的信息以创建正确的数据库类型。
如果需要使用某个或另一个数据库特定的命令,就必须通过向下类型转换(downcasting)指定期望由工厂创建的数据库类型。
最后,可以忽略应用程序的配置信息直接创建一个 DataBase 对象子类型的 Database 对象。要做到这一点,必须知道要创建的数据库的类型,以及连接字符串和其他任何子类型需要的信息。
创建默认数据库
不指定参数的调用工厂的 CreateDatabase 方法将创建一个默认的 database 。配置文件决定哪个命名实例是默认实例。可以使用配置控制台修改默认实例。
下列代码展示了如何创建一个标记为默认实例的 database 对象。
Database dbSvc = DatabaseFactory.CreateDatabase();
注意
如果配置文件没有指定默认实例,并且客户代码在调用 CreateDatabase 方法时也没有指定参数,应用程序块将抛出异常。
使用实例
要使用实例,可以通过逻辑名称在应用程序代码中引用 database ,并且修改数据库配置信息(如位置或连接字符串信息)而不用重新编译代码。
下列示例展示了如何使用名称“Sales”创建 database 。
// Use a named database instance that refers to an arbitrary database type,
// which is determined by configuration information.
Database myDb = DatabaseFactory.CreateDatabase("Sales");
创建特定的 Database 类型
如果必须使用专用于特定数据库类型的方法,在创建数据库时可以指定 database 类型。下列代码要求工厂创建一个 SqlDatabase 对象。
// Create a SQL database.
SqlDatabase dbSQL = DatabaseFactory.CreateDatabase("Sales") as SqlDatabase;
同样,要创建一个 Oracle 数据库,使用 OracleDatabase 。要创建一个 SQL CE 数据库,使用 SqlCeDatabase 类型。
不使用配置创建一个数据库
可以通过提供一个连接字符串给 database 类构造函数来不使用配置数据的创建一个数据库对象。下列代码展示了如何创建一个 SqlDatabase 对象。
// Assume your application contains the routine GetConnectionString.
string myConnectionString = GetConnectionString();
SqlDatabase sqlDatabase = new SqlDatabase(myConnectionString);
GenericDatabase db = new GenericDatabase(connectionString, OdbcFactory.Instance);
使用 SQL Server CE
SQL Server CE 是一个小型的、进程内的数据库,它提供了关系数据库的必须功能,目的在于需要本地数据存储但不需要 SQL Server 的完整功能的桌面和移动应用程序。每个数据库都保存在一个文件中,默认情况下,扩展名为 .sdf 。使用 CreateFile 方法可以创建一个新的空数据库,此方法使用来自连接串的文件名。
对于 SQL Server CE ,打开一个连接就是打开数据库文件。结果是,为每个请求创建和释放连接将非常缓慢。为了避免这些性能问题,使用 SQL Server CE 的应用程序通常在使用数据库期间尽可能长的保存连接打开。
在第一次调用 Database 类的方法时,提供程序创建一个附加的 “keep alive”连接,它在内存中保持了数据库引擎。应用程序为每个 Database 类方法的调用打开和关闭其他的连接,但关闭这些连接不会关闭 “keep alive”连接。
要打开一个数据库,使用 CreateConnection 方法打开到它的连接。这个方法创建了 “keep alive”连接。当使用完数据库后,必须使用 CloseSharedConnection 方法关闭到数据库的 “keep alive”连接。对于每个连接字符串仅有一个 “keep alive”连接,尽管对于同样的连接字符串可以有多个打开的连接。
因为 SQL Server CE 是一个进程内的数据库,对数据库的多个调用将是快而有效的。SQL Server CE 不支持存储过程。如果试图使用任何 Execute 方法,如 ExecuteScalar 和 ExecuteNonQuery ,以一个存储过程做为参数的话,应用程序块将抛出异常。不用存储过程,可以使用内联的 SQL 语句来代替。在此有些 Execute 方法的重载是接受一个 SQL 语句为参数的。因为存储过程不受支持的同样原因,只能在一个请求中发送一条 SQL 语句。
SQL Server CE 有一个名为 SqlCeResultSet 的特殊结果集。这是查询返回的结果集类型。它支持在数据库中的查询、前向和后向移动、以及修改数据。
关于 SQL Server CE 的一般信息,请参见 Microsoft Web 站点上的 Microsoft SQL Server: SQL Server 2005 Compact Edition 。相关 API 的信息,请参见 MSDN 上的 System.Data.SqlServerCe 命名空间页。
注意 SQL Server CE 仅在完全信任环境中操作。
通过 TransactionScope 类使用 Oracle
尽管可以通过 Oracle 客户端来使用 TransactionScope 类,但事务总是被处理为分布式事务而不是轻量级的事务。分布式事务有较高的性能开销。
.NET Framework 托管的用于 Oracle 的提供程序需要一个名为 oramts.dll 的文件以使用 TransactionScope 类。更多信息,请参见 Microsoft 帮助和支持 Web 站点。
如果通过 Microsoft 事务服务器使用 Oracle,请参见 Oracle Web 站点上的 Oracle Services for MTS以获得适当的下载。
使用提示
DatabaseFactory 对象基于 ADO.NET 的 DbProviderFactory 对象决定哪种 Database 对象被创建,DbProviderFactory 与连接字符串相关联。连接字符串保存在配置文件的 <connectionStrings> 节中。默认情况下,应用程序块为 System.Data.SqlClient 类型的数据提供程序创建 SqlDatabase 类型的 Database 对象,为 System.Data.SqlServerCe 类型的数据提供程序创建 SqlCeDatabase 类型的对象,为 System.Data.OracleClient 类型的数据提供程序创建 OracleDatabase 类型的对象,为其他所有数据提供程序类型创建 GenericDatabase 类型的对象。
GenericDatabase 类仅支持由 ADO.NET 提供功能的数据库提供程序。特别的,支持参数发现的数据访问重载无法工作。GenericDatabase 可以由任何 .NET 托管的提供程序使用,包括 .NET Framework 2.0 中的 ODBC 和 OLE-DB 提供程序。可以通过在配置文件中的配置设置来覆盖数据提供程序类型和 Database 对象类型之间的映射。更多信息,请参见数据访问应用程序块的设计。
创建 DbCommand 对象
数据访问应用程序块提供了获取 ADO.NET DbCommand 对象的的统一方法。应用程序块的数据访问方法包含了接受 DbCommand 对象的重载。如果用 DbCommand 对象来使用重载,在调用存储过程时将可以进行更多的控制。例如,如果使用 DbCommand 对象,就可以使用在输出参数中返回多个结果的存储过程。另外, DbCommand 对象允许指定存储过程的超时值。注意:SQL Server CE 不支持存储过程,用内联 SQL 语句来代替。更多信息,请参见创建 Database 对象的细节。
用于 SQL 语句的 DbCommand对象
使用 GetSqlStringCommand 方法创建用于内联 SQL 语句的 DbCommand 对象。特定的 SQL 命令在方法调用时做为一个参数进行传递。
下列代码展示了如何使用 GetSqlStringCommand。
Database db = DatabaseFactory.CreateDatabase();
string sqlCommand = "Select CustomerID, LastName, FirstName From Customers";
DbCommand dbCommand = db.GetSqlStringCommand(sqlCommand);
Database db = DatabaseFactory.CreateDatabase(); DbCommand dbCommand = db.GetStoredProcCommand("GetProductsByCategory");
注意:存储过程的参数受 Database 类的方法的支持。关于如何使用存储过程参数参数的更多信息,请参见处理参数。
管理连接
数据库连接是有限资源,它们的妥善管理对可扩展的应用程序来说是必不可少的。仅在需要时保持连接打开并尽快关闭是一个很好的实践。根据设计,绝大多数的 Database 类方法在每次调用时打开和关闭到数据库的连接。因为,应用程序代码不需要包含用于管理连接的代码。(默认情况下,基于性能的原因,ADO.NET 将连接返回到连接池中,而不是关闭他们。因此,不需要缓存 Database 对象。)Database db = DatabaseFactory.CreateDatabase();
string sqlCommand = "Select ProductID, ProductName From Products";
DbCommand dbCommand = db.GetSqlStringCommand(sqlCommand);
// No need to open the connection; just make the call.
DataSet customerDataSet = db.ExecuteDataSet(dbCommand);
Database db = DatabaseFactory.CreateDatabase(); DbCommand dbCommand = db.GetSqlStringCommand("Select Name, Address From Customers"); using (IDataReader dataReader = db.ExecuteReader(dbCommand)) { // Process results }
使用 TransactionScope 类在此对 Database 类的某些方法进行了一些修改,以利用 .NET Framework 2.0 的 TransactionScope 类。此类自动将数据库调用加入到一个外围的事务中。这在将业务对象加入到一个事务中而不传递事务到这些业务对象中时非常有用。以下是 TransactionScope 类的使用的基本模型。
using (TransactionScope scope = new
TransactionScope(TransactionScopeOption.RequiresNew))
{
int rows = db.ExecuteNonQuery(CommandType.Text, insertString);
rows = db.ExecuteNonQuery(CommandType.Text, insertString2);
}
二个 ExecuteNonQuery 方法将行插入到了在创建 TransactionScope 实例时定义的事务中。
TransactionScope 类创建了一个本地的、轻量级的事务。它假定为发生在事务中的所有的数据库调用使用一个连接。这意味着,做为传递 DbTransaction 实例的另一种方法,简单的传递连接,然后 .NET Framework 自动为执行的每个命令设置了连接。
Enterprise Library,换句话说,通常为每个请求打开并关闭连接。此方法与 TransactionScope 类工作的方法不兼容。如果有多个连接,TransactionScope 类将认为事务是分布式事务。分布式事务比本地事务有显著的性能和资源消耗。
要避免这些,Database 类的方法,如 ExecuteDataSet ,识别 TransactionScope 实例活动的时机,并添加 database 调用到此事务中。如果事务的当前活动是使用 TransactionScope 实例的结果,Database 类方法会使用单一的连接。
特别的,GetOpenConnection 方法替换了 Database 方法中的 OpenConnection 方法,GetOpenConnection 方法返回一个连接包装器。如果没有事务正在处理,方法将销毁包装器。然而,当事务还在处理中时,方法将保持连接打开。
如果使用 ExecuteXmlReader 方法,将测试看 TransactionScope 实例是否是活动的。此方法将返回保持 reader 使用的连接的 XmlReader 对象。当 reader 使用结束后,最后的方法就是关闭此连接。然而,如果使用的是 TransactionScope 的实例,必须不能这么做,因为关闭此连接并创建一个新的连接将会改变轻量级的事务为分布式事务。
注意:多线程中共享在一个 transaction scope 中的同一事务将导致下列异常:“Transaction context in use by another session.”
Database db = DatabaseFactory.CreateDatabase();
object results = new object2;
DbCommand dbCommand = db.GetStoredProcCommand("GetCustomersAndSuppliers", results);
如果存储过程仅返回一个游标,则不必须传递对象数组。
用于创建可移植数据库应用程序的建议。
在此有一些用于创建可移植数据库应用程序的建议:
Database db = DatabaseFactory.CreateDatabase(); DbCommand dbCommand = db.GetStoredProcCommand("GetProductsByCategory"); db.AddInParameter(dbCommand, "CategoryID", DbType.Int32, 100);
使用 CommandBehavior.CloseConnection 调用 ExecuteReader。它在 DataReader 关闭时关闭连接。如果在一个 try 块中使用 ExecuteReader ,可以添加一个 finally 语句并关闭返回的 DataReader 对象,就像展示在下列示例中的一样。
Database db = DatabaseFactory.CreateDatabase();
DbCommand dbCommand = db.GetStoredProcCommand("GetProductsByCategory");
IDataReader dataReader = null;
try
{
//...
dataReader = db.ExecuteReader(dbCommand);
}
catch(Exception ex)
{
// Process exception
}
finally
{
if (dataReader != null)
dataReader.Close();
}
Database db = DatabaseFactory.CreateDatabase();
DbCommand dbCommand = db.GetStoredProcCommand("GetProductsByCategory");
using (IDataReader dataReader = db.ExecuteReader(dbCommand))
{
// Process results
}
对于在 .NET 中的异常管理的设计和实现原则,请参见异常管理架构指南。
处理参数
绝大多数存储过程接受用于输入存储过程或在输出时设置的值的参数。就像使用 ADO.NET 一样,数据访问应用程序块允许开发人员指定参数所有的属性。这些属性可以包括方向、数据类型和长度。此方法叫做显式参数处理。然而,为了方便,可以仅指定用于输入参数的值。在这种情况下,应用程序块将查找并提供参数的属性。此方法叫参数发现。Database db = DatabaseFactory.CreateDatabase(); string sqlCommand = "GetProductDetails"; DbCommand dbCommand = db.GetStoredProcCommand(sqlCommand); db.AddInParameter(dbCommand, "ProductID", DbType.Int32, 5); db.AddOutParameter(dbCommand, "ProductName", DbType.String, 50); db.AddOutParameter(dbCommand, "UnitPrice", DbType.Currency, 8);
注意:前面的代码不包括专用于数据库类型的参数名称令牌。因此,代码保留了跨多个不同数据库提供程序的通用性。当此代码运行于 SqlClient 数据提供程序时(并因此使用 SqlDatabase 类),下列代码将与前面的代码有着同样的行为。然而,此代码不能移植到其他的数据类型。
Database db = DatabaseFactory.CreateDatabase(); string sqlCommand = "GetProductDetails"; DbCommand dbCommand = db.GetStoredProcCommand(sqlCommand); db.AddInParameter(dbCommand, "@ProductID", DbType.Int32, 5); db.AddOutParameter(dbCommand, "@ProductName", DbType.String, 50); db.AddOutParameter(dbCommand, "@UnitPrice", DbType.Currency, 8);
使用列值做为参数输入
UpdateDataSet 方法要求三个不同的命令:一个用于插入值,一个用于修改值,另一个用于删除值。通常,这些命令用于存储过程而不是 SQL 字符串。它们在调用后保持由存储过程使用的参数。代替指定用于存储过程参数的值,来自 DataSet 的值被用作输入。在这种情况下,AddInParameter 的适当重载是接受源列做为参数的方法之一。
下列代码展示了如何使用列值做为参数输入。
Database db = DatabaseFactory.CreateDatabase(); DbCommand insertCommand = db.GetStoredProcCommand("AddProduct"); db.AddInParameter(insertCommand, "ProductName", DbType.String, "ProductName", DataRowVersion.Current); db.AddInParameter(insertCommand, "CategoryID", DbType.Int32, "CategoryID", DataRowVersion.Current); db.AddInParameter(insertCommand, "UnitPrice", DbType.Currency, "UnitPrice", DataRowVersion.Current);
参数发现
使用数据访问应用程序块,开发人员可以指定用于参数的值,而不需要关于这些参数的任何其他信息。在使用参数发现时,将要指定所有参数,并设置所有输出参数为 NULL 。
下列代码示范了如何仅通过指定参数值而无其他属性来使用 GetStoredProcCommand 。
Database db = DatabaseFactory.CreateDatabase(); string sqlCommand = "UpdateProduct"; DbCommand dbCommand = db.GetStoredProcCommand(sqlCommand, 11, "Queso Cabrales", 4, 25);