Skip to content

Unit of Work

AlexLEWIS edited this page Sep 8, 2021 · 7 revisions

中文 | English

Unit of work can put multiple repositories into one unit for internal management and execution, and finally execute all operations through Commit. Unit of work internally uses database transactions.

static IFreeSql fsql = new FreeSql.FreeSqlBuilder()
    .UseConnectionString(FreeSql.DataType.MySql, connectionString)
    //Automatically synchronize the entity structure to the database.
    .UseAutoSyncStructure(true) 
    //Be sure to define as singleton mode
    .Build(); 

Usage

using (var uow = fsql.CreateUnitOfWork())
{
  var songRepo = fsql.GetRepository<Song>();
  var userRepo = fsql.GetRepository<User>();
  songRepo.UnitOfWork = uow; //Manually bind unit of work
  userRepo.UnitOfWork = uow;

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

  uow.Orm.Insert(new Song()).ExecuteAffrows();
  //Note: uow.Orm and fsql are both IFreeSql
  //uow.Orm CRUD and uow are the same transaction (understood as temporary IFreeSql)
  //fsql CRUD and uow are not in the same transaction

  uow.Commit();
}

Reference: Use TransactionalAttribute + UnitOfWorkManager in ASP.NET Core to achieve multiple transaction propagation

Interface Definition

The uow.GetOrBeginTransaction() method can get the transaction object.

public interface IUnitOfWork : IDisposable
{
  /// <summary>
  /// The object Select/Delete/Insert/Update/InsertOrUpdate is consistent with the unit of work transaction and can be omitted to pass WithTransaction
  /// </summary>
  IFreeSql Orm { get; }

  /// <summary>
  /// Open the transaction, or return to the opened transaction
  /// </summary>
  /// <param name="isCreate">If the transaction is not opened, then open</param>
  /// <returns></returns>
  DbTransaction GetOrBeginTransaction(bool isCreate = true);

  IsolationLevel? IsolationLevel { get; set; }

  void Commit();

  void Rollback();

  /// <summary>
  /// Entity change tracking within the unit of work
  /// </summary>
  DbContext.EntityChangeReport EntityChangeReport { get; }
}

Entity Changing Event

Global Settings:

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

Individual settings:

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

The parameter report is a list collection, and the type of the collection elements is defined as follows:

public class ChangeInfo {
  public object Object { get; set; }
  public EntityChangeType Type { get; set; }
  /// <summary>
  /// When Type = Update, get the object before the update
  /// </summary>
  public object BeforeObject { get; set; }
}
public enum EntityChangeType { Insert, Update, Delete, SqlRaw }
Type of Change Description
Insert The entity object is inserted
Update The entity object is updated
Delete The entity object is deleted
SqlRaw SQL statement executed

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

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

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

Reference

Clone this wiki locally