迷恋弦哥

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Using NHibernate with TransactionScope

通过事务范围来使用NHibernate

  与其他系统的可靠集成是挺常见的业务需求.在这些系统报错时,不只本地数据库的事务需要回滚,也许其他的多个事务资源都需要.本节介绍如何使用微软的TransactionScope和NHibernate来达成该目标.

准备

  1. 创建一个控制台应用程序.
  2. 添加引用:第一章中的Eg.Core项目,NHibernate.dll和NHibernate.ByteCode.Castle.dll.
  3. 参照Configuring NHibernate with App.config和Configuring log4net小节来完成该控制台程序.

步骤

1.   添加引用System.Transaction .
2.   添加一个名为IReceiveProductUpdates 的公共接口,该接口有3个方法,代码如下:

View Code
void Add(Product product);
void Update(Product product);
void Remove(Product product);

3.   添加一个名为WarehouseFacade的公共类,代码如下:

View Code
public class WarehouseFacade : IReceiveProductUpdates
{
  public void Add(Product product)
  {
    Console.WriteLine("Adding {0} to warehouse system.",
                product.Name);
  }
  public void Update(Product product)
  {

Console.WriteLine("Updating {0} in warehouse system.",
                      product.Name);
  }
  public void Remove(Product product)
  {
    Console.WriteLine("Removing {0} from warehouse system.",
                      product.Name);
    var message = string.Format(
      "Warehouse still has inventory of {0}.",
      product.Name);
    throw new ApplicationException(message);
  }
}

4.   添加一个名为ProductCatalog的公共类,代码如下:

View Code
public class ProductCatalog : IReceiveProductUpdates
{
  private readonly ISessionFactory _sessionFactory;
  public ProductCatalog(ISessionFactory sessionFactory)
  {
    _sessionFactory = sessionFactory;
  }
  public void Add(Product product)
  {
    Console.WriteLine("Adding {0} to product catalog.",
                      product.Name);
    using (var session = _sessionFactory.OpenSession())
    using (var tx = session.BeginTransaction())
    {
      session.Save(product);
      tx.Commit();
    }
  }
  public void Update(Product product)
  {
    Console.WriteLine("Updating {0} in product catalog.",
                      product.Name);
    using (var session = _sessionFactory.OpenSession())
    using (var tx = session.BeginTransaction())

{
      session.Update(product);
      tx.Commit();
    }
  }
  public void Remove(Product product)
  {
    Console.WriteLine("Removing {0} from product catalog.",
                      product.Name);
    using (var session = _sessionFactory.OpenSession())
    using (var tx = session.BeginTransaction())
    {
      session.Delete(product);
      tx.Commit();
    }
  }
}

5.   更新Program类,代码如下:

View Code
class Program
{
  static void Main(string[] args)
  {
    var nhConfig = new Configuration()
      .Configure();
    var sessionFactory = nhConfig
      .BuildSessionFactory();
    var catalog = new ProductCatalog(sessionFactory);
    var warehouse = new WarehouseFacade();
    var p = new Program(catalog, warehouse);
    var sprockets = new Product()
                     {
                       Name = "Sprockets",
                       Description = "12 pack, metal",
                       UnitPrice = 14.99M
                     };
    p.AddProduct(sprockets);
    sprockets.UnitPrice = 9.99M;

  p.UpdateProduct(sprockets);
    p.RemoveProduct(sprockets);
    Console.WriteLine("Press any key.");
    Console.ReadKey();
  }
  private readonly IReceiveProductUpdates[] _services;
  public Program(params IReceiveProductUpdates[] services)
  {
    _services = services;
  }
  private void AddProduct(Product newProduct)
  {
    Console.WriteLine("Adding {0}.", newProduct.Name);
    try
    {
      using (var scope = new TransactionScope())
      {
        foreach (var service in _services)
          service.Add(newProduct);
        scope.Complete();
      }
    }
    catch (Exception ex)
    {
      Console.WriteLine("Product could not be added.");
      Console.WriteLine(ex.Message);
    }
  }
  private void UpdateProduct(Product changedProduct)
  {
    Console.WriteLine("Updating {0}.",
                      changedProduct.Name);
    try
    {
      using (var scope = new TransactionScope())
      {
        foreach (var service in _services)
          service.Update(changedProduct);

     scope.Complete();
      }
    }
    catch (Exception ex)
    {
      Console.WriteLine("Product could not be updated.");
      Console.WriteLine(ex.Message);
    }
  }
  private void RemoveProduct(Product oldProduct)
  {
    Console.WriteLine("Removing {0}.",
                      oldProduct.Name);
    try
    {
      using (var scope = new TransactionScope())
      {
        foreach (var service in _services)
          service.Remove(oldProduct);
        scope.Complete();
      }
    }
    catch (Exception ex)
    {
      Console.WriteLine("Product could not be removed.");
      Console.WriteLine(ex.Message);
    }
  }
}

 

6.   编译运行,结果如下图所示:

7.   查看NHCookbook数据库,将会看到一行产品数据:单价为$9.99的Sprockets.

原理

  这个示例中,我们有两个接收product更新的服务,第一个,ProductCatalog 使用NHibernate存储product数据.第二个,WarehouseFacade,一个小的外观定义模式,她并不是定义良好的,她通过所代表的更大的warehouse系统,使用许多不同的技术整合我们的应用程序.
  在这两个系统中的服务,提供了add, update和remove产品的功能.通过将这些功能封装到一个TransactionScope中,提供了回滚功能:在warehouse系统失败时,回滚对product catalog所做的修改,并且维护了一个一致的状态.
  NHibernate要求在和数据库交互时必须使用事务.TransactionScope并不是替代者.请看下图,TransactionScope完全涵盖了会话和NHibernate事务.应该在会话注销后调用TransactionScope.Complete().顺序不对的话,很可能导致像连接漏洞的bug.

  当移除一个product时,WarehouseFacade会抛出一个异常,奇怪的状况发生了.我们提交了NHibernate事务,但是为什么不能进行delete操作?实际上,delete操作发生了,但是被TransactionScope回滚取消了.启用NHibernate事务后,NHibernate会检测到由TransactionScope创建或征用的相关事务.本实例中,底层连接和数据库事务会被保持到TransactionScope提交,或者被回滚.

posted on 2012-07-16 14:08  迷恋弦哥  阅读(277)  评论(0编辑  收藏  举报