Linq To Sql : 三种事务处理方式
Linq To Sql : 三种事务处理方式:
Linq To SQL支持三种事务处理模型:显式本地事务、显式可分发事务、隐式事务。
1.隐式事务
当调用SubmitChanges 时,L2S会检查当前环境是否开启了事务(下面两节中创建的事务),如果没有开始事务,则 L2S启动本地事务(IDbTransaction),并使用此事务执行所生成的 SQL 命令。
使用Reflector查看DataContext.SubmitChanges的源代码(见本文最后的附录),可以看到,如果开启了一个事务,并将其传给DataContext.Transaction,则在执行SubmitChanges时就不会再重新开启一个新的事务了。 例如,下面的代码会创建应隐式事务:
public static void TestTranIn2000()
{
using (SQL2000.Sql2000DataContext context1 = new SQL2000.Sql2000DataContext())
{
List<SQL2000.TLINQ> linq = context1.TLINQ.ToList();
linq.ForEach(e => e.Value += e.Value);
context1.SubmitChanges();
}
}
可以打开SQL Server Profile来查看生成的T-SQL,生成的Update语句被包含在Begin Transaction和Commit Transaction之间,如下图所示:
-
显式本地事务
SubmitChanges只能处理最基本的CUD(Create/Update/Delete)操作;而在日常的应用中,逻辑往往没有这么简单,或者考虑性能等因素,我们需要配合ADO.Net执行原生的TSQL;这时我们可能要让ADO.Net + Linq to SQL来进行配合处理。也就是说,我们可以手工创建一个DbConnection和DbTransaction,然后传给DataContext,代码示例如下:
1: public static void TestTranInSQL2008()
2: {
3: using(SqlConnection conn = new SqlConnection(Settings.Default.AdventureWorksConnectionString))
4: {
5: conn.Open(); //1. 创建并打开DbConnection连接
6: using (SqlTransaction tran = conn.BeginTransaction()) //2. 开启DbTransaction
7: {
8: //3. 使用ADO.Net执行TSQL
9: SqlCommand cmd = new SqlCommand("Update TLinq SET Value=10", conn, tran);
10: cmd.ExecuteNonQuery();
11:
12: //4. 配合Ado.Net和Linq to Sql: 将ADO.Net的DbConnection和DbTransaction传给Linq to Sql
13: using (AdventureWorksDataContext context1 = new AdventureWorksDataContext(conn))
14: {
15: context1.Transaction = tran;
16: List<TLINQ> linq = context1.TLINQ.ToList();
17: context1.TLINQ.InsertOnSubmit(new TLINQ() { Value = "1" });
18: linq.ForEach(e => e.Value = (Convert.ToInt32(e.Value) + 1).ToString());
19: context1.SubmitChanges();
20: }
21:
22: tran.Commit(); //5. 需要提交手工创建的事务
23: }
24: }
25: }
-
显式可分发事务
使用TransactionScope来进行显示可分发事务控制。TransactionScope就像事务处理里面的一面魔镜,如果需要事务,就对着它喊:“主啊,请赐我事务!”,于是这个操作就有了事务功能。关于TransactionScope的详细介绍,可以参考MSDN:使用事务范围实现隐式事务,及SQL Server的联机丛书:CLR 集成和事务使用显式可分发事务进行处理的示例代码如下:
1: public static void TestTransactionScopeInSQL2008()
2: {
3: Action action = () => //1.把要执行的操作封装在一个或多个Action中
4: {
5: using (AdventureWorksDataContext context1 = new AdventureWorksDataContext())
6: {
7: context1.ExecuteCommand("Update TLinq SET Value={0}", 10);
8: List<TLINQ> linq = context1.TLINQ.ToList();
9: context1.TLINQ.InsertOnSubmit(new TLINQ() { Value = "1" });
10: linq.ForEach(e => e.Value = (Convert.ToInt32(e.Value) + 1).ToString());
11: context1.SubmitChanges();
12: }
13: };
14: TransactioExtension.Excute(action);
15: }
或者这样:
1: /// <summary>
2: /// 此方法里面完全不必考虑事务
3: /// </summary>
4: public static void TestTransactionScopeInSQL2008()
5: {
6: using (AdventureWorksDataContext context1 = new AdventureWorksDataContext())
7: {
8: context1.ExecuteCommand("Update TLinq SET Value={0}", 10);
9: List<TLINQ> linq = context1.TLINQ.ToList();
10: context1.TLINQ.InsertOnSubmit(new TLINQ() { Value = "1" });
11: linq.ForEach(e => e.Value = (Convert.ToInt32(e.Value) + 1).ToString());
12: context1.SubmitChanges();
13: }
14: }
15:
16: //而在外面直接这样使用
17: TransactioExtension.Excute(() => TestTransactionScopeInSQL2008());
最后来个汇总:
事务类型 | 优点 | 缺点 |
---|---|---|
隐式事务 | 使用简单,由L2S自动处理。 | 仅限于单个DataContext中的SubmitChanges方法内使用。 |
显式本地事务 | 可以配合Ado.Net一起使用,可以跨多个DataContext来使用 | 使用相对繁琐一点儿;且不支持与DataContext.ExecuteCommand配合使用 |
显式可分发事务 | 功能强大(可以配合ADO.Net或者DataContext.ExcuteCommand使用,可以跨DataContext使用,可以跨数据库使用,可以跨服务器使用),使用简单 | 可能会对性能有一些影响 |