了解Entity Framework中事务处理

      Entity Framework 6以前,框架本身并没有提供显式的事务处理方案,在EF6中提供了事务处理的API。

      所有版本的EF,只要你调用SaveChanges方法进行插入、修改或删除,EF框架会自动将该操作进行事务包装。这种方法无法对事务进行显式的控制,例如新建事务等,可能会造成事务的粒度非常大,降低效率。EF不会对查询进行事务包装。

     从EF6开始,默认情况下,如果每次调用Database.ExecuteSqlCommand(),如果其不在存在于任何事务中,则会将该Command包装到一个事务中。框架提供了多种重载,允许你重写这些方法,实现事务的控制。同样,执行存储过程的ObjectContext.ExecuteFunction()方法是实现了这种机制(但是ExecuteFunction不能被重写)。这两种情况下,使用的事务隔离级别均为数据库提供的默认隔离级别,SQL Server中使用的是READ COMMITED。

      有同学提供了EF6之前版本的事务方案,如下:

复制代码

 1 using (BlogDbContext context =new BlogDbContext())2 {3     using (TransactionScope transaction =new TransactionScope())4     {5         context.BlogPosts.Add(blogPost);6         context.SaveChanges();7         postBody.ID = blogPost.ID;8         context.EntryViewCounts.Add(9             new EntryViewCount() { EntryID = blogPost.ID });
10         context.PostBodys.Add(postBody);
11         context.SaveChanges();
12         //提交事务
13         transaction.Complete();
14     } 
15 }

复制代码

      其实,上面方法执行结果不会错,但是存在隐患,这样情况下,显式事务其实是多余的。所以我对这种方案持怀疑态度(没有进行内部代码的分析,有时间了分析下,希望大家拍砖)。

      官方体统的解决方案为:

复制代码

 1 using System.Collections.Generic; 2 using System.Data.Entity; 3 using System.Data.SqlClient; 4 using System.Linq; 5 using System.Transactions; 6  7 namespace TransactionsExamples 8 { 9     class TransactionsExample 
10     { 
11         static void UsingTransactionScope() 
12         { 
13             using (var scope = new TransactionScope(TransactionScopeOption.Required)) 
14             { 
15                 using (var conn = new SqlConnection("...")) 
16                 { 
17                     conn.Open(); 
18  
19                     var sqlCommand = new SqlCommand(); 
20                     sqlCommand.Connection = conn; 
21                     sqlCommand.CommandText = 
22                         @"UPDATE Blogs SET Rating = 5" + 
23                             " WHERE Name LIKE '%Entity Framework%'"; 
24                     sqlCommand.ExecuteNonQuery(); 
25  
26                     using (var context = 
27                         new BloggingContext(conn, contextOwnsConnection: false)) 
28                     { 
29                         var query = context.Posts.Where(p => p.Blog.Rating > 5); 
30                         foreach (var post in query) 
31                         { 
32                             post.Title += "[Cool Blog]"; 
33                         } 
34                         context.SaveChanges(); 
35                     } 
36                 } 
37  
38                 scope.Complete(); 
39             } 
40         } 
41     } 
42 }

复制代码

 

      一般情况下,用户不需要对事务进行特殊的控制,使用EF框架默认行为即可。如果要对细节进行控制,参考下面章节:

EF6 API工作机制

EF6以前版本EF框架自己管理数据库连接,如果你自己尝试打开连接可能会抛出异常(打开一个已打开的连接会抛出异常)。由于事务必须在一个打开的连接上执行,因此要合并一系列操作到一个事务中,要么使用TractionScope,要么使用ObjectContext.Connection属性直接执行EntityConnection的Open(),并BeginTransaction()。另外,如果你在数据库底层连接上执行了事务,上面API会失败。

注意:EF6中移除了仅接受关闭连接的限制。

EF6 开始提供了:

Database.BeginTransaction() : 为用户提供一种简单易用的方案,在DbContext中启动并完成一个事务 -- 合并一系列操作到该事务中。同时使用户更方便的指定事务隔离级别。

