领域模型中分散的事务如何集中统一处理(C#解决方案)

开篇

什么是事务,事务的应用场景

作项目时,常常会遇到一些需求,好比注册用户时,要求同时存入用户的基本信息和初始化该用户的账户,若是在这两个环节中的任何一个地方出错,则要求回滚全部操做,这就是事务,它的主要目的是为了数据的完整性,即要么全盘成功,要么全盘失败,你们都穿一条裤衩。数据库

今天我要说的复琐事务

事务的概念很简单,应用起来也很方便,固然能够在存储过程当中直接实现事务保证数据的完整性,可是我通常不会这样作,由于我基本把数据库就当作一个数据仓库而已,它是一个容器,没有其余逻辑,逻辑都放领域层来处理,由于这不是咱们今天的主要话题,因此就很少讲了。回到正题,那么咱们要在C#中实现事务是怎么实现的呢,很简单,直接调用Connection的BeginTransaction方法便可,咱们拿ADO.Net中的SqlServer举例,完整的应用示例以下:框架

以上代码两Usr表中插入两条数据,要求这两条数据必须都成功插入,不然回滚,逻辑很简单。ide

更好的方式

上面的代码没什么问题,可是若是咱们每一个地方都这样写,那的确很繁琐,因此出来了不少ORM框架来辅助咱们,再看一下EntityFramework是怎么实现事务的spa

代码:线程

using (RetailEntities context = new RetailEntities())
      {
          context.Customers.Add(entity1);
          context.Customers.Add(entity2);
          context.SaveChanges();
      }

估计你们都用过,没错,它在SaveChanges方法以前,自动为咱们开始了一个事务。这样作的好处,不言而喻,比起上面那样作可谓是简洁了许多。可是今天咱们不使用EntityFramework,而是使用本身的方式来解决将要面临的问题。设计

我实现事务的方式

看过我博客的朋友有可能会知道,我之前也写过一个ORM框架(XDBFramework),今天咱们主要不讲这个框架,但会引用其中的代码来讲明问题,下面就上代码,来看在XDBFramework中是怎样实现事务的。3d

DataContext类code

DataAccess类,也就是上面的_da对象

 

生成的DataContext类blog

 

调用代码:

 using (var context = new DataContext())
            {
                long count = context.Admin.Count();
                var a = DataContextStatic.Recharge.Count(s => s.State == Convert.ToInt32(1));
                string passport = "x" + DateTime.Now.Ticks;
                context.BeginTransaction();
                try
                {
                    context.Admin.Insert(new Model_Admin
                                             {
                                                 Passport = passport,
                                                 Password = "123456",
                                                 AddTime = DateTime.Now
                                             });
                    context.Admin.Insert(new Model_Admin
                                             {
                                                 Passport = passport + "_2",
                                                 Password = "123456",
                                                 AddTime = DateTime.Now
                                             });
                    context.CommitTransaction();
                }
                catch
                {
                    context.RollbackTransaction();
                }

                
            }
事务代码

能这样实现的关键在于DataContext对象拥有一个DataAccess对象,调用各个DataContext上的各个实体操做自身对应表的时候会调用同一个DataAccess对象,也就是同一个connection,同一个trasaction进行数据的检索,插入,更新,删除操做。这样就实现了把多个操做放到一个事务内。

引出问题

想象一下这样一种场景,用户在购买一件商品时,既要生成订单,又要扣除其账户的相应金额,咱们假定在生成订单时会生成多个明细,扣除余额时也会有多条明细。

解决办法:

1.有一种方式就是创建一个manager类,而后把生成订单和扣除其账户的相应金额的逻辑写到一个事务放到这个manager中,若是采用这种方式,固然就不会有本文存在了。

2.这里要以领域模型的思惟方式,即什么对象作什么事,以领域模型建模,这里就有两个模型,即用户账户和订单。

因而:  

  a.订单实体里有生成订单的方法,这个方法中包含插入订单总览和明细。

  b.账户这个实体里有扣除余额的方法,这个方法中包含扣除余额和扣除明细

这样一来,就有好几个须要事务,即:

    1,生成订单和扣除余额

    2,生成订单和明细

    3,扣除余额和扣除明细

 这样问题就来了,它们自己是一个事务,为了模型的完整,咱们如今将它们放在了三处。如何在最后将它们合并成一个事务呢,这就是咱们今天要解决的问题。

进入实例

场景

工做流你们应该接触过,我如今就拿这个来讲,在保存设计图时,我须要保存那些节点和线条的位置和大小等,在这以前若是是一副被修改的流程图我还要清除原来的元素,在这个场景中,有不少操做都要求在一个事务中,好比以前元素的清徐,新节点的保存,新链接线的保存,新注释的保存等。而这里又有不少实体,好比流程图模版,活动节点(用户活动,系统活动),链接线,注释等,拿它们的保存方法来说都有不一样逻辑,甚至在保存方法内也还有事务的存在。

代码

流程图的保存方法

能够看到这里开始了一个大的事务,将各元素的清除和保存都放到了这个事务中。

如下的删除方法中又包含了事务(以"Transaction(()=>)"开始的地方)

实现

总结

主要思想是为每一个线程分配一个DataContext对象,若是发现是同一个线程在开启事务时,判断是否已经开启事务,如是则直接使用已经开启的事务,如不然开启一个新的事务,原理很简单。

相关文章
相关标签/搜索