同一台服务器上抛出两个DbContext:该平台不支持分布式事务

时间:2019-05-31 00:12:16

标签: sql-server entity-framework-core dbcontext transactionscope distributed-transactions

我无法弄清TransactionScope为什么启动分布式事务(在SQL Server上未配置)。我想改用本地事务,当两个数据库位于同一SQL Server实例中时可以使用该事务。我的代码有什么问题,我该如何解决?我可以强制“交易范围”首先尝试本地交易吗?

数据库

enter image description here

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=DESKTOP;Initial Catalog=test;Integrated Security=True",
    "Test2Connection": "Data Source=DESKTOP;Initial Catalog=test2;Integrated Security=True"
  }
}

startup.cs 注册TestContext和Test2Context

services.AddDbContext<TestContext>(options =>
 options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

services.AddDbContext<Test2Context>(options =>
 options.UseSqlServer(Configuration.GetConnectionString("Test2Connection")));

services.AddTransient<ICustomerRepository, CustomerRepository>();
services.AddTransient<IMaterialRepository, MaterialRepository>();

// This service inject TestContext and Test2Context
services.AddTransient<ICustomerService, CustomerService>();

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

CustomerRepository (使用TestContext)

public class CustomerRepository : ICustomerRepository
    {
        private readonly TestContext _context;
        public CustomerRepository(TestContext context)
        {
            _context = context;
        }
        public Customer Retrieve(int id)
        {
            return _context.Customers.Where(x => x.Id == id).FirstOrDefault();
        }
    }

MaterialRepository (使用Test2Context)

public class MaterialRepository : IMaterialRepository
    {
        private readonly Test2Context _context;
        public MaterialRepository(Test2Context context)
        {
            _context = context;
        }
        public Material Retrieve(int id)
        {
            return _context.Materials.Where(x => x.Id == id).FirstOrDefault();
        }
    }

CustomerService

public class CustomerService : ICustomerService
    {
        private readonly ICustomerRepository _customerRepository;
        private readonly IMaterialRepository _materialRepository;
        public CustomerService(
            ICustomerRepository customerRepository, 
            IMaterialRepository materialRepository)
        {
            _customerRepository = customerRepository;
            _materialRepository = materialRepository;
        }
        public void DoSomething()
        {
            using (var transaction = new TransactionScope(TransactionScopeOption.Required
               //,new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }
                ))
            {
                var customer = _customerRepository.Retrieve(1);
                var material = _materialRepository.Retrieve(1); // The exception is thrown here !
                // _customerRepository.Save(customer);
                transaction.Complete();
            }
        }
    }

从第二个上下文引发的This platform does not support distributed transactions异常中读取。

enter image description here

1 个答案:

答案 0 :(得分:1)

  

为什么TransactionScope开始分布式交易?

因为您有两个不同的SQL Server会话。如果没有将交易升级为分布式交易,客户将无法在单独的会话上协调交易。

  

我可以强制“交易范围”首先尝试本地交易吗?

如果您对两个DbContext实例都使用单个Sql Server会话,则无需升级为分布式事务。

您应该能够简单地对两个DbContext使用相同的查询字符串,并且SqlClient将自动为这两者缓存并重用单个连接。当事务中登记的SqlConnection是Close()或Disposed()时,实际上将其搁置,以等待事务结果。任何以后使用相同的连接字符串尝试打开新的SqlConnection的尝试都将返回该相同的连接。默认情况下,DbContext将为每个操作打开和关闭SqlConnection,因此它应该从此行为中受益。

如果相同的连接字符串不起作用,则可能必须打开SqlConnection并使用它来构造两个DbContext实例。

但是,等等,这些表位于不同的数据库中。是的,如果有充分的理由,您可以将它们留在那里。您需要做一些工作,以使单个SqlConnection能够访问两个数据库中的对象。最好的方法是访问CREATE SYNONYM,以便您的应用程序可以连接到单个数据库并使用本地名称访问远程对象。这也使您可以在单个实例上拥有多个应用程序实例(对于开发/测试非常方便)。