ADO.NET 引入的主要变化之一是用 DataTable、DataSet、DataAdapter 和 DataReader 对象的组合取代了 ADO Recordset 对象。DataTable 表示单个表中行的集合,在这一方面类似于 Recordset。DataSet 表示 DataTable 对象的集合,同时包括将各种表绑定在一起的关系和约束。实际上,DataSet 是带有内置 XML 支持的、内存中的关系结构。
DataSet 的主要特性之一是它不了解可能用来填充它的基础数据源。它是一个不连续的、独立的实体,用于表示数据集合,并且可以通过多层应用程序的不同层在组件之间传递。它还可以作为 XML 数据流进行序列化,这使其非常适合于在不同种类的平台之间进行数据传输。ADO.NET 使用 DataAdapter 对象将数据传送到 DataSet 和基础数据源,或者从数据源传出。DataAdapter 对象还提供以前与 Recordset 关联的增强的批量更新功能。
ADO.NET 依赖于 .NET 数据提供程序的服务。这些提供程序提供对基础数据源的访问,并且包括四个主要对象(Connection、Command、DataReader 和 DataAdapter)。
目前,ADO.NET 随附了两类提供程序:Bridge 提供程序和 Native 提供程序。通过 Bridge 提供程序(如那些为 OLE DB 和 ODBC 提供的提供程序),可以使用为以前的数据访问技术设计的数据库。Native 提供程序(如 SQL Server 和 Oracle 提供程序)通常能够提供性能方面的改善,部分原因在于少了一个抽象层。
命名空间组织结构
与各个 .NET 数据提供程序相关联的类型(类、结构、枚举等)位于其各自的命名空间中:
•
System.Data.SqlClient。包含 SQL Server .NET 数据提供程序类型。
•
System.Data.OracleClient。包含 Oracle .NET 数据提供程序。
•
System.Data.OleDb。包含 OLE DB .NET 数据提供程序类型。
•
System.Data.Odbc。包含 ODBC .NET 数据提供程序类型。
•
System.Data。包含独立于提供程序的类型,如 DataSet 和 DataTable。
在各自的关联命名空间内,每个提供程序都提供了对 Connection、Command、DataReader 和 DataAdapter 对象的实现。SqlClient 实现的前缀为“Sql”,而 OleDb 实现的前缀为“OleDb”。例如,Connection 对象的 SqlClient 实现是 SqlConnection,而 OleDb 实现则为 OleDbConnection。同样,DataAdapter 对象的两个实现分别为 SqlDataAdapter 和 OleDbDataAdapter。
存储过程与直接 SQL
本文档中显示的大多数代码片段使用 SqlCommand 对象来调用存储过程,以执行数据库操作。在某些情况下,您将不会看到 SqlCommand 对象,因为存储过程名被直接传递给 SqlDataAdapter 对象。在内部,这仍然会导致创建 SqlCommand 对象。
您应该使用存储过程而不是嵌入的 SQL 语句,原因如下:
•
存储过程通常可以改善性能,因为数据库能够优化存储过程使用的数据访问计划,并且能够缓存该计划以供将来重用。
•
可以在数据库内分别设置各个存储过程的安全保护。客户端不必对基础表拥有访问权限,就可以获得执行存储过程的权限。
•
存储过程可以简化维护工作,因为修改存储过程通常要比更改已部署组件中的硬编码 SQL 语句容易。
•
存储过程为基础数据库架构增加了额外的抽象级别。存储过程的客户端与存储过程的实现细节是彼此隔离的,与基础架构也是彼此隔离的。
•
存储过程可以减少网络流量,因为可以批量执行 SQL 语句,而不是从客户端发送多个请求。
SQL Server 联机文档强烈建议您不要使用“sp_”作为名称前缀来创建任何存储过程,因为此类名称已经被指定给系统存储过程。SQL Server 始终按以下顺序来查找以 sp_ 开头的存储过程:
1.
在主数据库中查找存储过程。
2.
基于所提供的任何限定符(数据库名或所有者)来查找存储过程。
3.
使用 dbo 作为所有者来查找存储过程(如果未指定所有者)。
属性与构造函数参数
可以通过构造函数参数来设置 ADO.NET 对象的特定属性值,也可以直接设置属性值。例如,下面的代码片段在功能上是等效的。
// Use constructor arguments to configure command object
SqlCommand cmd = new SqlCommand( "SELECT * FROM PRODUCTS", conn );
// The above line is functionally equivalent to the following
// three lines which set properties explicitly
sqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT * FROM PRODUCTS";
从性能角度看,这两种方法之间的差异是微不足道的,因为针对 .NET 对象设置和获取属性要比针对 COM 对象执行类似操作更为高效。
选择哪种方法取决于个人喜好和编码风格。不过,对属性进行明确设置确实能够使代码更易理解(尤其是当您不熟悉 ADO.NET 对象模型时)和调试。
管理数据库连接
数据库连接代表一种关键的、昂贵的和有限的资源,尤其是在多层 Web 应用程序中。正确地管理连接是十分必要的,因为您采取的方法可能显著影响应用程序的总体可伸缩性。同时,还要认真考虑在何处存储连接字符串。需要使用可配置的且安全的位置。
在管理数据库连接和连接字符串时,应该努力做到:
•
通过在多个客户端中多路复用数据库连接池,帮助实现应用程序的可伸缩性。
•
采用可配置的、高性能的连接池策略。
•
在访问 SQL?Server 时使用 Windows 身份验证。
•
在中间层避免模拟。
•
安全地存储连接字符串。
•
尽量晚地打开数据库连接,尽量早地将其关闭。
本节讨论连接池,并且帮助您选择适当的连接池策略。本节还将考虑应该如何管理、存储和操纵数据库连接字符串。最后,本节将给出两种编码模式,可用来帮助确保连接被可靠地关闭,并被返回到连接池。
SQL Server .NET 数据提供程序的池机制
如果您使用的是 SQL Server .NET 数据提供程序,请使用该提供程序提供的连接池支持。这是一种由该提供程序在内部实现的支持事务处理并且非常高效的机制,它存在于托管代码中。池是以每个应用程序的域为基础创建的,并且在应用程序域卸载之前不会销毁。
可以透明地使用这种形式的连接池,但应该知道池的管理方式以及可用来微调连接池的各种配置选项。
在许多情况下,对于您的应用程序而言,SQL Server .NET 数据提供程序的默认连接池设置可能已经足够了。在开发和测试基于 .NET 的应用程序的过程中,建议您对规划通信模式进行模拟,以确定是否需要修改连接池大小。
需要生成可伸缩的高性能应用程序的开发人员应该最大限度地减少使用连接的时间,只在检索或更新数据时才使连接保持打开状态。连接关闭时,将被返回到连接池,并可供重用。在此情况下,到数据库的实际连接不会被切断;不过,如果连接池被禁用,则到数据库的实际连接将被关闭。
开发人员应该十分小心,不要依赖垃圾回收器来释放连接,因为当引用离开作用范围时,连接未必能够关闭。这是连接泄漏的一种常见根源,当请求新连接时,这会导致连接异常。
配置 SQL Server .NET 数据提供程序连接池
可以使用一组名称-值对(通过连接字符串提供)来配置连接池。例如,可以配置是否启用连接池(默认情况下启用)、池的最大容量和最小容量,以及要打开连接的排队请求可以阻塞的时间长度。下面是一个示例连接字符串,用于配置池的最大容量和最小容量。
"Server=(local); Integrated Security=SSPI; Database=Northwind;
Max Pool Size=75; Min Pool Size=5"
打开连接并创建池以后,会将多个连接添加到池中,以便将连接数量提高到所配置的最小数量。随后,可以继续向该池中添加连接,直至达到所配置的最大池数量。当达到最大数量时,要打开连接的新请求将排队等待一段可配置的时间。
更多信息
当使用 SQL Server .NET 数据提供程序连接池时,请注意以下几个方面:
•
连接是通过连接字符串上的完全匹配算法进行池化的。池机制甚至对名称-值对之间的空格也敏感。例如,下面的两个连接字符串将导致两个独立的池,因为第二个连接字符串包含额外的空格字符。
SqlConnection conn = new SqlConnection(
"Integrated Security=SSPI;Database=Northwind");
conn.Open(); // Pool A is created
SqlConmection conn = new SqlConnection(
"Integrated Security=SSPI ; Database=Northwind");
conn.Open(); // Pool B is created (extra spaces in string)
•
连接池被划分为多个事务专有池和一个与当前尚未在事务中登记的连接对应的池。对于与特定事务上下文关联的线程,会返回相应池(该池包含在该事务中登记的连接)的连接。这就使得使用已登记的连接成为一个透明的过程。
OLE DB .NET 数据提供程序的池机制
OLE DB .NET 数据提供程序通过使用基础 OLE DB 资源池来池化连接。有多个用于配置资源池的选择:
•
可以使用连接字符串来配置、启用或禁用资源池。
•
可以使用注册表。
•
可以用编程方式配置资源池。
为避免出现与注册表相关的部署问题,请不要使用注册表来配置 OLE DB 资源池。
监控连接池
要对应用程序使用连接池的情况进行监控,可以使用 SQL Server 随附的事件探查器工具,或者使用 Microsoft Windows? 2000 操作系统随附的性能监视器工具。
使用 SQL Server 事件探查器监控连接池
1.
单击 Start,指向 Programs,指向 MicrosoftSQLServer,然后单击 Profiler 以启动事件探查器。
2.
在 File 菜单上,指向 New,然后单击 Trace。
3.
提供连接详细信息,然后单击 OK。
4.
在 Trace Properties 对话框中,单击 Events 选项卡。
5.
在 Selected event classes 列表中,确保 Audit Login 和 Audit Logout 事件显示在 Security Audit 下面。要使跟踪变得更为清晰,请从该列表中删除所有其他事件。
6.
单击 Run 以启动跟踪。当连接建立时,您将看到 Audit Login 事件;当连接关闭时,您将看到 Audit Logout 事件。
使用性能监视器监控连接池
1.
单击 Start,指向 Programs,指向 Administrative Tools,然后单击 Performance 以启动性能监视器。
2.
右键单击图形背景,然后单击 AddCounters。
3.
在 Performance object 下拉列表中,单击 SQL Server:General Statistics。
4.
在显示的列表中,单击 User Connections。
5.
单击 Add,然后单击 Close。
管理安全性
尽管数据库连接池提高了应用程序的总体可伸缩性,但这意味着您不再能够在数据库级别管理安全性。这是因为,要支持连接池,连接字符串必须完全相同。如果您需要跟踪每个用户的数据库操作,请考虑添加一个参数,以便能够传递用户标识并在数据库中手动记录用户操作。您需要将该参数添加到每个操作中。
使用 Windows 身份验证
在连接到 SQL Server 时,应该使用 Windows 身份验证,因为它提供了许多好处:
1.
安全性更易于管理,因为您使用单一 (Windows) 安全模型,而不是独立的 SQL Server 安全模型。
2.
可避免将用户名和密码嵌入到连接字符串中。
3.
不会以明文方式通过网络传递用户名和密码。
4.
通过采用密码到期期限、最小长度以及在多次无效登录请求后锁定帐户,改善了登录安全性。
存储连接字符串
要存储数据库连接字符串,可以有多种选择,这些选择具有不同级别的灵活性和安全性。尽管在源代码中对连接字符串进行硬编码可提供最佳性能,但文件系统缓存可确保在外部将该字符串存储到文件系统中所带来的性能下降是微不足道的。几乎在所有情况下,人们都首选外部连接字符串所提供的额外的灵活性(它支持管理员配置)。
当您选择连接字符串存储方法时,需要注意的两个最重要的事项是安全性和配置简易性,然后紧跟着的是性能。
可以选择下列位置来存储数据库连接字符串:
•
在应用程序配置文件中;例如,ASP.NET Web 应用程序的 Web.config
•
在通用数据链接 (UDL) 文件中(仅由 OLE DB .NET 数据提供程序支持)
•
在 Windows 注册表中
•
在自定义文件中
•
在 COM+ 目录中,方法是使用构建字符串(仅适用于服务组件)
通过使用 Windows 身份验证来访问 SQL Server,可以避免将用户名和密码存储在连接字符串中。如果您的安全要求需要采取更严格的措施,请考虑以加密格式存储连接字符串。
对于 ASP.NET Web 应用程序而言,在 Web.config 文件内以加密格式存储连接字符串,代表着一种安全的、可配置的解决方案。
注 可以在连接字符串中将 Persist Security Info 命名值设置为 false,以禁止通过 SqlConnection 或 OleDbConnection 对象的 ConnectionString 属性返回对安全敏感的细节(如密码)。
下面几小节讨论了如何使用各种选择来存储连接字符串,并介绍了各种方法的相对优点和缺点。这些内容有助于您根据自己特定的应用程序方案做出明智的选择。
使用 XML 应用程序配置文件
可以使用 <appSettings> 元素在应用程序配置文件的自定义设置节中存储数据库连接字符串。该元素支持任意的密钥-值对,如以下代码片段所示:
<configuration>
<appSettings>
<add key="DBConnStr"
value="server=(local);Integrated Security=SSPI;database=northwind"/>
</appSettings>
</configuration>
注 <appSettings> 元素出现在 <configuration> 元素下面,并且不是紧跟在 <system.web> 的后面。
优点
•
易于部署。连接字符串是通过定期 .NET xcopy 部署与配置文件一起部署的。
•
易于以编程方式访问。通过 ConfigurationSettings 类的 AppSettings 属性,可以在运行时方便地读取已配置的数据库连接字符串。
•
支持动态更新(仅限于 ASP.NET)。如果管理员在 Web.config 文件中更新连接字符串,当下一次访问该字符串时(对于无状态组件而言,这可能是客户端下一次使用该组件进行数据访问请求),所做更改将生效。
缺点
•
安全性。尽管 ASP.NET Internet 服务器应用程序编程接口 (ISAPI) 动态链接库 (DLL) 禁止客户端直接访问带有 .config 文件扩展名的文件,并且可以使用 NTFS 权限进一步限制访问,您可能仍然希望避免以明文形式在前端 Web 服务器上存储这些详细信息。为获得额外的安全性,请以加密格式在配置文件中存储连接字符串。
可以使用 System.Configuration.ConfigurationSettings 类的静态 AppSettings 属性来检索自定义应用程序设置。以下代码片段对此进行了说明,该代码片段采用了前面例举的名为 DBConnStr 的自定义密钥:
using System.Configuration;
private string GetDBaseConnectionString()
{
return ConfigurationSettings.AppSettings["DBConnStr"];