Skip to content

工作单元

28810 edited this page Mar 21, 2020 · 32 revisions

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

var connstr = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;" + 
    "Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=10";

static IFreeSql fsql = new FreeSql.FreeSqlBuilder()
    .UseConnectionString(FreeSql.DataType.MySql, connstr)
    .UseAutoSyncStructure(true) //自动同步实体结构到数据库
    .Build(); //请务必定义成 Singleton 单例模式

如何使用

using (var uow = fsql.CreateUnitOfWork()) {
    
    var songRepo = uow.GetRepository<Song>();
    var userRepo = uow.GetRepository<User>();

    //上面两个仓储,由同一UnitOfWork uow 创建
    
    //在此执行仓储操作
    
    //这里不受异步方法影响

    uow.Commit();
}

参考:在 asp.net core 中注入工作单元方法

//第一步:
public class UnitOfWorkRepository<TEntity> : BaseRepository<TEntity, int>
{
  public UnitOfWorkRepository(IFreeSql fsql, IUnitOfWork uow) : base(fsql, null, null)
  {
    this.UnitOfWork = uow;
  }
}

//第二步:
public void ConfigureServices(IServiceCollection services)
{
  services.AddSingleton<IFreeSql>(fsql);
  services.AddScoped<FreeSql.IUnitOfWork>(sp => fsql.CreateUnitOfWork());

  services.AddScoped(typeof(IReadOnlyRepository<>), typeof(UnitOfWorkRepository<>));
  services.AddScoped(typeof(IBasicRepository<>), typeof(UnitOfWorkRepository<>));
  services.AddScoped(typeof(BaseRepository<>), typeof(UnitOfWorkRepository<>));
  services.AddScoped(typeof(IReadOnlyRepository<,>), typeof(UnitOfWorkRepository<,>));
  services.AddScoped(typeof(IBasicRepository<,>), typeof(UnitOfWorkRepository<,>));
  services.AddScoped(typeof(BaseRepository<,>), typeof(UnitOfWorkRepository<,>));

  //批量注入程序集内的所有自建仓储类,可以根据自己需要来修改
  Assembly[] assemblies = new [] { typeof(XxxRepository).Assembly };
  if (assemblies?.Any() == true)
      foreach (var asse in assemblies)
        foreach (var repo in asse.GetTypes().Where(a => a.IsAbstract == false && typeof(UnitOfWorkRepository).IsAssignableFrom(a)))
          services.AddScoped(repo);
}

接口定义

uow.GetOrBeginTransaction() 方法可获取事务对象。

public interface IUnitOfWork : IDisposable
{

    DbTransaction GetOrBeginTransaction(bool isCreate = true);

    IsolationLevel? IsolationLevel { get; set; }

    /// <summary>
    /// 是否启用工作单元
    /// </summary>
    bool Enable { get; }

    void Commit();

    void Rollback();

    /// <summary>
    /// 禁用工作单元
    /// <exception cref="Exception"></exception>
    /// <para></para>
    /// 若已开启事务(已有Insert/Update/Delete操作),调用此方法将发生异常,建议在执行逻辑前调用
    /// </summary>
    void Close();

    /// <summary>
    /// 开启工作单元
    /// </summary>
    void Open();

    /// <summary>
    /// 此工作单元内的实体变化跟踪
    /// </summary>
    DbContext.EntityChangeReport EntityChangeReport { get; }
}

实体变化事件

全局设置:

fsql.SetDbContextOptions(opt => {
    opt.OnEntityChange = report => {
        Console.WriteLine(report);
    };
});

单独设置:

var uow = fsql.CreateUnitOfWork();
uow.OnEntityChange = report => {
    Console.WriteLine(report);
};

参数 report 是一个 List 集合,集合元素的类型定义如下:

public class EntityChangeInfo
{
    public object Object { get; set; }
    public EntityChangeType Type { get; set; }
}
public enum EntityChangeType { Insert, Update, Delete, SqlRaw }
变化类型 说明
Insert 实体对象被插入
Update 实体对象被更新
Delete 实体对象被删除
SqlRaw 执行了SQL语句

SqlRaw 目前有两处地方比较特殊:

  • 多对多联级更新导航属性的时候,对中间表的全部删除操作;
  • 通用仓储类 BaseRepository 有一个 Delete 方法,参数为表达式,而并非实体;
int Delete(Expression<Func<TEntity, bool>> predicate);

DbContext.SaveChanges,或者 Repository 对实体的 Insert/Update/Delete,或者 UnitOfWork.Commit 操作都会最多触发一次该事件。

参考资料

Clone this wiki locally