EF6将每个存储过程调用都包装在自己的事务中。如何防止这种情况发生?

36

使用 SQL Server Profiler 进行分析:EF 6 带有 BEGIN TRANCOMMIT TRAN 包装的每个存储过程调用。

这难道不是一个破坏性的改变吗?

或许这不仅是个破坏性的变化,而且使得存储过程中任何事务逻辑都不可能用 ROLLBACK TRAN 来回滚(注意: 在 SQL Server 中没有嵌套的事务), 当我们回滚到 @@TRANCOUNT 为零时就会出现 "执行后的事务计数表明 BEGIN 和 COMMIT 语句不匹配。之前的计数 = 1, 当前计数 = 0." 的标准 SQL Server 错误,因为 EF 6 所以我们正处于事务中。

请不要问我为什么想调用存储过程。我有成百上千个存储过程,它们都使用了 TRY ... COMMIT ... CATCH ROLLBACK 逻辑。

有什么办法可以防止 EF 6 这样做吗?


使用UseTransaction并手动启动自己怎么样? - cincura.net
如果您想要更多的控制权,直接使用ADO.Net怎么样? - Aron
请注意,在 EF Core 中,ExecuteSqlInterpolated/ExecuteSqlRaw 不会自动启动事务。 https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.relationaldatabasefacadeextensions.executesqlraw?view=efcore-6.0 - Michael Freidgeim
4个回答

41

有一个ExecuteSqlCommand方法的重载可以防止这种行为:

db.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sql, parameters);

1
谢谢,Max,你太棒了 :-)。有趣的是,EF大师们忽略了这个问题超过两个月。(并忽略了这是一个重大变更的事实) - g.pickardou
1
救命的信息 - 已经搜索了几周!谢谢! - eghetto
关于调用返回数据的存储过程所必须使用的 SqlQuery 命令怎么办呢?它似乎会强制关闭你打开的任何 Database.BeginTransaction 调用,并且不给你任何避免它的选项。 - Triynko

32
在 EF 6.1.2 中,一个标志可以控制行为。将EnsureTransactionsForFunctionsAndCommands设置为false会影响已导入实体的存储过程(这些存储过程在内部调用ExecuteFunction())。
 using (SomeEf6Context ctx = NewContext())
 {
     ctx.Configuration.EnsureTransactionsForFunctionsAndCommands = false;
     // Call an imported SP
 }      

该设置不会影响任何SaveChanges()的调用。

MSDN 链接


2
强调一下,这个修复需要更新到EF 6.1.2或更高版本。我曾经四处奔波,想知道为什么我找不到EnsureTransactionsForFunctionsAndCommands属性,差点找了一个星期,因为我一直在使用EF 6.0.0。 - Zachary
你救了我的命。 - Angesehen

0

正如 crokusek 所说,您可以设置该标志以禁用存储过程的事务。

如果您正在使用任何依赖注入(DI)库,您可以像这样设置它(我正在使用 Simple Injector):

public partial class Startup
{
    public Container ConfigureSimpleInjector(IAppBuilder app)
    {
        var container = new Container();

        // Configure OWIN and Identity Framework
        ...

        // Configure persistence
        container.RegisterPerWebRequest<FakeDbContext>(() =>
        {
            var fakeDbContext = new FakeDbContext();
            fakeDbContext.Configuration.EnsureTransactionsForFunctionsAndCommands = false;
            return fakeDbContext;
        }

        // Register other services
        ...

        container.Verify();

        // For MVC
        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

        return container;
     }
}

0

仅作贡献,我正在使用Unity作为DI,EF 6.1.3和数据库优先,并且当我调用在我的edmx文件中映射的过程或函数时,我收到消息:"因为会话中有其他线程正在运行,所以不允许新事务"。选项EnsureTransactionsForFunctionsAndCommands = false解决了这个问题。 Max Zerbini的解决方案也有效,但我需要为每个过程调用使用那种方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接