依赖服务中的事务管理

时间:2019-09-23 09:34:18

标签: c# asp.net-core dependency-injection nhibernate architecture

我对接下来的架构解决方案感兴趣。

我有:

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已注册为“作用域”,所以我可以从_repository.CommitCreateOrganization([1]和[2])中删除CreateUser。在这种情况下,[4]将负责提交所有更改。

但是,OrganizationServiceUserService分别使用时该怎么办?毕竟,现在它们已成为非独立服务,并且在不将更改提交委托给其他服务的情况下无法保存数据:

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

就此决定是一个好的决定

2 个答案:

答案 0 :(得分:2)

交易需要一个工作单元。没有其他方法可以协调存储库。您在这里面临问题的原因是您的整个设计是错误的。

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

然后,服务应该是功能齐全的独立单元。如果他们依赖其他服务,那您做错了。该服务应与您的应用程序的特定子域相对应。如果需要以事务方式一起管理用户和组织,那么您应该拥有包含两者的一项服务。

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

答案 1 :(得分:1)

因此,我已经开始朝着克里斯在他的答案中提到的方向发展,并直接使用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;
    }

如果该方法已经启动,则允许该方法使用现有的事务,否则将为该工作创建事务。

由于您不拥有对ISession的引用,因此您的回购中也不应包含Dispose。它的生命周期应该由创建该实例的人来处理。

通用存储库也不应具有提交功能,除非它明确启动了新事务。因此,现在您需要处理启动和提交所说事务的东西。在Web方案中,通常每个请求都有一个会话。这意味着您将在BeginRequest中创建会话并将其在EndRequest中处置。然后,我使用事务属性在执行控制器操作之前管理创建的事务,并在执行控制器方法之后提交/回滚。