I need to realize transaction under two models (using two separated bounded contexts). So code like this:
using (TransactionScope scope = new TransactionScope())
{
//Operation 1
using(var context1 = new Context1())
{
context1.Add(someCollection1);
context1.SaveChanges();
}
//Operation 2
using(var context2 = new Context2())
{
context2.Add(someCollection2);
context2.SaveChanges();
}
scope.Complete();
}
return the exception:
An ambient transaction has been detected. Entity Framework Core does not support ambient transactions. See http://go.microsoft.com/fwlink/?LinkId=800142
In Link they advice to use one connection for two context. And use context2 in using block of context1.
But if I use own controller/service for every model:
using (TransactionScope scope = new TransactionScope())
{
service1.DoWork();
service2.DoWork();
scope.Complete();
}
How should I implement this? Add connection as parameter in method - seems absurdly. Init service with connection also bad idea.
You can use 'contexts' like this:
using (var context1 = new Context1())
{
using (var transaction = context1.Database.BeginTransaction())
{
try
{
context1.Add(someCollection1);
context1.SaveChanges();
// if we don't have errors - next step
using(var context2 = new Context2())
{
// second step
context2.Add(someCollection2);
context2.SaveChanges();
}
// if all success - commit first step (second step was success completed)
transaction.Commit();
}
catch (Exception)
{
// if we have error - rollback first step (second step not be able accepted)
transaction.Rollback();
}
}
}
If you will be use many controllers/services, than you can pass DbConnection
into your methods of services with using internal methods for this operation. You must encapsulate logic of low layer.
Init service with connection also bad idea. - may be you right. But you can try init two methods with one connection and single transaction.
See next answers, they can help you:
Use below snipet
using (var context = new MyContext())
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
var customer = context.Customers
.Where(c => c.CustomerId == 2)
.FirstOrDefault();
customer.Address = "43 rue St. Laurent";
context.SaveChanges();
var newCustomer = new Customer
{
FirstName = "Elizabeth",
LastName = "Lincoln",
Address = "23 Tsawassen Blvd."
};
context.Customers.Add(newCustomer);
context.SaveChanges();
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
}
}
}
More Details: https://entityframeworkcore.com/saving-data-transaction
To your statement of How should I implement this?
:
using (TransactionScope scope = new TransactionScope())
{
service1.DoWork();
service2.DoWork();
scope.Complete();
}
I am using a Controller > Service > Repository pattern, and I have found the same error is resolved for me when utilizing the async option. Each repository has a db context injected in Startup.cs
following the usual approach. Perhaps this was fixed in a newer version of .NET Core, or maybe I am just using the same context instance across repositories? The following works for me:
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
service1.DoWork();
service2.DoWork();
scope.Complete();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With