如何检查DbContext是否有事务? [英] How to check whether DbContext has transaction?

查看:154
本文介绍了如何检查DbContext是否有事务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:
我有WCF服务,其中SimpleIjector作为IoC,每个WCF请求都会创建DbContext实例。

Background: I have WCF service with SimpleInjector as IoC which creates instance of DbContext per WCF request.

后端本身就是CQRS。 CommandHandlers有很多修饰符(验证,授权,日志记录,不同处理程序组的一些通用规则等),其中之一是Transaction Decorator:

Backend itself is CQRS. CommandHandlers have a lot of decorators (validation, authorization, logging, some common rules for different handler groups etc) and one of them is Transaction Decorator:

public class TransactionCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand> 
    where TCommand : ICommand
{
    private readonly ICommandHandler<TCommand> _handler;
    private readonly IMyDbContext _context;
    private readonly IPrincipal _principal;

    public TransactionCommandHandlerDecorator(ICommandHandler<TCommand> handler,
        IMyDbContext context, IPrincipal principal)
    {
        _handler = handler;
        _context = context;
        _principal = principal;
    }

    void ICommandHandler<TCommand>.Handle(TCommand command)
    {
        using (var transaction = _context.Database.BeginTransaction())
        {
            try
            {
                var user = _context.User.Single(x => x.LoginName == _principal.Identity.Name);
                _handler.Handle(command);
                _context.SaveChangesWithinExplicitTransaction(user);
                transaction.Commit();
            }
            catch (Exception ex)
            {
                transaction.Rollback();
                throw;
            }
        }
    }
}

问题当任何命令尝试链式执行同一WCF请求中的另一个命令时,会发生此错误。
我在此行得到了预期的例外:

Problem occurs when any command tries to chain execute another command within the same WCF request. I got an expected exception at this line:

using (var transaction = _context.Database.BeginTransaction())

因为我的DbContext实例已经有事务。

because my DbContext instance already has a transaction.

有什么方法可以检查当前事务的存在吗?

Is there any way to check current transaction existence?

推荐答案

您可以使用实体框架的DbContext中的事务,而不必使用事务可能或应该使用 TransactionScope 类,它创建一个环境事务范围并在后台管理与(SQL)数据库的所有连接的事务。

Instead of using the transaction from the DbContext of Entity Framework you could or maybe should use the TransactionScope class which creates an ambient transaction scope and manages transactions of all connections made to the (SQL) database under the covers.

它甚至会直接放入 SqlCommand 中使用准确的(区分大小写)连接字符串,则在同一事务中使用> SqlCommand 。写入 MessageQueue 也封装在同一事务中

It even would put a direct SqlCommand in the same transaction if you would use the exact (case-sensitive) connectionstring for the SqlCommand. Messages writen to the MessageQueue are also encapsulated in the same transaction

它甚至可以同时管理与不同数据库的连接。为此,它使用 DTC Windows服务。请注意,如果需要进行配置,这很麻烦。通常,使用单个数据库连接(或到同一数据库的多个连接)就不需要DTC。

It even could manage connections to different databases at the same time. It uses the DTC windows service for this. Beware that this is a pain to configure if needed. Normally, with a single DB connection (or multiple connections to the same DB) you won't need the DTC.

TransactionScopeCommandHandlerDecorator 实现非常简单:

public class TransactionScopeCommandHandlerDecorator<TCommand> 
        : ICommandHandler<TCommand>
{
    private readonly ICommandHandler<TCommand> decoratee;

    public TransactionScopeCommandHandlerDecorator(ICommandHandler<TCommand> decoratee)
    {
        this.decoratee = decoratee;
    }

    public void Handle(TCommand command)
    {
        using (var scope = new TransactionScope())
        {
            this.decoratee.Handle(command);

            scope.Complete();
        }
    }
}

但是:正如qujck在评论中已经提到的,您缺少 ICommandHandler 作为原子操作的概念。一个命令处理程序永远不要引用另一个命令处理程序。不仅对事务不利,而且请考虑以下问题:

But: As qujck already mentioned in the comments, you are missing the concept of ICommandHandler as an atomic operation. One commandhandler should never reference another commandhandler. Not only is this bad for transactions, but also consider this:

想象一下应用程序的增长,您会将一些命令处理程序重构为后台线程,该线程将在某些Windows服务中运行。在此Windows服务中, PerWcfOperation 生活方式不可用。您可能需要 LifeTimeScope 生活方式。因为您的设计允许这样做,所以这很好!您通常会将命令处理程序包装在 LifetimeScopeCommandHandler 装饰器以启动 LifetimeScope 。在您当前的设计中,单个命令处理程序引用了其他命令处理程序,您会遇到问题,因为每个命令处理程序都将在其自己的作用域中创建,因此会比其他命令处理程序注入另一个DbContext!

Imagine the application grows and you would refactor some of your commandhandlers to a background thread, which will run in some windows service. In this windows service a PerWcfOperation lifestyle is not available. You would need a LifeTimeScope lifestyle for you commandhandlers now. Because your design allows it, which is great by the way!, you would typicaly wrap your commandhandlers in a LifetimeScopeCommandHandler decorator to start the LifetimeScope. In your current design where a single commandhandler references other commandhandlers you will run into a problem, because every commandhandler will be created in its own scope a thus gets an other DbContext injected than the other commandhandlers!

因此,您需要进行一些重新设计,并使您的命令处理程序整体抽象并创建用于执行DbContext操作的较低级别的抽象。

So you need to do some redesign and make your commandhandlers holistic abstractions and create a lower level abstraction for doing the DbContext operations.

这篇关于如何检查DbContext是否有事务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