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。
本文档中显示的大多数代码片段使用 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 objectSqlCommand cmd = new SqlCommand( "SELECT * FROM PRODUCTS", conn );// The above line is functionally equivalent to the following// three lines which set properties explicitlysqlCommand 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 数据提供程序的默认连接池设置可能已经足够了。在开发和测试基于 .NET 的应用程序的过程中,建议您对规划通信模式进行模拟,以确定是否需要修改连接池大小。
需要生成可伸缩的高性能应用程序的开发人员应该最大限度地减少使用连接的时间,只在检索或更新数据时才使连接保持打开状态。连接关闭时,将被返回到连接池,并可供重用。在此情况下,到数据库的实际连接不会被切断;不过,如果连接池被禁用,则到数据库的实际连接将被关闭。
开发人员应该十分小心,不要依赖垃圾回收器来释放连接,因为当引用离开作用范围时,连接未必能够关闭。这是连接泄漏的一种常见根源,当请求新连接时,这会导致连接异常。
可以使用一组名称-值对(通过连接字符串提供)来配置连接池。例如,可以配置是否启用连接池(默认情况下启用)、池的最大容量和最小容量,以及要打开连接的排队请求可以阻塞的时间长度。下面是一个示例连接字符串,用于配置池的最大容量和最小容量。
"Server=(local); Integrated Security=SSPI; Database=Northwind; Max Pool Size=75; Min Pool Size=5"
打开连接并创建池以后,会将多个连接添加到池中,以便将连接数量提高到所配置的最小数量。随后,可以继续向该池中添加连接,直至达到所配置的最大池数量。当达到最大数量时,要打开连接的新请求将排队等待一段可配置的时间。
……