Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transaction between contexts

I'm developing a Console Application using Entity Framework Core (7).

The application is divided into 3 different areas but the database is shared. I created 3 different DbContext and now I need to perform a transaction between all of them. So I need an atomic operation the save all the changes or nothing (rollback).

I know that in entity frameowrk 6 there was a class called TransactionScope but I cant find an alterntive in EF Core.

Using the following code:

public static void Main(string[] args)
    {
        var options = new DbContextOptionsBuilder<DbContext>()
        .UseSqlServer(new SqlConnection("Server=x.x.x.x,1433;Database=test;user id=test;password=test;"))
        .Options;

        var cat = new Cat { Name = "C", Surname = "C", Age = 55 };
        var dog = new Dog { Date = DateTime.Now, Code = 120, FriendId = cat.Id };


        using (var context1 = new DogsContext(options))
        {
            using (var transaction = context1.Database.BeginTransaction())
            {
                try
                {
                    context1.Dogs.Add(dog);
                    context1.SaveChanges();

                    using (var context2 = new CatsContext(options))
                    {
                        context2.Database.UseTransaction(transaction.GetDbTransaction());
                        context2.Cats.Add(cat);
                    }

                    transaction.Commit();
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    transaction.Rollback();
                }
            }
        }
    }

I get the following error:

System.InvalidOperationException: ExecuteScalar requires the command to have a transaction when the connection assigned to the co
mmand is in a pending local transaction.  The Transaction property of the command has not been initialized.
   at System.Data.SqlClient.SqlCommand.ValidateCommand(Boolean async, String method)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStrea
m, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteScalar()
   at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, String executeMe
thod, IReadOnlyDictionary`2 parameterValues, Boolean openConnection, Boolean closeConnection)
   at Microsoft.EntityFrameworkCore.Migrations.HistoryRepository.Exists()
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
like image 212
BlackEye Avatar asked Dec 07 '16 14:12

BlackEye


People also ask

Is SaveChanges a transaction?

In Entity Framework, the SaveChanges() method internally creates a transaction and wraps all INSERT, UPDATE and DELETE operations under it. Multiple SaveChanges() calls, create separate transactions, perform CRUD operations and then commit each transaction.

What is TransactionScope in Entity Framework?

NET framework it provides management of its own transaction components using TransactionScope class. TransactionScope is a class of System Namespace. It can also be termed as Transactions Namespace. The TransactionScope class supports transactions from code blocks and that is why it plays a key role in the .

How are transactions handled within a database?

Transaction PrinciplesOperations on persistent classes in a database are always performed in the context of a transaction. A transaction is an ordered sequence of operations that transforms a database from one state of consistency to another state of consistency.

What is SaveChanges Entity Framework?

SaveChanges()Saves all changes made in this context to the database. This method will automatically call DetectChanges() to discover any changes to entity instances before saving to the underlying database.


1 Answers

TransactionScope is not part of Entity Framework. Its part of the System.Transactions namespace. Additionally, TransactionScope is not the recommended approach for handling transactions with Entity Framework 6.x.

With Entity Framework Core you can share a transaction across multiple contexts for relational databases only. The contexts must share the same database connection.

More information here: https://learn.microsoft.com/en-us/ef/core/saving/transactions

Example (not tested):

        using (var context1 = new YourContext())
        {
            using (var transaction = context1.Database.BeginTransaction())
            {
                try
                {
                    // your transactional code
                    context1.SaveChanges();
                    
                    using (var context2 = new YourContext())
                    {
                        context2.Database.UseTransaction(transaction.GetDbTransaction());
                        // your transactional code
                    }
                    
                    // Commit transaction if all commands succeed, transaction will auto-rollback when disposed if either commands fails
                    transaction.Commit();
                }
                catch (Exception)
                {
                    // handle exception
                }
            }
        }
like image 190
William Xifaras Avatar answered Oct 22 '22 10:10

William Xifaras