Database.UseTransaction() : 允许DbContext使用一个EF框架外的事务。

在同一DbContext中合并一系列操作到一个事务中

Database.BeginTransaction()有两个重载方法。一个方法提供一个IsolationLevel参数,另一个无参方法使用底层数据库提供程序默认的数据库事务隔离级别。两个重载方法均返回一个DbContextTransaction对象,该对象提供Commit和Rollback方法,用于数据库底层事务的提交和回滚。

使用DbContextTransaction意味着,一旦提交或回滚事务,就要释放该对象。一种简单的方法是使用using语法,在using代码块结束时自动调用该对象的Dispose方法。

复制代码

 1 using System; 2 using System.Collections.Generic; 3 using System.Data.Entity; 4 using System.Data.SqlClient; 5 using System.Linq; 6 using System.Transactions; 7  8 namespace TransactionsExamples 9 { 
10     class TransactionsExample 
11     { 
12         static void StartOwnTransactionWithinContext() 
13         { 
14             using (var context = new BloggingContext()) 
15             { 
16                 using (var dbContextTransaction = context.Database.BeginTransaction()) 
17                 { 
18                     try 
19                     { 
20                         context.Database.ExecuteSqlCommand( 
21                             @"UPDATE Blogs SET Rating = 5" + 
22                                 " WHERE Name LIKE '%Entity Framework%'" 
23                             ); 
24  
25                         var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
26                         foreach (var post in query) 
27                         { 
28                             post.Title += "[Cool Blog]"; 
29                         } 
30  
31                         context.SaveChanges(); 
32  
33                         dbContextTransaction.Commit(); 
34                     } 
35                     catch (Exception) 
36                     { 
37                         dbContextTransaction.Rollback(); 
38                     } 
39                 } 
40             } 
41         } 
42     } 
43 }

复制代码

注意:启动一个事务需要底层数据库连接已打开。因此,如果连接未打开,调用Database.BeginTransaction()会打开连接,在其Dispose时关闭连接。

传递一个现有事务到DbContext

      有时,你可能需要在同一数据库上,执行一个EF框架之外更大范围的事务,这是就需要自己打开连接并启动事务,然后通知EF框架:

1) 使用已打开的数据库连接

2) 在该连接上使用现有的事务

      要实现上面的行为,你需要使用继承自DbContext的构造方法XXXContext(conn,contextOwnsConnection),其中:

                   conn : 是一个已存在的数据库连接

                   contextOwnsConnection : 是一个布尔值,指示上下文是否自己占用数据库连接。

注意:这种情况下,contextOwnsConnection必须设置为false,因为它通知EF框架,在自己使用完连接后,不要关闭它。见下面代码:

复制代码

1 using (var conn = new SqlConnection("...")) 
2 { 
3     conn.Open(); 
4     using (var context = new BloggingContext(conn, contextOwnsConnection: false)) 
5     { 
6     } 
7 }

复制代码

      此外,你必须自己启动事务(如果你不想使用默认IsolationLevel,可以自己设置之)并让EF框架知道该连接上已经存在已启动的事务(参考下面代码的33行)。
      然后就可以直接在连接上执行数据库操作,或者在DbContext上执行,所有这些操作均在同一事务中执行,你负责提交或回滚事务,并调用DatabaseTransaction.Dispose(),最后要关闭和释放数据库连接。请参考以下代码:

