ABP-工作单元

前言:

什么是工作单元:

意思是可以“工作”的单元:我们再看一个现实中例子,也最能说明Unit Of Work所包含的意思,就是银行转账操作,包含两个动作:转出方扣钱和转入方加钱,这两个动作要么都完成,要么都不完成,也就是事务操作,完成就Commit(提交),完不成就Rollback(回滚)。

回到Unit Of Work的定义,Unit of Work是用来解决领域模型存储和变更工作,在ORM进行持久化的时候,比如Entity Framework的SaveChanges操作,其实就可以看做是Unit Of Work,也就是定义中所说“用来解决领域模型存储和变更工作”,但是如果项目是基于Entity Framework进行DDD(领域驱动设计)开发设计的,那Entity Framework中的Domain Model就必然包含业务逻辑,这就不符合“而这些数据层业务并不属于领域模型本身具有的”,也就是说Unit Of Work必须独立于Domain Layer(领域层),注意独立的业务是“数据层”业务,并不是业务场景中的“业务”,比如“转账业务”,转出方扣钱和转入方加钱这个业务就属于“数据层业务”,有的人会把Unit Of Work放在Domain Layer(领域层)中,其实是有些不恰当的,应该是放在Infrastructure Layer(基础层)中,但其实也只是相对而言,如果涉及到具体的业务单元模块,具体实现可以放在领域层中

简单来说:

在很多操作中我们可能是进行多项操作的,比如 同时保存两个表的数据,如果我们先保存一个表(SaveChanges()之后),再保存另一个表(SaveChanges())其实是两个不同的事务,通俗的讲,我们对数据库提交了两次数据。而UnitOfWork 就是 把本次操作(分别保存两张表)放在统一事务中,所有的CRUD完成后,统一提交数据,即保证数据的一致性,又减少了提交次数,提高性能。

 

 

仓储类

在DDD(领域驱动设计)开发设计中,Unit Of Work的使用一般会结合Repository(仓储)使用,有关Repository可以参阅dudu的一篇文章:http://www.cnblogs.com/dudu/archive/2011/05/25/repository_pattern.html,文中的解释很清楚直白:

Repository:是一个独立的层,介于领域层与数据映射层(数据访问层)之间。它的存在让领域层感觉不到数据访问层的存在,它提供一个类似集合的接口提供给领域层进行领域对象的访问。Repository是仓库管理员,领域层需要什么东西只需告诉仓库管理员,由仓库管理员把东西拿给它,并不需要知道东西实际放在哪。

Unit Of Work所做的工作可以看做是收集Repository出入库的“商品”,便于一次装车,运输过程中如果没有出现问题,那这车的所有“商品”就安全到达,如果出现问题,那这车的所有“商品”全部打回,这辆车就是“单元”的意思。

ABP中的连接和事务管理 

Unit Of Work模式,由马丁大叔提出,是一种数据访问模式。UOW模式的作用是在业务用例的操作中跟踪对象的所有更改(增加、删除和更新),并将所有更改的对象保存在其维护的列表中。在业务用例的终点,通过事务,一次性提交所有更改,以确保数据的完整性和有效性。总而言之,UOW协调这些对象的持久化及并发问题。

如果手动实现的思路:

https://blog.csdn.net/sigxxl/article/details/7875260

http://www.cnblogs.com/yuangang/p/5725201.html

 

第一话:强大的ABP

在ABP框架中默认把我们的应用服务都注册了工作单元,假设我们有一个像下面的应用服务:

在Create方法中,我们使用了person仓储插入了一个person,而且使用user仓储增加总人数。在这里例子中,这两个仓储共享相同的连接和事务,因为它们在一个应用服务方法中。ABP在进入CreatePerson方法时打开一个数据库连接并开始一个事务,如果没有抛出异常事务会在方法结尾时提交,如果有任何异常发生,将会回滚。这样一来,在CreatePerson方法中的所有数据库操作都成了原子的(工作单元)

       
        public virtual async Task<PersonEditDto> Create(PersonEditDto input)
        {//插入person数据
            var entity = input.MapTo<Person>();
            entity =await _entityRepository.InsertAsync(entity);
//为person插入对应的tel表

            var telentity = new Tel
            { Id = entity.Id,
                PersonId = entity.Id,
                PhoneNumber = "1"
            };
            var tel = await _TelRepository.InsertAsync( telentity );

            return entity.MapTo<PersonEditDto>();

        }

 

第二步:如果非服务应用,只是一个类,我们怎么用呢?

声明[UnitOfWork]特性

[UnitOfWork]
 public virtual async Task<PersonEditDto> Create(PersonEditDto input)
        {
            var entity = input.MapTo<Person>();
            entity =await _entityRepository.InsertAsync(entity);
           
            var telentity = new Tel
            { //Id = entity.Id,
                PersonId = entity.Id,
                PhoneNumber = "1"
            };
            var tel = await _TelRepository.InsertAsync( telentity );

            return entity.MapTo<PersonEditDto>();

        }

第二种方法是使用IUnitOfWorkManager.Begin()方法,如下所示:

public class MyService
{
    private readonly IUnitOfWorkManager _unitOfWorkManager;
    private readonly IPersonRepository _personRepository;
    private readonly IStatisticsRepository _statisticsRepository;

    public MyService(IUnitOfWorkManager unitOfWorkManager, IPersonRepository personRepository, IStatisticsRepository statisticsRepository)
    {
        _unitOfWorkManager = unitOfWorkManager;
        _personRepository = personRepository;
        _statisticsRepository = statisticsRepository;
    }

    public void CreatePerson(CreatePersonInput input)
    {
        var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };

