依赖服务中的事务管理

Transaction management in dependent services

我对下一刻的架构方案很感兴趣

我有:

public class GenericRepository<T> : IDisposable {
    public GenericRepository(ISession session){
        _session = session;
    };
    public T InsertAsync(T entity){...};
    public IQueryable<T> Read(){...};
    public T UpateAsync(T entity){...};
    public void DeleteAsync(T entity){...};
    public Task Commit(){
        return _session.Transaction.Commit();
    };
    public void Dispose(){
        if(_session.Transaction.IsActive){
            _session.Transaction.Rollback();
        }
    };
}
public class UserService{
    public UserService(GenericRepository<User> repository){...}
    public long CreateUser(string userName){
        ...
        _repository.Commit(); // [1]
    };
}
public class OrganizationService{
    public OrganizationService(GenericRepository<Organization> repository){...}
    public int CreateOrganization(string code){
        ...
        _repository.Commit(); // [2]
    };
}

使用以下注册:

services.AddScoped<ISession>(x => x.GetRequiredService<NHSessionProvider>().OpenSession());
services.AddScoped(typeof(GenericRepository<>));
services.AddScoped<UserService>();
services.AddScoped<OrganizationService>();

这些CreateOrganizationCreateUser可以在代码的任何部分独立使用:

public IActionResult Post([FromServices] OrganizationService service, [FromBody] string code){
    service.CreateOrganization(code);
    return Ok();
}
public IActionResult Post([FromServices] UserService service, [FromBody] string userName){
    service.CreateUser(userName);
    return Ok();
}

但是,现在我有一项新服务:

public class MyBillingService{
    public MyBillingService(GenericRepository<Contractor> repository, OrganizationService organizationService, UserService userService){...}
    public int CreateNewContractor(string organizationCode, string userName){
        ...
        _organizationService.CreateOrganization(organizationCode);
        ...
        _userService.CreateUser(userName);// [3]
        ...     
        _repository.Commit(); // [4]
    }
}

在此实现中,CreateOrganizationCreateUser 有自己的事务,如果 [3] 抛出异常,则无论如何都会创建组织。 好的,因为 ISession 注册为 Scoped,那么我可以从 CreateOrganizationCreateUser([1] 和 [2])中删除 _repository.Commit。在这种情况下,[4] 将负责提交所有更改。

但是OrganizationServiceUserService单独使用怎么办呢?毕竟,现在它们已经成为非独立服务,如果不将更改提交委托给其他服务就无法保存数据:

public IActionResult Post([FromServices] UserService service, [FromServices] TransactionService transaction, [FromBody] string userName){
    service.CreateUser(userName);   
    transaction.Commit();
    return Ok();
}

就这个决定而言是个好决定?

事务需要一个工作单元。没有其他方法来协调存储库。你在这里遇到问题的原因是你的整个设计是错误的。

首先,您根本不应该拥有这些存储库。您使用的是 EF Core,它是一个 ORM,并且已经实现了存储库和工作单元模式。使用 ORM 就是选择为您的 DAL 使用第三方库。围绕它包装您自己的 DAL 层是毫无意义的,并且会给您的应用程序带来不必要的维护和测试成本,但好处 。您的服务应直接取决于您的上下文。

那么,服务应该是独立的功能单元。如果他们依赖于其他服务,那你就错了。该服务应与您应用程序的特定子域相对应。如果用户和组织需要在事务上一起管理,那么您应该有一个包含两者的一个服务。

或者,如果您 want/need 将两者分开,则需要合并 sagas 的概念。

所以我开始更多地转向 Chris 在他的回答中提到的内容并直接使用 ISession,但我过去使用过通用存储库。您的回购无法正确处理已经开始的交易。

所以我的通用存储库有几个方法

    protected virtual TResult Transact<TResult>(Func<TResult> func)
    {
        if (_session.Transaction.IsActive)
            return func.Invoke();

        TResult result;
        using (var tx = _session.BeginTransaction(IsolationLevel.ReadCommitted))
        {
            result = func.Invoke();
            tx.Commit();
        }

        return result;
    }

    protected virtual void Transact(System.Action action)
    {
        Transact(() =>
        {
            action.Invoke();
            return false;
        });
    }

然后实现回购功能的方法如下所示

    public bool Remove(T item)
    {
        Transact(() => _session.Delete(item));
        return true;
    }

这允许该方法在已经启动的情况下使用现有事务,否则为这项工作创建您的事务。

您的存储库中也不应包含 Dispose,因为您不拥有对 ISession 的引用。它的生命周期应该由创建该实例的人处理。

通用存储库也不应具有提交功能,除非它显式启动新事务。所以现在你需要有一些东西来处理启动和提交所述事务。在 Web 场景中,您通常处于每个请求场景的会话中。这意味着您正在 BeginRequest 中创建会话并在 EndRequest 中处理它。然后,我使用事务属性来管理在执行控制器操作之前创建事务,并在执行控制器方法之后 commit/rollback。