依赖服务中的事务管理
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>();
这些CreateOrganization
和CreateUser
可以在代码的任何部分独立使用:
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]
}
}
在此实现中,CreateOrganization
和 CreateUser
有自己的事务,如果 [3] 抛出异常,则无论如何都会创建组织。
好的,因为 ISession
注册为 Scoped,那么我可以从 CreateOrganization
和 CreateUser
([1] 和 [2])中删除 _repository.Commit
。在这种情况下,[4] 将负责提交所有更改。
但是OrganizationService
和UserService
单独使用怎么办呢?毕竟,现在它们已经成为非独立服务,如果不将更改提交委托给其他服务就无法保存数据:
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。
我对下一刻的架构方案很感兴趣
我有:
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>();
这些CreateOrganization
和CreateUser
可以在代码的任何部分独立使用:
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]
}
}
在此实现中,CreateOrganization
和 CreateUser
有自己的事务,如果 [3] 抛出异常,则无论如何都会创建组织。
好的,因为 ISession
注册为 Scoped,那么我可以从 CreateOrganization
和 CreateUser
([1] 和 [2])中删除 _repository.Commit
。在这种情况下,[4] 将负责提交所有更改。
但是OrganizationService
和UserService
单独使用怎么办呢?毕竟,现在它们已经成为非独立服务,如果不将更改提交委托给其他服务就无法保存数据:
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。