You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何EF Core 6中跨双DbContext的事务未升级为分布式事务且无报错?

问题:为何EF Core 6中跨两个DbContext的TransactionScope未抛出异常?

我有如下匿名后的控制器代码。由于EF Core 6不支持分布式事务,且在同一个TransactionScope中使用了两个不同的DbConnection和DbContext,我预期会抛出异常,且事务应尝试升级为分布式事务。我已调试确认两个连接的进程ID不同,因此与连接池无关。为何这段代码能正常运行?

环境信息

  • .NET Core 6
  • Npgsql.EntityFrameworkCore.PostgreSQL 版本 6.0.7
  • Microsoft.EntityFrameworkCore 版本 6.0.8

控制器代码

using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Npgsql;

namespace Controllers.Test;

[Route("test")]
public sealed class TestController : ControllerBase
{
    private readonly IConfiguration _configuration;
    private readonly IHostEnvironment _host;

    public TestController(
        IConfiguration configuration,
        IHostEnvironment host)
    {
        _configuration = configuration;
        _host = host;
    }

    [HttpGet]
    public void Get()
    {
        using var transactionScope = new TransactionScope(
            TransactionScopeOption.Required,
            new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromMinutes(30) },
            TransactionScopeAsyncFlowOption.Enabled);

        var fooConnectionString = _configuration.GetConnectionString("foo")!;
        var barConnectionString = _configuration.GetConnectionString("bar")!;

        using var fooConnection = new NpgsqlConnection(fooConnectionString);
        using var barConnection = new NpgsqlConnection(barConnectionString);
        
        fooConnection.Open();
        barConnection.Open();

        using var fooDbContext = new FooDbContext(new DbContextOptionsBuilder<FooDbContext>()
            .UseNpgsql(fooConnection, o => o.UseNodaTime())
            .Options);
        using var barDbContext = new BarDbContext(new DbContextOptionsBuilder<BarDbContext>()
            .UseNpgsql(barConnection, o => o.UseNodaTime())
            .Options);

        var foo = fooDbContext.Foos.OrderBy(x => x.Id).First();
        foo.SequenceNumber++;
        fooDbContext.SaveChanges();

        var bar = barDbContext.Bars.OrderBy(x => x.Id).First();
        bar.SequenceNumber++;
        barDbContext.SaveChanges();

        transactionScope.Complete();
    }
}

解答

这段代码能正常运行的核心原因是两个DbContext的操作并未真正加入同一个TransactionScope事务,而是各自使用独立的本地事务,具体细节如下:

  1. TransactionScope与第一个连接的本地事务绑定
    当你在TransactionScope中打开第一个PostgreSQL连接时,Npgsql会将该连接的本地事务关联到TransactionScope。此时TransactionScope本质是本地事务,而非分布式事务。

  2. 第二个连接未加入TransactionScope事务
    EF Core 6本身不支持通过TransactionScope自动升级为分布式事务,而PostgreSQL在.NET Core环境下默认也不依赖MSDTC(分布式事务协调器)。当你打开第二个连接时,EF Core并不会强制抛出异常,而是允许该连接创建自己的独立本地事务——也就是说第二个DbContext的操作完全不受TransactionScope的管控。

  3. 事务原子性并未实现
    你调用的两次SaveChanges()各自在独立的本地事务中执行,transactionScope.Complete()并不会保证两个操作的原子性。例如,如果第一个SaveChanges()成功后,第二个操作抛出异常,第一个操作的修改不会被回滚,这和你预期的分布式事务行为完全不符。

  4. 为何未抛出异常?
    EF Core 6对TransactionScope的支持有限,尤其是针对不支持分布式事务的数据库提供程序。它不会主动检测并抛出"跨连接事务"的异常,而是静默允许每个连接使用自己的本地事务,这就导致代码能正常运行,但并未实现你想要的事务原子性。

你可以通过以下方式验证:在第二个SaveChanges()之前手动抛出异常,观察第一个SaveChanges()的修改是否被保留——结果会是第一个修改依然存在,证明两个操作不在同一个事务中。

内容的提问来源于stack exchange,提问作者Chris Yungmann

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 02:24:56