ADO.NET 的最佳实践技巧(二)

翻译|其它|编辑:郝浩|2006-03-16 11:23:00.000|阅读 1695 次

概述:

# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>

用多个表填充 DataSet

如果使用批处理 SQL 语句检索多个表并填充 DataSet,第一个表用指定给 Fill 方法的表名命名。后面的表用指定给 Fill 方法的表名加上一个从 1 开始并且增量为 1 的数字命名。例如,如果运行下面的代码:

'Visual Basic
Dim da As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers; SELECT * FROM Orders;", myConnection)
Dim ds As DataSet = New DataSet()
da.Fill(ds, "Customers")

//C#
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Customers; SELECT * FROM Orders;", myConnection);
DataSet ds = new DataSet();
da.Fill(ds, "Customers");

来自 Customers 表的数据放在名为 "Customers" 的 DataTable 中。来自 Orders 表的数据放在名为 "Customers1" 的 DataTable 中。

填充完 DataSet 之后,可以很容易地把 "Customers1" 表的 TableName 属性改为 "Orders"。但是,后面的填充会导致 "Customers" 表被重新填充,而 "Orders" 表会被忽略,并创建另外一个 "Customers1" 表。为了对这种情况作出补救,创建一个 DataTableMapping,把 "Customers1" 映射到 "Orders",并为其他后面的表创建其他的表映射。例如:

'Visual Basic
Dim da As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers; SELECT * FROM Orders;", myConnection)
da.TableMappings.Add("Customers1", "Orders")
Dim ds As DataSet = New DataSet()
da.Fill(ds, "Customers")

//C#
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Customers; SELECT * FROM Orders;", myConnection);
da.TableMappings.Add("Customers1", "Orders");
DataSet ds = new DataSet();
da.Fill(ds, "Customers");

使用 DataReader

下面是一些使用 DataReader 获得最佳性能的技巧,同时还回答了一些关于使用 DataReader 的常见问题。

在访问相关 Command 的任何输出参数之前,必须关闭 DataReader

完成读数据之后总是要关闭 DataReader。如果使用 Connection 只是用于返回 DataReader,那么关闭 DataReader 之后立刻关闭它。

另外一个显式关闭 Connection 的方法是把 CommandBehavior.CloseConnection 传递给 ExecuteReader 方法,以确保相关的连接在关闭 DataReader 时被关闭。如果从一个方法返回 DataReader,而且不能控制 DataReader 或相关连接的关闭,则这样做特别有用。

不能在层之间远程访问 DataReaderDataReader 是为已连接好的数据访问设计的。

当访问列数据时,使用类型化访问器,例如,GetStringGetInt32。这使您不用进行将 GetValue 返回的 Object 强制转换成特定类型所需的处理。

一个单一连接每次只能打开一个 DataReader。在 ADO 中,如果打开一个单一连接,并且请求两个使用只进、只读游标的记录集,那么 ADO 会在游标生存期内隐式打开第二个、未池化的到数据存储区的连接,然后再隐式关闭该连接。对于 ADO.NET,“秘密”完成的动作很少。如果想在相同的数据存储区上同时打开两个 DataReaders,就必须显式创建两个连接,每个 DataReader 一个。这是 ADO.NET 为池化连接的使用提供更多控制的一种方法。

默认情况下,DataReader 每次 Read 时都要把整行加载到内存。这允许在当前行内随机访问列。如果不需要这种随机访问,为了提高性能,就把 CommandBehavior.SequentialAccess 传递给 ExecuteReader 调用。这将 DataReader 的默认行为更改为仅在请求时将数据加载到内存。注意,CommandBehavior.SequentialAccess 要求顺序访问返回的列。也就是说,一旦读过返回的列,就不能再读它的值了。

如果已经完成读取来自 DataReader 的数据,但仍然有大量挂起的未读结果,就在调用 DataReaderClose 之前先调用 CommandCancel。调用 DataReaderClose 会导致在关闭游标之前检索挂起的结果并清空流。调用 CommandCancel 会放弃服务器上的结果,这样,DataReader 在关闭的时候就不必读这些结果。如果要从 Command 返回输出参数,还要调用 Cancel 放弃它们。如果需要读取任何输出参数,不要调用 CommandCancel,只要调用 DataReaderClose 即可。

二进制大对象 (BLOB)

DataReader 检索二进制大对象 (BLOB) 时,应该把 CommandBehavior.SequentialAccess 传递给 ExecuteReader 方法调用。因为 DataReader 的默认行为是每次 Read 都把整行加载到内存,又因为 BLOB 值可能非常大,所以结果可能由于单个 BLOB 而使大量内存被用光。SequentialAccessDataReader 的行为设置为只加载请求的数据。然后还可以使用 GetBytesGetChars 控制每次加载多少数据。

记住,使用 SequentialAccess 时,不能不按顺序访问 DataReader 返回的不同字段。也就是说,如果查询返回三列,其中第三列是 BLOB,并且想访问前两列中的数据,就必须在访问 BLOB 数据之前先访问第一列的值,然后访问第二列的值。这是因为现在数据是顺序返回的,并且 DataReader 一旦读过该数据,该数据就不再可用。