复制代码

 1 using System; 2 using System.Collections.Generic; 3 using System.Data.Entity; 4 using System.Data.SqlClient; 5 using System.Linq; 6 sing System.Transactions; 7  8 namespace TransactionsExamples 9 { 
10      class TransactionsExample 
11      { 
12         static void UsingExternalTransaction() 
13         { 
14             using (var conn = new SqlConnection("...")) 
15             { 
16                conn.Open(); 
17  
18                using (var sqlTxn = conn.BeginTransaction(System.Data.IsolationLevel.Snapshot)) 
19                { 
20                    try 
21                    { 
22                        var sqlCommand = new SqlCommand(); 
23                        sqlCommand.Connection = conn; 
24                        sqlCommand.Transaction = sqlTxn; 
25                        sqlCommand.CommandText = 
26                            @"UPDATE Blogs SET Rating = 5" + 
27                             " WHERE Name LIKE '%Entity Framework%'"; 
28                        sqlCommand.ExecuteNonQuery(); 
29  
30                        using (var context =  
31                          new BloggingContext(conn, contextOwnsConnection: false)) 
32                         { 
33                             context.Database.UseTransaction(sqlTxn); 
34  
35                             var query =  context.Posts.Where(p => p.Blog.Rating >= 5); 
36                             foreach (var post in query) 
37                             { 
38                                 post.Title += "[Cool Blog]"; 
39                             } 
40                            context.SaveChanges(); 
41                         } 
42  
43                         sqlTxn.Commit(); 
44                     } 
45                     catch (Exception) 
46                     { 
47                         sqlTxn.Rollback(); 
48                     } 
49                 } 
50             } 
51         } 
52     } 
53 }

复制代码

注意:

  • 你可以传递null到方法Database.UseTransaction()来清除EF框架对当前事务的记忆。如果你这样做,事务既不会提交也不会回滚。所以要谨慎使用之,除非你确实需要这样。
  • 如果EF框架已经持有一个事务,此时你传递一个事务,Database.UseTransaction()将抛出一个异常:

       ★ EF框架已经持有一个事务;

       ★ 当EF框架已经在一个TransactionScope中运行;

       ★ 其数据库连接对象为null (例如,无连接--通常这种情况表示事务已经完成);

       ★ 数据库连接对象与EF框架的数据库连接对象不匹配;

 对TransactionScope的一些补充

如果你使用.net framework 4.5.1及以上版本,可以使用TransactionScope的TransactionScopeAsyncFlowOption参数提供对异步的支持:

复制代码

 1 using System.Collections.Generic; 2 using System.Data.Entity; 3 using System.Data.SqlClient; 4 using System.Linq; 5 using System.Transactions; 6  7 namespace TransactionsExamples 8 { 9     class TransactionsExample 
10     { 
11         public static void AsyncTransactionScope() 
12         { 
13             using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 
14             { 
15                 using (var conn = new SqlConnection("...")) 
16                 { 
17                     await conn.OpenAsync(); 
18  
19                     var sqlCommand = new SqlCommand(); 
20                     sqlCommand.Connection = conn; 
21                     sqlCommand.CommandText = 
22                         @"UPDATE Blogs SET Rating = 5" + 
23                             " WHERE Name LIKE '%Entity Framework%'"; 
24                     await sqlCommand.ExecuteNonQueryAsync(); 
25  
26                     using (var context = new BloggingContext(conn, contextOwnsConnection: false)) 
27                     { 
28                         var query = context.Posts.Where(p => p.Blog.Rating > 5); 
29                         foreach (var post in query) 
30                         { 
31                             post.Title += "[Cool Blog]"; 
32                         } 
33  
34                         await context.SaveChangesAsync(); 
35                     } 
36                 } 
37             } 
38         } 
39     } 
40 }

复制代码

 

目前,使用TransactionScope还有一些限制:

  • 需要.NET 4.5.1及以上版本才支持异步方法;
  • 不能适用于云方案(除非你确保只有一个连接 -- 云方案不支持分布式事务);
  • 不能和Database.UseTransaction()结合使用;
  • 如果你的DDL代码存在问题(例如数据库初始化问题)或没有通过MSDTC服务来支持分布式事务,将抛出异常;

使用TransactionScope的优点:

  • 自动将本地事务升级为分布式事务:前提是你有不止一个连接到给定数据库或要组合一个连接到另一个数据库连接到同一事务(注意:你必须启动MSDTC服务以支持分布式事务)。
  • 易于编程。如果你更希望淡化对事务的关注,而非显示操作事务,使用TransactionScope将是一个更合适的选择。

 

      随着EF6提供了Database.BeginTransaction()和Database.UseTransaction() 两个API,使用TransactionScope不在是必须的了。如果你依然使用TransactionScope,就必须留意上面限制。建议你尽可能使用新的API,而非TransactionScope。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/439189.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode-769. medium】最多能完成排序的块

力扣 解题报告: 注意这种【根据一个要求,将数组分成多个区间】类模型的问题(比如汽车加油站、加法表达式求和),套路就这三步: 1、初始化 2、for循环或者while,里面三步 2.1 更新 2.2 如果符合…

SQL Server Profiler工具

一、SQL Profiler工具简介 SQL Profiler是一个图形界面和一组系统存储过程,其作用如下: 图形化监视SQL Server查询;在后台收集查询信息;分析性能;诊断像死锁之类的问题;调试T-SQL语句;模拟重放…

【LeetCode856. medium】括号的分数

856. 括号的分数 解题报告: 括号问题考虑栈。 本来是想用栈做,但是发现其实得分是相加还是乘2,和括号的层级有关系。 比如第三层的括号之间就是相加,第二层的括号就是把第三层的分数和乘2。记得算完第二层的分之后,…

SharePoint Permission中6个表的关联关系**

1、UserInfo:定义所有user,对应的类为SPUser,以site collection为单位,tp_ID字段标识user的id值。 2、Groups:定义所有组,对应的类为SPGroup,以site collection为单位,ID字段表示组…

【LeetCode2434. medium】使用机器人打印字典序最小的字符串

2434. 使用机器人打印字典序最小的字符串 解题报告: 首先进行题意转换,其实上述两种操作描述的就是一个栈。 为什么要进行题意转换呢?因为你看题目说有两种操作,但是却不问最少的操作数,说明说不定这两种操作可以合并…

天猫精灵方糖拆解报告和芯片详解

折腾: 【记录】天猫精灵方糖拆解过程 后,下面详细整理关于芯片的信息。 总体截图: 各个单元: MEDIATEK ARM MT8516AAAA 1812-BZASH BET02027 ACMQPQ8K 【整理】SoC CPU MEDIATEK MT8516详解 SAMSUNG 810 K9F1G08U0F SC…

【LeetCode 904-medium】水果成篮

