Non riesco a capire perché TransactionScope stia iniziando una transazione distribuita (che non è configurata su SQL Server). Vorrei invece utilizzare la transazione locale, che può essere utilizzata quando due database si trovano nella stessa istanza di SQL Server. Cosa c'è di sbagliato nel mio codice, come posso ripararlo? Posso forzare Transope Scope a provare prima le transazioni locali?
Banche dati
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 registrando TestContext e 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 utilizzando 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 utilizzando 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();
}
}
Assistenza clienti
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();
}
}
}
lettura dal lancio del secondo contesto This platform does not support distributed transactions
eccezione delle This platform does not support distributed transactions
.
Anche la transazione distribuita viene attivata, quando si utilizza la stessa stringa di connessione per due contesti di database
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;
}
}
Assistenza clienti
var customer = _customerRepository.Retrieve(1);
var customerReadOnly = _customerReadOnlyRepository.Retrieve(1); // Throw's the same error.
perché TransactionScope sta iniziando una transazione distribuita?
Perché hai due diverse sessioni di SQL Server. Il cliente non ha modo di coordinare le transazioni in sessioni separate senza promuovere la transazione in una transazione distribuita.
Posso forzare Transope Scope a provare prima le transazioni locali?
Se si utilizza una singola sessione SQL Server per entrambe le istanze DbContext, non sarà necessario promuoverla per una transazione distribuita.
Dovresti essere in grado di utilizzare semplicemente stringhe di query identiche per entrambi DbContexts e SqlClient memorizzerà automaticamente nella cache e riutilizzerà una singola connessione per entrambi. Quando una SqlConnection inclusa in una transazione è Close () o Disposed (), viene effettivamente messa da parte in attesa dell'esito della transazione. Qualsiasi tentativo successivo di aprire un nuovo SqlConnection utilizzando la stessa stringa di connessione restituirà la stessa connessione. Un DbContext, per impostazione predefinita, aprirà e chiuderà SqlConnection per ogni operazione, quindi dovrebbe beneficiare di questo comportamento.
Se la stessa stringa di connessione non funziona, potrebbe essere necessario aprire SqlConnection e utilizzarlo per costruire entrambe le istanze DbContext.
Ma aspetta, le tabelle sono in diversi database. Sì, e se c'è una buona ragione per cui puoi lasciarli lì. È necessario eseguire alcune operazioni per consentire a un singolo SqlConnection di accedere agli oggetti in entrambi i database. Il modo migliore per farlo è CREARE SYNONYM in modo che l'applicazione possa connettersi a un singolo database e accedere agli oggetti remoti con nomi locali. Ciò consente anche di avere più istanze dell'applicazione su una singola istanza (utile per dev / test).