SixPack Library -- StoredProcedure
SixPack 库关于如何使用 StoredProcedure 类的教程。
引言
本文是关于解释如何使用 SixPack 库的教程系列的一部分。在本例中,我们将展示如何以一种简单但极其强大的方式使用 StoredProcedure
类来调用存储过程。
背景
关于 SixPack 库
SixPack 快速开发库是用于在 .NET/Mono 平台上进行快速开发的类集合。它根据通用公共许可证 2.1 (LGPL) 发布,可通过 Google code 获取。
使用代码
为什么我们开发了存储过程支持?
存储过程调用在大多数企业或 Web 开发项目中是一项非常常见的活动。Microsoft 的 ADO.NET 技术使得这项工作非常简单,那么为什么还要费力使用库来完成呢?
以下是我们实现此库功能的几个原因:
- 尽管 ADO.NET 功能强大,但它缺少一些功能,您在支持大型网站时可能需要进行开发(例如,处理死锁)。
- 作为最佳实践,许多人不希望开发人员使用 ADO.NET 的所有功能(通常,
DataReader
需要非常小心地处理)。 - 我们希望存储过程调用代码与数据库无关。
- 我们有大量的存储过程需要支持,并且希望开发人员能够生成调用代码,而不是编写它。
- 我们希望在开发过程中能够轻松跟踪存储过程的性能,同时在发布时没有性能瓶颈。
基本用法
我们从 SubSonic 的早期版本中“借鉴”了 StoredProcedure
类的基本签名。这使得熟悉该库的开发人员更容易将他们的代码迁移到我们自己的库。
DataSet ds = new StoredProcedure("myStoredProcName")
.AddParameter("@first", 42, DbType.Int, 0)
.AddParameter("@second", "hello", DbType.String, 50)
.GetDataSet();
这将调用一个名为 myStoredProcName
的存储过程,该过程有两个参数,并将结果作为 DataSet
返回。
请注意以下几点。
首先,您不必传递连接或连接名称。该库将自动使用您 Web.config 中可用的第一个连接字符串。在 99% 的情况下,每个项目只有一个连接字符串,所以这完全足够了。
其次,您传递的参数类型不是特定于数据库的。该库会读取连接字符串中的 SQL 客户端,并自动使用正确的 ADO.NET 层。这很重要,因为您编写的代码是数据库无关的。如果您需要支持不同的数据库,这将非常有用,因为只要它们的存储过程名称和签名相同,一切都将开箱即用,无需重新编译。
连接字符串解析和 SQL 客户端创建仅在启动时执行。没有性能损失。
使用代码生成器
调用代码可以自动生成。给定存储过程及其参数的列表,开发一个通用工具或使用 CodeSmith 等代码生成器来生成这样的代码相对容易。
using System.Data;
using SixPack.Data;
namespace example
{
public static class StoredProcedures
{
public static StoredProcedure ExecuteWidget(string widgetName, string another)
{
return new StoredProcedure("executeWidget")
.AddParameter("@widgetName", widgetName, DbType.AnsiString, 255)
.AddParameter("@another", another, DbType.AnsiString, 255);
}
// More stored procedures wrapped as static methods go here...
}
}
有了上面生成的代码,程序员就可以像这样调用存储过程。
DataSet ds = StoredProcedures.ExecuteWidget("foo", "bar").GetDataSet();
请再次注意,生成的代码和手写代码都是 RDBMS 独立的。
不同的调用类型
您可能会想,为什么我们没有在上面的生成代码中包含 GetDataSet
方法。这是因为您可以通过不同的方式调用存储过程。以下是不同调用命令的示例:
StoredProcedure procedure = new StoredProcedure("myStoredProcName")
.AddParameter("@first", 42, DbType.Int, 0)
.AddParameter("@second", "hello", DbType.String, 50);
DataSet ds = procedure.GetDataSet();
procedure.Execute();
Object o = procedure.ExecuteScalar();
使用不同的连接字符串
如果您不想使用第一个连接字符串而是想使用另一个连接字符串,该怎么办?您可以将连接字符串名称传递给 StoredProcedure
构造函数,它将被使用。
StoredProcedure procedure = new StoredProcedure("spName", "connectionName");
请注意,您不能直接传递连接字符串。该类需要在创建时知道连接字符串和提供程序以进行缓存。
DbConnections 和 DbTransactions
该类可以与现有系统很好地协同工作。所有调用方法都支持传递 DbConnection
或 DbTransaction
。
StoredProcedure procedure = new StoredProcedure("spName", "connectionName");
procedure.Execute(myDbTransaction);
如果您传递的是这两种类型之一的类,它将在调用中使用。请注意,您必须小心您传递的具体类型。例如,您不能使用 SQL Server 连接字符串创建 StoredProcedure
实例,然后传递 OleDbTransaction
。这会导致运行时异常。
使用输出参数
除了仅绑定输入参数的 AddParameter
方法之外,您还可以使用 AddOutParameter
方法将输出参数绑定到存储过程调用。然后,您可以使用 GetOutputParameter
方法检索该值。
StoredProcedure procedure = new StoredProcedure("foo")
.AddOutParameter("@bar", DbType.AnsiString, 50);
procedure.Execute();
string myResult = (string)procedure.GetOutputParameter("@bar");
启用死锁支持
注意:此功能仅在 SQL Server 下支持。
在高负载环境中,存储过程可能会开始出现死锁事件。
换句话说,您可能会有两个存储过程调用,每个都等待对方释放某个数据库锁。这个问题有一系列解决方法和最佳实践,但在复杂系统负载下,没有 100% foolproof 的方法可以确保这种情况永远不会发生。
在这种情况下,SQL Server 会终止其中一个调用,通常是成本较低的那个,以便另一个可以完成。在这种情况下,最好重新提交死锁受害者存储过程——但要适度:必须限制重新提交的次数。
StoredProcedure
类可以自动为您处理此问题,但默认情况下不会。要启用死锁受害者重新提交,您可以将以下键添加到您的 web.config 或 app.config 文件中。
<configuration>
<appsettings>
<!-- This will let the StoreProcedure class
retry submitting deadlock victims 5 times. -->
<add value="5" name="StoredProcedureDeadlockRetries">
</add>
</appsettings>
跟踪存储过程
在开发项目时,您可能需要出于各种原因了解存储过程的性能。
只需使用 SixPack 库的调试版本,配置日志系统,所有存储过程调用都将被跟踪,包括参数值和执行时间。
关注点
- 在未来的文章中,我们将解释如何配置和使用日志系统。目前,您可以快速浏览一下
Log
类 wiki 页面。 - 该库缺少除 SQL Server 以外的 RDBMS 系统的死锁重试功能。如果有人拥有不同 RDBMS 下的“死锁”系统,我们将不胜感激,以便我们可以在这些条件下编写和测试代码。如果您想了解更多关于死锁的信息,可以阅读这篇 文章。
- SixPack 库是开源的,可在 Google code 上的此站点 上获取。
历史
首次发布。