EF Working with Transactions

原文:https://msdn.microsoft.com/en-us/data/dn456843.aspx

Prior to EF6 Entity Framework insisted on opening the database connection itself (it threw an exception if it was passed a connection that was already open). Since a transaction can only be started on an open connection, this meant that the only way a user could wrap several operations into one transaction was either to use a TransactionScope or use the ObjectContext.Connection property and start calling Open() and BeginTransaction() directly on the returned EntityConnection object. In addition, API calls which contacted the database would fail if you had started a transaction on the underlying database connection on your own.

Note: The limitation of only accepting closed connections was removed in Entity Framework 6. For details, see Connection Management (EF6 Onwards).

Starting with EF6 the framework now provides:

  1. Database.BeginTransaction() : An easier method for a user to start and complete transactions themselves within an existing DbContext – allowing several operations to be combined within the same transaction and hence either all committed or all rolled back as one. It also allows the user to more easily specify the isolation level for the transaction.
  2. Database.UseTransaction() : which allows the DbContext to use a transaction which was started outside of the Entity Framework.

Combining several operations into one transaction within the same context

Database.BeginTransaction() has two overrides – one which takes an explicit IsolationLevel and one which takes no arguments and uses the default IsolationLevel from the underlying database provider. Both overrides return a DbContextTransaction object which provides Commit() and Rollback() methods which perform commit and rollback on the underlying store transaction.

The DbContextTransaction is meant to be disposed once it has been committed or rolled back. One easy way to accomplish this is the using(…) {…} syntax which will automatically call Dispose() when the using block completes:

static void StartOwnTransactionWithinContext() 
        { 
            using (var context = new BloggingContext()) 
            { 
                using (var dbContextTransaction = context.Database.BeginTransaction()) 
                { 
                    try 
                    { 
                        context.Database.ExecuteSqlCommand( 
                            @"UPDATE Blogs SET Rating = 5" + 
                                " WHERE Name LIKE '%Entity Framework%'" 
                            ); 
 
                        var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
                        foreach (var post in query) 
                        { 
                            post.Title += "[Cool Blog]"; 
                        } 
 
                        context.SaveChanges(); 
 
                        dbContextTransaction.Commit(); 
                    } 
                    catch (Exception) 
                    { 
                        dbContextTransaction.Rollback(); 
                    } 
                } 
            } 
        } 

Note: Beginning a transaction requires that the underlying store connection is open. So calling Database.BeginTransaction() will open the connection  if it is not already opened. If DbContextTransaction opened the connection then it will close it when Dispose() is called.

Sometimes you would like a transaction which is even broader in scope and which includes operations on the same database but outside of EF completely. To accomplish this you must open the connection and start the transaction yourself and then tell EF a) to use the already-opened database connection, and b) to use the existing transaction on that connection.

To do this you must define and use a constructor on your context class which inherits from one of the DbContext constructors which take i) an existing connection parameter and ii) the contextOwnsConnection boolean.

Note: The contextOwnsConnection flag must be set to false when called in this scenario. This is important as it informs Entity Framework that it should not close the connection when it is done with it (e.g. see line 4 below):

using (var conn = new SqlConnection("...")) 

    conn.Open(); 
    using (var context = new BloggingContext(conn, contextOwnsConnection: false)) 
    { 
    } 
}

 

Furthermore, you must start the transaction yourself (including the IsolationLevel if you want to avoid the default setting) and let the Entity Framework know that there is an existing transaction already started on the connection (see line 33 below).

Then you are free to execute database operations either directly on the SqlConnection itself, or on the DbContext. All such operations are executed within one transaction. You take responsibility for committing or rolling back the transaction and for calling Dispose() on it, as well as for closing and disposing the database connection. E.g.:

static void UsingExternalTransaction() 
        { 
            using (var conn = new SqlConnection("...")) 
            { 
               conn.Open(); 
 
               using (var sqlTxn = conn.BeginTransaction(System.Data.IsolationLevel.Snapshot)) 
               { 
                   try 
                   { 
                       var sqlCommand = new SqlCommand(); 
                       sqlCommand.Connection = conn; 
                       sqlCommand.Transaction = sqlTxn; 
                       sqlCommand.CommandText = 
                           @"UPDATE Blogs SET Rating = 5" + 
                            " WHERE Name LIKE '%Entity Framework%'"; 
                       sqlCommand.ExecuteNonQuery(); 
 
                       using (var context =  
                         new BloggingContext(conn, contextOwnsConnection: false)) 
                        { 
                            context.Database.UseTransaction(sqlTxn); 
 
                            var query =  context.Posts.Where(p => p.Blog.Rating >= 5); 
                            foreach (var post in query) 
                            { 
                                post.Title += "[Cool Blog]"; 
                            } 
                           context.SaveChanges(); 
                        } 
 
                        sqlTxn.Commit(); 
                    } 
                    catch (Exception) 
                    { 
                        sqlTxn.Rollback(); 
                    } 
                } 
            } 
        } 

 

转载于:https://www.cnblogs.com/ly7454/p/4599149.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值