1、 这题做法多种多样,看能不能找到五种做法。 其实就是这道题: 力扣 力扣题我的AC代码:(法1:边遍历边维护答案,如果遇到f[i]是第三个数就抛弃 除了f[i-1]的另一个数) class Solution { pu…

【LeetCode-940 hard】不同的子序列 II

题干: 力扣 解题报告: dp[i]代表以s[i]结尾的子序列数量。 然后再加一层循环枚举倒数第二个字符。 dp之后考虑去重(或者直接再用一个数组cnted[26]表示a~z字符结尾的子序列已经有多少个了)然后直接作差就可以,因为…

天猫方糖 篇一:新版天猫放糖改造立体声

时间线: 2018年11月23日晚,通过分析得到新版天猫精灵可能支持立体声。后来经过不懈努力下,终于找到接线方式并且当晚测试成功,这将是是民间第一个支持立体声输出的天猫方糖。 2018年11月24日下午,在众群友的指导下&am…

【LeetCode-6223 hard】317场周赛. 移除子树后的二叉树高度

力扣 题干: 解题报告: 我做麻烦了 ,其实两遍dfs就可以了,第一遍算高度,第二遍算深度,并且在第二次dfs的时候可以直接维护出答案数组:ans[i]代表删掉i为根节点的子树后树的高度。 其实主要的思…

LINQ TO SQL (一):1. 对象关系设计器(O/R 设计器)

对象关系设计器(O/R 设计器)的作用: 1. 提供了一个可视化设计图面,用于创建基于数据库中对象的 LINQ to SQL 实体类和关系;创建映射到数据库中的对象的对象模型。 2. 生成一个强类型 DataContext,用于在实体…

【LeetCode - 1235. hard】规划兼职工作

题干: 先来看简单版本: n个区间,求最大的不相交区间数 【51NOD—贪心算法专题】 D 做任务一 区间贪心最大不相交子区间数_荷叶田田_的博客-CSDN博客 如果限定这道题的报酬都是1,那么就转化成【最大不相交区间数】了。 那道题…

HTML 链接 强制打开“另存为...”弹出式文本链接打开HTML

现在的HTML5里有一个download属性&#xff0c;可以直接进行另存为&#xff0c;还可以自己重新命名文件。。。 <a href"file link" download"filename.ext" target"_blank">Click here to download</a> HTML <a> download 属性…

Office web app server2013详细的安装和部署

SharePoint 2013集成Office web apps server2013详细的安装和部署 安装前的需要理解的&#xff1a; 1、Office Web Apps 只能由使用基于声明的身份验证的 SharePoint 2013 Web 应用程序使用。Office Web Apps 呈现和编辑在使用经典模式身份验证的 SharePoint 2013 Web 应用程序…

借助office web apps实现在线预览和在线编辑

我所有的代码都是用go语言编写&#xff0c;你可以直接编译后使用&#xff0c;不用再有其他的操作。 最近项目实在太忙&#xff0c;这几天才有时间&#xff0c;这次是重头戏&#xff0c;要好好琢磨一下怎么写&#xff0c;才能更简洁的说清楚一切。 上一节&#xff0c;我们已经安…

Dbml文件提取建表TSql-CodeSmith

在昨天一个大学师弟&#xff0c;他问我能不能将LinqToSql文件转化为创建表的TSql语句&#xff0c;他是刚开始学习.NET&#xff0c;所以在网上下些示例看&#xff0c;但苦于没有数据库。所以就有了这一篇博客&#xff0c;作为我的Code生成技术的CodeSimth的最后一篇示例。在下一…

【转】新思想、新技术、新架构——更好更快的开发现代ASP.NET应用程序(续1)

上周星期天开通了博客并发布了第一篇文章《新思想、新技术、新架构——更好更快的开发现代ASP.NET应用程序》&#xff0c;汇集了一些比较流行的技术和开源项目&#xff0c;也把自己的程序架构、部分代码风格、前端表现简单做了一些展示&#xff0c;引起了近100位朋友的评论。特…

CCNA-Cisco-Packet-Tracerchs(思科官网)安装教程以及使用

Cisco-Packet-Tracerchs 思科官网模拟器&#xff0c;各大高校与中职都在使用。 但是由于支持的命令不太多&#xff0c;适用于NA阶段以及入门阶段的人群使用。 以下是免费百度网盘链接&#xff1a; https://pan.baidu.com/s/136fsYRnAfzGoj0DsQFaYTg 提取码: qa4e 复制这段内容…

CCNA-VLAN讲解与交换机三种端口模式(Acess,Trunk,Hybrid)小白入门级

VLAN讲解与三种端口模式(Acess,Trunk,Hybrid) TAG:所有文章均为原创&#xff0c;可以转载但请声明&#xff0c;是在学校里面做的&#xff0c;使用不了EVE和ENSP&#xff0c;请各路大神嘴下留情&#xff0c;如文章内容有误导请及时联系博主----来自一个17岁的中专生。 1.什么是V…

【转】2015-新思想、新技术、新架构——更好更快的开发现代ASP.NET应用程序

在博客园学习很长时间了&#xff0c;今天终于自己也开通了博客&#xff0c;准备分享一些感悟和经验。首先感谢博客园园主提供了这么好的程序员学习交流平台&#xff0c;也非常感谢张善友、dax.net、netfocus、司徒正美 等技术大牛的无私分享&#xff0c;从他们身上学到了很多。…