在.NET编程环境中,分布式事务管理和对象关系映射(ORM)是两个非常强大的工具。本文将展示如何将这两个工具简单地结合起来使用。
分布式事务是使用COM+时最喜欢的部分。通过开启一个分布式事务,可以将不同的数据库、消息队列、文件、电子邮件等几乎所有能想到的东西纳入同一个事务中。两阶段提交机制告诉,要么全部成功,要么全部回滚。如果服务器宕机,事务还可以从上次中断的地方重新开始。
NHibernate是另一个喜欢使用的强大工具。如果不熟悉这个ORM(对象关系映射器),可以点击访问主站。NHibernate是基于标准ADO.NET构建的,并且具有使用事务的能力。但普通的ADO.NET事务只限于一个数据库连接。COM+的分布式事务可以跨越多个数据源。希望NHibernate代码能够参与到分布式事务中。本文将展示是如何实现这一点的。
在代码中,有几件事情应该了解:
COM+项目有一个后构建步骤。后构建使用regsvcs来注销并重新注册COM+ DLL。这增加了编译的等待时间,但省去了手动操作的麻烦。为了让regsvcs工作,将框架目录添加到了路径中(C:\Windows\Microsoft.NET\Framework\v1.1.4322)。像往常一样,如果改变了路径变量,必须重启Visual Studio。
还创建了一个NUnit测试项目来运行实际的测试。在测试项目的后构建中,将App.Config复制到目标目录,并给它添加了".config"后缀。这样NUnit就可以找到它了。
通常,将测试项目设置为启动项目。要让NUnit启动项目,请打开项目属性窗口,选择左侧的Configuration Properties->Debugging。将Debug Mode设置为Program,然后点击Apply。对于启动应用程序,找到NUnit GUI可执行文件(C:\Program Files\NUnit 2.2\bin\nunit-gui.exe)。对于命令行参数,输入DLL的文件名(CPNTestHarness.dll)。
包含了一个SQL脚本来创建在示例中使用的表。将这些表放入数据库,并更改App.Config中的连接字符串。
可能注意到了设置NHibernate配置的奇怪方式。没有使用App.Config来设置它,而是用代码从App.Config中提取连接字符串。这只是个人偏好。通常发现,在企业环境中App.Config是不够的,通常会从Microsoft企业库中提取配置设置。所以快速更改了代码以适应这个示例。
这个类基本上是处理与NHibernate通信的方式。它创建配置并获取ISessionFactory。它只为这个示例暴露了三个方法:Save(object)、Delete(object)和GetAll(Type)。这些对普通的NHibernate用户来说应该都很熟悉。没有包括特定的获取方法,因为不需要它来进行测试。
整篇文章的真正重点围绕EnlistIfPossible()方法。当使用常规的ADO.NET时,可以通过使用EnlistDistributedTransaction方法将数据库连接注册到COM+分布式事务中。不幸的是,这个方法不是任何接口的一部分。它也不是System.Data.IDbConnection类的一部分,这基本上是NHibernate会给所有。知道的是,大多数IDbConnection的实现都有一个EnlistDistributedTransaction方法。一个例外是System.Data.SqlServerCe库。
为了让连接参与分布式事务,只需要在连接对象上调用EnlistDistributedTransaction方法(如果它存在)。为了做到这一点,使用了反射:
private static void EnlistIfPossible(System.Data.IDbConnection conn)
{
if (ContextUtil.IsInTransaction)
{
MethodInfo mi = conn.GetType().GetMethod("EnlistDistributedTransaction", BindingFlags.Public | BindingFlags.Instance);
if (mi != null)
{
mi.Invoke(conn, new object[] { (System.EnterpriseServices.ITransaction)ContextUtil.Transaction });
}
}
}
非常简单。现在,每当打开一个NHibernate会话时,都可以将数据库连接注册到COM+事务中。这在Save和Delete中完成。
OrmManager将参与事务(如果存在)。但如果不存在事务,它不会创建新的事务。这样用户可以选择是否进行事务处理。为了处理事务,有一个类包装了OrmManager,叫做TransactionController。这个类基本上允许开始和结束事务,并在中间做一大堆ORM代码。
开始和结束事务是一段标准的COM+代码:
public void BeginTransaction()
{
ServiceConfig sc = new ServiceConfig();
sc.Transaction = TransactionOption.RequiresNew;
ServiceDomain.Enter(sc);
ContextUtil.MyTransactionVote = TransactionVote.Commit;
...
}
public void EndTransaction()
{
if (ContextUtil.MyTransactionVote == TransactionVote.Commit)
ContextUtil.SetComplete();
else
ContextUtil.SetAbort();
ServiceDomain.Leave();
...
}
NUnit测试框架展示了如何使用代码:
TransactionController tc = new TransactionController();
Table1 t1; // NHibernate对象
Table2 t2; // NHibernate对象
try
{
tc.BeginTransaction();
t1 = new Table1();
t1.Num = 1;
t2 = new Table2();
t2.Num = 2;
tc.Save(t1);
tc.Save(t2);
}
finally
{
tc.EndTransaction();
}