Linq to Sql : 三种事务处理方式
时间:2022-03-13 22:54
原文:
Linq to SQL支持三种事务处理模型:显式本地事务、显式可分发事务、隐式事务。(from MSDN: )。MSDN中描述得相对比较粗狂,下面就结合实例来对此进行阐述。
0. 测试环境
OS | Windows Server 2008 Enterprise + sp1 |
IDE | Visual Studio 2008, .net framework 3.5 + SP1 |
DB | SQL Server 2000 + sp4 SQL Server 2008 Enterprise Edition |
1. 隐式事务
当调用SubmitChanges 时,L2S会检查当前环境是否开启了事务(下面两节中创建的事务),如果没有开始事务,则 L2S启动本地事务(IDbTransaction),并使用此事务执行所生成的 SQL 命令。
使用Reflector查看DataContext.SubmitChanges的源代码(见本文最后的附录),可以看到,如果开启了一个事务,并将其传给DataContext.Transaction,则在执行SubmitChanges时就不会再重新开启一个新的事务了。
例如,下面的代码会创建应隐式事务:
当您进行此调用时,DataContext 会设法将您所做的更改转换为等效的 SQL 命令。您可以使用自己的自定义逻辑来重写这些操作,但提交顺序是由 DataContext 的一项称作“更改处理器”的服务来协调的。事件的顺序如下:
-
当您调用 SubmitChanges 时,LINQ to SQL 会检查已知对象的集合以确定新实例是否已附加到它们。如果已附加,这些新实例将添加到被跟踪对象的集合。
-
所有具有挂起更改的对象将按照它们之间的依赖关系排序成一个对象序列。如果一个对象的更改依赖于其他对象,则这个对象将排在其依赖项之后。
-
在即将传输任何实际更改时,LINQ to SQL 会启动一个事务来封装由各条命令组成的系列。
-
对对象的更改会逐个转换为 SQL 命令,然后发送到服务器。
此时,如果数据库检测到任何错误,都会造成提交进程停止并引发异常。将回滚对数据库的所有更改,就像未进行过提交一样。DataContext 仍具有所有更改的完整记录。
因此,这里还是打开SQL Server Profile来确认:
OK,现在可以放心了,这里的确是先执行Insert,再执行Update;执行Update时出现了SqlException异常。这时查看测试表TLINQ中的数据,发现没有插入新的记录进去。也就是说,这里使用了事务,但是SQL Server Profile没有跟踪到。
至于为啥会这样,我暂时也没有搞清楚;还有就是下面一节中即使执行DbConnection.BeginTransaction(),也不会跟踪到begin tran和commit tran。不知道是不是SQL Server 2008里面升级了啥。哪位如果知道原因,麻烦告知我一声,谢谢。
最后总结一下隐式事务的优缺点:
优点:使用简单,L2S都帮我们搞定了,我们不需要写任何代码。
缺点:只能处理单个DataContext中的单个SubmitChanges()函数的调用。如果需要将SubmitChanges()与其他自定义更新操作(譬如ExcuteCommand)共用一个Transaction、或者与其他DataContext共用一个DBTransation,就没辙了.......
2. 显式本地事务
SubmitChanges只能处理最基本的CUD(Create/Update/Delete)操作;而在日常的应用中,逻辑往往没有这么简单,或者考虑性能等因素,我们需要配合ADO.Net执行原生的TSQL;这时我们可能要让ADO.Net + Linq to SQL来进行配合处理。
也就是说,我们可以手工创建一个DbConnection和DbTransaction,然后传给DataContext,代码示例如下:
测试环境 是否需要开启DTC 创建出来的事务类型 Linq to Sql + Sql Server 2000(单一数据库) 需要 分布式事务 Linq to Sql + Sql Server 2008(单一数据库) 不需要 轻型本地事务 Linq to Sql + Sql Server 2008(跨多个数据库) 需要 访问第一个数据库时,会创建轻型本地事务;当继续访问更多的数据库时,会将事务升级为完全可分发的分布式事务 最后总结一下使用显式可分发事务的优缺点:
优点:使用简单,可以配合ADO.Net或者DataContext.ExcuteCommand使用,可以跨DataContext使用,可以跨数据库使用,可以跨服务器使用。
缺点:分布式事务通常会使用大量的系统资源。Microsoft 分布式事务处理协调器 (MS DTC) 会管理此类事务,并集成在这些事务中访问的所有资源管理器。庆幸的是:在打开一个具有活动TransactionScope事务的连接而未打开任何其他连接的情况下,该事务会以轻型事务的形式提交,而不是产生完全分布式事务的额外开销。
最后来个汇总:
事务类型 | 优点 | 缺点 |
隐式事务 | 使用简单,由L2S自动处理。 | 仅限于单个DataContext中的SubmitChanges方法内使用。 |
显式本地事务 | 可以配合Ado.Net一起使用,可以跨多个DataContext来使用 | 使用相对繁琐一点儿;且不支持与DataContext.ExecuteCommand配合使用 |
显式可分发事务 | 功能强大(可以配合ADO.Net或者DataContext.ExcuteCommand使用,可以跨DataContext使用,可以跨数据库使用,可以跨服务器使用),使用简单 | 可能会对性能有一些影响(我暂时也没有测试过-,-) |
附:用Reflector查看DataContext.SubmitChanges的源代码如下:
1: public virtual void SubmitChanges(ConflictMode failureMode)
2: {
3: this.CheckDispose();
4: this.CheckNotInSubmitChanges();
5: this.VerifyTrackingEnabled();
6: this.conflicts.Clear();
7: try
8: {
9: this.isInSubmitChanges = true;
10: if ((Transaction.Current == null) && (this.provider.Transaction == null)) //如果不在事务环境内
11: {
12: bool flag = false;
13: DbTransaction transaction = null;
14: try
15: {
16: if (this.provider.Connection.State == ConnectionState.Open)
17: {
18: this.provider.ClearConnection();
19: }
20: if (this.provider.Connection.State == ConnectionState.Closed)
21: {
22: this.provider.Connection.Open();
23: flag = true;
24: }
25: transaction = this.provider.Connection.BeginTransaction(IsolationLevel.ReadCommitted); //开启事务
26: this.provider.Transaction = transaction;
27: new ChangeProcessor(this.services, this).SubmitChanges(failureMode);
28: this.AcceptChanges();
29: this.provider.ClearConnection();
30: transaction.Commit();
31: return;
32: }
33: catch
34: {
35: if (transaction != null)
36: {
37: try
38: {
39: transaction.Rollback();
40: }
41: catch
42: {
43: }
44: }
45: throw;
46: }
47: finally
48: {
49: this.provider.Transaction = null;
50: if (flag)
51: {
52: this.provider.Connection.Close();
53: }
54: }
55: }
56: new ChangeProcessor(this.services, this).SubmitChanges(failureMode);
57: this.AcceptChanges();
58: }
59: finally
60: {
61: this.isInSubmitChanges = false;
62: }
63: }
Linq to Sql : 三种事务处理方式,布布扣,bubuko.com