有关如何在 ADO.NET 中访问 BLOB 的详细描述,请参阅 Obtaining BLOB Values from a Database

使用命令

ADO.NET 提供了几种命令执行的不同方法以及优化命令执行的不同选项。下面包括一些技巧,它们是关于选择最佳命令执行以及如何提高执行命令的性能。

使用 OleDbCommand 的最佳实践

不同 .NET 框架数据提供程序之间的命令执行被尽可能标准化了。但是,数据提供程序之间仍然存在差异。下面给出一些技巧,可微调用于 OLE DB 的 .NET 框架数据提供程序的命令执行。

按照 ODBC CALL 语法使用 CommandType.Text 调用存储过程。使用 CommandType.StoredProcedure 只是秘密地生成 ODBC CALL 语法。

一定要设置 OleDbParameter 的类型、大小(如果适用)、以及精度和范围(如果参数类型是 numeric 或 decimal)注意,如果不显式提供参数信息,OleDbCommand 会为每个执行命令重新创建 OLE DB 参数访问器。

使用 SqlCommand 的最佳实践

使用 SqlCommand 执行存储过程的快速提示:如果调用存储过程,将 SqlCommandCommandType 属性指定为 StoredProcedureCommandType。这样通过将该命令显式标识为存储过程,就不需要在执行之前分析命令

使用 Prepare 方法

对于重复作用于数据源的参数化命令,Command.Prepare 方法能提高性能Prepare 指示数据源为多次调用优化指定的命令。要想有效利用 Prepare,需要彻底理解数据源是如何响应 Prepare 调用的。对于一些数据源(例如 SQL Server 2000),命令是隐式优化的,不必调用 Prepare。对于其他(例如 SQL Server 7.0)数据源,Prepare 会比较有效。

显式指定架构和元数据

只要用户没有指定元数据信息,ADO.NET 的许多对象就会推断元数据信息。下面是一些示例:

DataAdapter.Fill 方法,如果 DataSet 中没有表和列,DataAdapter.Fill 方法会在 DataSet 中创建表和列。

CommandBuilder,它会为单表 SELECT 命令生成 DataAdapter 命令属性。

CommandBuilder.DeriveParameters,它会填充 Command 对象的 Parameters 集合。

但是,每次用到这些特性,都会有性能损失。建议将这些特性主要用于设计时和即席应用程序中。在可能的情况下,显式指定架构和元数据。其中包括在 DataSet 中定义表和列、定义 DataAdapterCommand 属性、以及为 Command 定义 Parameter 信息。

ExecuteScalar 和 ExecuteNonQuery

如果想返回像 Count(*)、Sum(Price) 或 Avg(Quantity) 的结果那样的单值,可以使用 Command.ExecuteScalarExecuteScalar 返回第一行第一列的值,将结果集作为标量值返回。因为单独一步就能完成,所以 ExecuteScalar 不仅简化了代码,还提高了性能;要是使用 DataReader 就需要两步才能完成(即,ExecuteReader + 取值)。

使用不返回行的 SQL 语句时,例如修改数据(例如INSERT、UPDATE 或 DELETE)或仅返回输出参数或返回值,请使用 ExecuteNonQuery。这避免了用于创建空 DataReader 的任何不必要处理。

有关更多信息,请参阅 Executing a Command

测试 Null

如果表(在数据库中)中的列允许为空,就不能测试参数值是否“等于”空。相反,需要写一个 WHERE 子句,测试列和参数是否都为空。下面的 SQL 语句返回一些行,它们的 LastName 列等于赋给 @LastName 参数的值,或者 LastName 列和 @LastName 参数都为空。

SELECT * FROM Customers
WHERE ((LastName = @LastName) OR (LastName IS NULL AND @LastName IS NULL))

把 Null 作为参数值传递

对数据库的命令中,当把空值作为参数值发送时,不能使用 null(Visual Basic庐 .NET 中为 Nothing)。而需要使用 DBNull.Value。例如:

'Visual Basic
Dim param As SqlParameter = New SqlParameter("@Name", SqlDbType.NVarChar, 20)
param.Value = DBNull.Value

//C#
SqlParameter param = new SqlParameter("@Name", SqlDbType.NVarChar, 20);
param.Value = DBNull.Value;

执行事务

ADO.NET 的事务模型已经更改。在 ADO 中,当调用 StartTransaction 时,调用之后的任何更新操作都被视为是事务的一部分。但是,在 ADO.NET 中,当调用 Connection.BeginTransaction 时,会返回一个 Transaction 对象,需要把它与 CommandTransaction 属性联系起来。这种设计可以在一个单一连接上执行多个根事务。如果未将 Command.Transaction 属性设置为一个针对相关的 Connection 而启动的 Transaction,那么 Command 就会失败并引发异常。

即将发布的 .NET 框架将使您可以在现有的分布式事务中手动登记。这对于对象池方案来说很理想;在该方案中,一个池对象打开一次连接,但是在多个独立的事务中都涉及到该对象。.NET 框架 1.0 发行版中这一功能并不可用。

有关事务的更多信息,请参阅 Performing Transactions 以及 .NET Data Access Architecture Guide
标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com


为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP