Два DbContext на одном сервере: эта платформа не поддерживает распределенные транзакции - PullRequest
0 голосов
/ 31 мая 2019

Я не могу понять, почему 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

Распределенная транзакция также запускается при использовании одной и той же строки подключения для двух контекстов базы данных

startup.cs

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

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

CustomerReadOnlyRepository

public class CustomerReadOnlyRepository : ICustomerReadOnlyRepository
    {
        private readonly TestReadOnlyContext _context;
        public CustomerReadOnlyRepository(TestReadOnlyContext context)
        {
            _context = context;
        }
        public Customer Retrieve(int customerId)
        {
            Customer customer = _context.Customers.Where(x => x.Id == customerId).Include("Offices").FirstOrDefault();

            return customer;
        }
    }

CustomerService

var customer = _customerRepository.Retrieve(1);
var customerReadOnly = _customerReadOnlyRepository.Retrieve(1); // Throw's the same error.

1 Ответ

1 голос
/ 02 июня 2019

почему TransactionScope запускает распределенную транзакцию?

Потому что у вас есть два разных сеанса SQL Server. У клиента нет возможности координировать транзакции в отдельных сеансах без преобразования транзакции в распределенную транзакцию.

Могу ли я заставить область транзакций сначала попробовать локальную транзакцию?

Если вы используете один сеанс Sql Server для обоих экземпляров DbContext, то ему не нужно переходить к распределенной транзакции.

Вы должны иметь возможность просто использовать одинаковые строки запроса для обоих DbContexts, а SqlClient автоматически кеширует и повторно использует одно соединение для обоих. Когда SqlConnection, зачисленный в транзакцию, имеет значение Close () или Disposed (), он фактически откладывается в ожидании результата транзакции. Любая последующая попытка открыть новый SqlConnection с использованием той же строки соединения вернет это же соединение. DbContext по умолчанию будет открывать и закрывать SqlConnection для каждой операции, поэтому он должен извлечь выгоду из этого поведения.

Если одна и та же строка подключения не работает, вам, возможно, придется открыть SqlConnection и использовать его для создания обоих экземпляров DbContext.

Но подождите, таблицы находятся в разных базах данных. Да, и если есть веская причина для этого, вы можете оставить их там. Вам нужно проделать определенную работу, чтобы разрешить одному SqlConnection доступ к объектам в обеих базах данных. Лучший способ сделать это - CREATE SYNONYM s, чтобы ваше приложение могло подключаться к одной базе данных и получать доступ к удаленным объектам с локальными именами. Это также позволяет вам иметь несколько экземпляров вашего приложения в одном экземпляре (удобно для dev / test).

...