5

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.

error
  • 131
  • 1
  • 7
  • Share a single DbConnection between the two contexts, and then you can use a single transaction for both. "Init service with connection" is not a "bad idea". Your services should share DbContext and DbConnection instances for exactly this reason, and your Dependency Injection framework will handle this for you. – David Browne - Microsoft Aug 28 '17 at 14:01
  • If I add dbConnection as parameter in Service, I get that my connection is open during all working time of Service live. I can spend 1 minute for get data and 30 minute for calculation. So is it normal that I have open connection even when it is not need? – error Aug 28 '17 at 14:43
  • If your service instances are long-lived (why?), then you need some other object to represent a Unit Of Work. And that is _assuming_ that you have a valid reason to use multiple DbContexts. – David Browne - Microsoft Aug 28 '17 at 14:45
  • Service live per user for one request. I can not guarantee that request will be promptly processed. – error Aug 28 '17 at 14:55
  • Reason for using bounded context: we have database with 150 tables approximately, different process and customers. – error Aug 28 '17 at 15:05
  • "I can not guarantee that request will be promptly processed' why not? That seems like something you should be able to control. Anyway you can Open the DbConnection just before the Transaction and Close it afterword if you are worried about the number of open connections caused by long-running operations. – David Browne - Microsoft Aug 28 '17 at 15:06

2 Answers2

0

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:

Denis Bubnov
  • 1,985
  • 4
  • 23
  • 47
-1

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

Rejwanul Reja
  • 934
  • 1
  • 14
  • 16