FreeSql.Repository (四)工作单元

欢迎来到《FreeSql.Repository 仓储模式》系列文档,本系列文档专注介绍 【仓储+工作单元】 的使用方式。完整文档请前往 wiki 中心:https://github.com/dotnetcore/FreeSql/wiki

UnitOfWork 可将多个仓储放在一个单元管理执行,最终通用 Commit 执行所有操作,内部采用了数据库事务;

罗里吧嗦一堆,简单点理解:把它看成事务

工作单元定义

public interface IUnitOfWork : IDisposable
{
    /// <summary>
    /// 开启事务,或者返回已开启的事务
    /// </summary>
    /// <param name="isCreate">若未开启事务,则开启</param>
    /// <returns></returns>
    DbTransaction GetOrBeginTransaction(bool isCreate = true);
    IsolationLevel? IsolationLevel { get; set; }
    void Commit();
    void Rollback();
}

除上述的定义,我们增加了 IFreeSql Orm 属性访问原始用法,并且保持在一个事务单元执行。

如何使用

工作单元建议使用 IoC 管理生命周期,否则用起来那叫一个麻烦:

using (var uow = fsql.CreateUnitOfWork())
{
    var songRepo = fsql.GetRepository<Song>();
    var userRepo = fsql.GetRepository<User>();
    songRepo.UnitOfWork = uow; //手工绑定工作单元
    userRepo.UnitOfWork = uow;

    songRepo.Insert(new Song());
    userRepo.Update(...);

    uow.Orm.Insert(new Song()).ExecuteAffrows();
    //注意:uow.Orm 和 fsql 都是 IFreeSql
    //uow.Orm CRUD 与 uow 是一个事务(理解为临时 IFreeSql)
    //fsql CRUD 与 uow 不在一个事务

    uow.Commit();
}

依赖注入

以 webapi 类型项目为例,如果注入 IUnitOfWork,一次请求只能开启一个工作单元事务。因此我们引入工作单元管理器(UnitOfWorkManager)的概念,负责管理请求内的一组工作单元。

本章节内容有点繁琐,不过它是一劳永逸的,建议耐着性子看完,并且使用起来。从此不再为事务的用法烦恼掉发……

UnitOfWorkManager 支持六种传播方式(propagation),意味着跨方法的事务非常方便,并且支持同步异步:

  • Requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,默认的选择。
  • Supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • Mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • NotSupported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • Never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • Nested:以嵌套事务方式执行。
UnitOfWorkManager 成员 说明
IUnitOfWork Current 返回当前的工作单元
void Binding(repository) 将仓储的事务交给它管理
IUnitOfWork Begin(propagation, isolationLevel) 创建工作单元

第一步:配置 Startup.cs 注入

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IFreeSql>(fsql);
    services.AddScoped<UnitOfWorkManager>();
    services.AddFreeRepository(null, typeof(Startup).Assembly);
}

第二步:定义事务特性

[AttributeUsage(AttributeTargets.Method)]
public class TransactionalAttribute : Attribute
{
    public Propagation Propagation { get; set; } = Propagation.Requierd;
    public IsolationLevel? IsolationLevel { get; set; }
}

第三步:引入动态代理库

在 Before 从容器中获取 UnitOfWorkManager,调用它的 var uow = Begin(attr.Propagation, attr.IsolationLevel) 方法

在 After 调用 Before 中的 uow.Commit 或者 Rollback 方法,最后调用 uow.Dispose

第四步:在 Controller 或者 Service 中使用事务特性

public class SongService
{
    BaseRepository<Song> _repoSong;
    BaseRepository<Detail> _repoDetail;
    SongRepository _repoSong2;

    public SongService(
        BaseRepository<Song> repoSong,
        BaseRepository<Detail> repoDetail,
        SongRepository repoSong2)
    {
        _repoSong = repoSong;
        _repoDetail = repoDetail;
        _repoSong2 = repoSong2;
    }

    [Transactional]
    public virtual void Test1()
    {
        //这里 _repoSong、_repoDetail、_repoSong2 所有操作都是一个工作单元
        this.Test2();
    }

    [Transactional(Propagation = Propagation.Nested)]
    public virtual void Test2() //嵌套事务,新的(不使用 Test1 的事务)
    {
        //这里 _repoSong、_repoDetail、_repoSong2 所有操作都是一个工作单元
    }
}

是不是进方法就开事务呢?

不一定是真实事务,有可能是虚的,就是一个假的 unitofwork(不带事务)

也有可能是延用上一次的事务

也有可能是新开事务,具体要看 Propagation 传播模式

示范项目:https://github.com/dotnetcore/FreeSql/tree/master/Examples/aspnetcore_transaction

实战项目:https://github.com/zhontai

系列文章导航

FreeSql.Repository (四)工作单元

上一篇:docker安装oracle11g


下一篇:Mongodb学习(一)、Mongodb的安装与注册服务