服务层如何管理事务?

发布于 2024-10-15 19:30:05 字数 711 浏览 3 评论 0原文

我们正在开发具有以下架构的 .Net 应用程序:表示层(使用 ASP.Net MVC 2 的 MVC 模式)、服务层、数据访问层(使用实体框架上的存储库模式)。

我们决定将事务管理放在服务层,但我们不确定如何实现它。我们希望完全在服务层级别控制事务。也就是说,每次控制器调用服务层中的方法时,它都必须是有关数据库更新的原子操作。

如果服务层提供的不同服务之间没有关系,那么事情就很简单:每个方法应该在执行结束时提交更改(即在它使用的上下文上调用 save 方法)。但有时服务层的服务是协同工作的。

例如:我们提供的发货服务具有一个确认方法,该方法接收以下参数:发货 ID、指示其是否对应于新客户或现有客户的标志、客户 ID(如果发货确认是针对现有客户的)客户)和客户名称(如果是新客户)。如果标志设置为“新客户”,则服务层必须 (a) 创建客户并 (b) 确认发货。对于 (a) 发货服务调用客户服务(客户服务已经实现了创建新客户并将其存储在数据库中所需的验证和逻辑)。

在这种情况下谁应该提交更改?

  • 客服应该做吗?它无法在创建新客户后提交更改,因为稍后在发货确认方法中可能会出现问题,但它必须在直接调用的情况下提交更改(在其他用例中,提供用于创建客户端)。
  • 调用服务方法的控制器应该这样做吗?但控制器不应该知道任何有关事务的信息,我们决定将所有事务知识放在服务层中。
  • 服务层中的事务管理器?如何设计?谁在何时调用?

我们是否应该遵循这样的设计模式?

We’re developing a .Net application with the following architecture: presentation layer (using MVC pattern with ASP.Net MVC 2), service layer, data access layer (using repository pattern over Entity Framework).

We’ve decided to put the transaction management in the service layer but we’re not sure about how to implement it. We want to control the transaction entirely at the service layer level. That is, every time a controller calls a method in the service layer, it has to be an atomic operation regarding database updates.

If there were no relation between different services provided in the service layer, then it would be simple: each method should commit the changes at the end of its execution (that is, call the save method on the context it uses). But sometimes services at the service layer work together.

e.g.: we provide a shipment service that has a confirm method which receives the following parameters: the shipment id, a flag indicating if it corresponds to a new customer or an existing one, the customer id (in case the shipment confirmation is for an existing customer) and a customer name (in case it is for a new customer). If the flag is set to "new customer", then the service layer has to (a) create the customer and (b) confirm the shipment. For (a) the shipment service calls the customer service (which already implements the validations and logic needed to create a new customer and store it in the database).

Who should commit the changes in this scenario?

  • Should the customer service do it? it cannot commit the changes after creating the new customer, because something can go wrong later in the shipment confirmation method, but it has to commit its changes in the case it is call directly (in other use case, provided to create a client).
  • Should the controller calling the service method do it? but the controller shouldn’t know anything about transactions, we've decided to put all transaction knowladge in the service layer.
  • A Transaction Manager in the services layer? How to design it?, who calls it and when?

Is there a design pattern for this that we should follow?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

山人契 2024-10-22 19:30:05

我的服务上有一个 Commit() ,只有在服务创建 UnitOfWork 时才会提交,如果它在构造函数中传递,则提交不会执行任何操作。

我为该服务使用了第二个(内部)构造函数:

public class MyService
{
private IUnitOfWork _uow;
private bool _uowInternal;

public MyService()
{
    _uow = new UnitOfWork();
    _uowInternal = false;
}

internal MyService(IUnitOfWork uow)
{
    _uow = uow;
    _uowInternal = true;
}
public MyServiceCall()
{
    // Create second service, passing in my UnitOfWork:
    var svc2 = new MySecondService(_uow);

    // Do your stuff with both services.
    ....
    // Commit my UnitOfWork, which will include all changes from svc2:
    Commit();
}

public void Commit()
{
    if(!_uowInternal)
        _uow.Commit();
}
}

I have a Commit() on my service, this only commits if the UnitOfWork is created by the service, if it is passed in the constructor the commit does nothing.

I used a second (internal) constructor for the service:

public class MyService
{
private IUnitOfWork _uow;
private bool _uowInternal;

public MyService()
{
    _uow = new UnitOfWork();
    _uowInternal = false;
}

internal MyService(IUnitOfWork uow)
{
    _uow = uow;
    _uowInternal = true;
}
public MyServiceCall()
{
    // Create second service, passing in my UnitOfWork:
    var svc2 = new MySecondService(_uow);

    // Do your stuff with both services.
    ....
    // Commit my UnitOfWork, which will include all changes from svc2:
    Commit();
}

public void Commit()
{
    if(!_uowInternal)
        _uow.Commit();
}
}
音盲 2024-10-22 19:30:05

在使用WCF和L2S而不是EF的类似架构中,我们选择在主服务接口实现类中使用事务。我们使用 TransactionScope 来实现此目的:

public void AServiceMethod() {
    using(TransactionScope ts = new TransactionScope()) {
         service1.DoSomething();
         service2.DoSomething();
         ts.Complete();
    }
}

主要缺点是交易可能会变大。在这种情况下,如果事务块中的某个服务调用仅需要只读访问,我们会将其包装在嵌套的 TransactionScope(TransactionScopeOption.Suppress) 块中,以防止在事务块中进一步锁定行/表。交易生命周期。

In a similar architecture with WCF and L2S instead of EF, we chose to use transactions in the main service interface implementation class. We used TransactionScope to achieve this:

public void AServiceMethod() {
    using(TransactionScope ts = new TransactionScope()) {
         service1.DoSomething();
         service2.DoSomething();
         ts.Complete();
    }
}

The main disadvantage is that the transaction may get big. In that case, if for example one of the service calls in the transaction block requires only readonly access, we wrap it in a nested TransactionScope(TransactionScopeOption.Suppress) block to prevent locking rows/tables further in the transaction lifetime.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文