原文: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 >= );
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 >= );
foreach (var post in query)
{
post.Title += "[Cool Blog]";
}
context.SaveChanges();
} sqlTxn.Commit();
}
catch (Exception)
{
sqlTxn.Rollback();
}
}
}
}

最新文章

  1. ansible 简单使用
  2. Python列表
  3. 【ASP.NET 插件】zyUpload的HTML5上传插件
  4. Open vSwitch使用案例扩展实验
  5. Servlet跳转到Jsp的指定div
  6. 用xcode6.3编译早期工程时出现Undefined symbols for architecture x86_64错误的解决办法(转)
  7. 12天学好C语言——记录我的C语言学习之路(Day 5)
  8. PHP异常处理
  9. 自己写的操作sql的公共类
  10. WPF案例(二)模拟Apple OS 界面前后180度反转
  11. Kubernetes 1.5.1 部署
  12. builds error
  13. shell脚本8--录制终端会话
  14. 基于ELK5.1(ElasticSearch, Logstash, Kibana)的一次整合
  15. C#退出程序,退出任务管理器
  16. Java基类和派生类
  17. Delphi动态调用C++写的DLL
  18. 自制 Chrome Custom.css 设置网页字体为微软雅黑扩展
  19. Jquery教你写一个简单的轮播.
  20. MVC多语言设置 实战简洁版

热门文章

  1. DirectX11 With Windows SDK--16 流输出阶段
  2. SQL修改日期类型字段为字符串类型
  3. Django的项目创建,以及该端口号,语言随地区而变化
  4. Pycharm工具导入requests包(python新手)
  5. js设置document.domain实现跨域
  6. TIME_WAIT 太多的解决办法
  7. udp/tcp流程
  8. php 常用的自定义函数
  9. linux安装selenium+chrome+phantomjs
  10. Windows Internals 笔记——进程