        using (var unitOfWork = _unitOfWorkManager.Begin())
        {
           //插入person数据
            var entity = input.MapTo<Person>();
            entity =await _entityRepository.InsertAsync(entity);
//为person插入对应的tel表

            var telentity = new Tel
            { Id = entity.Id,
                PersonId = entity.Id,
                PhoneNumber = "1"
            };
            var tel = await _TelRepository.InsertAsync( telentity );

            unitOfWork.Complete();
        }
    }
}

第三步:关闭工作单元

是的ABP框架的服务应用是默认帮我们实现了工作单元,我们一直在使用工作单元但是我们却不了解他

本篇我们就深入了解下他。如果先发现我们失去了工作单元会发送什么那就把工作单元关闭。

下面可以看到他会报错

 [UnitOfWork(IsDisabled = true)]
        public virtual async Task<PersonEditDto> Create(PersonEditDto input)
        {
            var entity = input.MapTo<Person>();
            entity =await _entityRepository.InsertAsync(entity);

            var telentity = new Tel
            { //Id = entity.Id,
                PersonId = entity.Id,
                PhoneNumber = "1"
            };
            var tel = await _TelRepository.InsertAsync( telentity );

            return entity.MapTo<PersonEditDto>();

        }

第四步:关闭工作单元的事务

工作单元默认是事务的(本质如此)。因此,ABP会开始->提交->回滚一个显式的数据库级别的事务。在一些特殊场合,事务可能会造成问题,因为它可能会锁住数据库中的一些行或者表。在这种情况下,你可能想关闭数据库级别的事务。UnitOfWork特性可以在构造函数中获得一个布尔值,从而以非事务形式工作。用法如下:

 [UnitOfWork(isTransactional: false)]  :关闭事务后,如果User创建报错就不会回滚

 [UnitOfWork(isTransactional: false)]  
 public virtual async Task<PersonEditDto> Create(PersonEditDto input)
        {
            var entity = input.MapTo<Person>();
            entity =await _entityRepository.InsertAsync(entity);
           
            var telentity = new Tel
            { //Id = entity.Id,
                PersonId = entity.Id,
                PhoneNumber = "1"
            };
            var tel = await _TelRepository.InsertAsync( telentity );

            return entity.MapTo<PersonEditDto>();

        }

假设你在一个非事务工作单元里更新一些实体,即使是这种况下,所有更新也是在工作单元的最后,执行一个单独的数据库命令。但是,如果在一个非事务性工作单元里,直接执行一条Sql语句,它会被直接执行并且不会被回滚。

使用非事务性工作单元要小心,因为大部分情况下,应当用事务性保证数据完整性。如果你只是读取数据,而不做修改,那么就可以安全地使用非事务性。

SaveChanges

(在非事务性工作的时候,可以不受下面错误语句的影响。或者在想先让上面执行,下面要上面的数据的时候)

在工作单元结束时ABP会保存所有的更改,而你不需要做任何事情。但是有些时候,你或许会想要在工作单元的执行过程中就保存更改。例如:使用EntityFramerok保存一个新实体的时候取得一个这个实体的ID。 保存并调出

你可以使用当前工作单元的 SaveChanges 或者 SaveChangesAsync 方法。

如下面代码使用了 SaveChanges后就可以,先获取entity.Id了

 public virtual async Task<PersonEditDto> Create()
        {
            PersonEditDto input = new PersonEditDto {Name="12",Sex=0 };
            var entity = input.MapTo<Person>();
            entity = await _entityRepository.InsertAsync(entity);
            //没这个是不可以先获取Id的
            CurrentUnitOfWork.SaveChanges();

            var telentity = new Tel
            {
                Id = entity.Id,
                PersonId = entity.Id,
                PhoneNumber = "1"
            };
            var tel = await _TelRepository.InsertAsync(telentity);

            return entity.MapTo<PersonEditDto>();

        }

注意:如果当前工作单元是事务性的,那么这个方法将会无效。

第五步:如果我们的GetAll没了工作单元

当在一个仓储方法之外调用GetAll()时,必须存在一个打开的数据库连接,因为GetAll返回了IQueryable,而且IQueryable会延迟执行。直到调用ToList()方法或者在foreach循环中使用IQueryable,才会真正执行数据库查询。因此,调用ToList()方法时,数据库连接必须是活着的(alive)。 关闭后查询就不可以用

考虑一下下面的例子:

      [UnitOfWork(IsDisabled =true)] 
        public async Task<List<Person>> GetAll()
        {
            var entity = _entityRepository.GetAll();

            return entity.ToList();
        }

第六步:自动保存

就下面2行代码person的sex就改变了。我们甚至不用调用_personRepository.Update方法。ORM框架会跟踪工作单元中实体的所有改变,并将改变反应给数据库。

注意没有必要为应用服务方法声明UnitOfWork特性,因为它们默认已经是工作单元了。

    public string Update(int id,int sex)

        {
            var person = _entityRepository.Get(id);
            person.Sex = sex;

            return "完成";

        }

 

番外改变一下工作单元的参数

public class SimpleTaskSystemCoreModule : AbpModule
{
    public override void PreInitialize()
    {
        Configuration.UnitOfWork.IsolationLevel = IsolationLevel.ReadCommitted;
        Configuration.UnitOfWork.Timeout = TimeSpan.FromMinutes(30);
    }

    //...其他模块方法
}

 

转载:

https://www.cnblogs.com/farb/p/ABPUnitofWork.html#ways

https://www.cnblogs.com/xishuai/p/3750154.html

https://www.jianshu.com/p/6f22d7bcb87a

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值