zoukankan      html  css  js  c++  java
  • 如何在项目中应用Lin“.NET研究”qToSql数据库事务 狼人:

      本文主要涉及LinqToSql数据库事务相关,文章不足之处,欢迎您指出。

      一、回顾T-SQL中的事务机制

      代码如下:

    1 /*加入事务机制后的存储过程*/
    2  create procedure sp_example
    3 @param1 int = null,
    4 @param2 nvarchar(20) = null
    5  as
    6 begin tran tranName /*sql 事务的加入*/
    7 insert into table0 (col1,col2,col3) values ('value1','value2','value3')
    8 update table1 set column1 = @param1 where 1=1
    9 --删除table2中一条已经被其他外键表引用的记录,此时会报sql引用错误
    10   delete from table2 where column3 = @param1
    11 insert into table3 (col1,col2) values ('value1','value2')
    12 if(@@error =0)
    13 commit tran tranName
    14 else
    15 rollback tran tranName
    16  go

      以上代码是一个具备事务机制的简单存储过程,需要指出的是当上述代码执行到第十行时,此时如果该存储过程未加入事务机制那么势必会导致第10行之前已经被影响的数据库记录也不会被还原(rollback)。这样的代码是我们不想见到的,所以事务在复杂的商业逻辑中保持数据的完整性还是尤为重要的。

      二、LinqToSql 中的SubmitChanges内置事务机制

      众所周知LinqToSql 中我们的事务机制代码变的相对简单了,如以下代码:

    1 public bool DeleteDepartment(int departmentId)
    2 {
    3 try
    4 {
    5 DataContext.SystemUser.DeleteOnSubmit(
    6 DataContext.SystemUser.FirstOrDefault(u => u.DepartmentID == departmentId));
    7
    8 DataContext.Department.DeleteOnSubmit(
    9 DataContext.Department.FirstOrDefault(f => f.DepartmentID == departmentId));
    10
    11 //事务机制被封装到SubmitChanges方法内
    12   DataContext.SubmitChanges();
    13
    14 return true;
    15 }
    16 catch
    17 {
    18 return false;
    19 }
    20 }

      上述代码很容易理解,在LinqToSql 为了删除一条部门记录。我们首选要删除该部门被引用的外键表记录这里是员工表,(以上代码只是为举例用,实际开发中是不会有此种业务的)当外键记录都删除成功后代码执行到第8行,这时才能能删除部门对象。否则报SqlException外键引用无法删除部门记录。我们唯一需要做的只是将 DataContext.SubmitChanges();这句放在所有Linq操作数据库语句之后这样就可以调用数据库事务机制了。比如当第5行代码执行时SystemUser还被Order表引用。当SubmitChanges执行时会自动调用transaction.Rollback()方法回滚SubmitChanges()之前的所有被影响的数据库记录,详情请阅Reflector。

      三、在LinqToSql中SubmitChange内置事务机制无法满足的业务场景

      当程序需要处理更多更复杂的商业逻辑时,我发现光凭SubmitChange方法自带的事务机制是远远不能满足的。

      该场景描述如下:

      如果为完成某一个特定的业务,需要在程序中使用多次的SubmitChanges方法。比如我们要做一个库存相关业务,该业务是由两张表组成:主表+从表。分别为主表:Depot和从表:DepotDetail 两张表。两张表关系如下:

      当我们通过LinqToSql生成一个库存对象时其实应先生成Depot对象后再将生成Depot对象的DepotID(主键)传递到DepotDetail对象中用于生成库存明细表记录。也就说为了生成库存明细表记录我们必须先生成Depot主表,那样就不得不先调用SubmitChanges方法,当保存DepotDetail对象时还需要再一次调用SubmitChanges()方法。因为调用了多次SubmitChanges方法所以SubmitChanges内置的回滚机制已经不能满足需要了。

      四、TransactionScope的应用

       我们需要引用.net 的System.Transactions 类库使用TransactionScope类,帮我们更有效的处理数据库事务机制。对TransactionScope进行封装,代码如下:

      本段代码原作者,被我稍稍改造如下: 

    1 public static class DBTransactionExtension
    2 {
    3 public static bool Excute(out string errorMsg, params Action[] actions)
    4 {
    5 //使用ReadCommitted隔离级别,保持与Sql Server的默认隔离级别一致
    6 return Excute(out errorMsg, IsolationLevel.ReadCommitted, null, actions);
    7
    8 }
    9
    10 public static void Excute(out string errorMsg, IsolationLevel level, params Action[] actions)
    11 {
    12 Excute(out errorMsg, level, null, actions);
    13 }
    上海闵行企业网站设计与制作pan>14
    15 public static void Excute(out string errorMsg, int timeOut, params Action[] actions)
    16 {
    17 Excute(out errorMsg, IsolationLevel.ReadCommitted, timeOut, actions);
    18 }
    19
    20 public static bool Excute(out string errorMsg, IsolationLevel level, int? timeOut, params Action[] actions)
    21 {
    22 errorMsg = "";
    23 if (actions == null || actions.Length == 0)
    24 return false;
    25 TransactionOptions options = new TransactionOptions();
    26
    27 options.IsolationLevel = level; //默认为Serializable,这里根据参数来进行调整
    28
    29 if (timeOut.HasValue)
    30
    31 options.Timeout = new TimeSpan(0, 0, timeOut.Value); //默认60秒
    32
    33 using (TransactionScope tran = n上海网站建设ew TransactionScope(TransactionScopeOption.Required, options))
    34 {
    35 try
    36 {
    37 Array.ForEach<Action>(actions, action => action());
    38 tran.Complete(); //通知事务管理器它可以提交事务
    39 return true;
    40 }
    41 catch (Exception ex)//回滚事务
    42 {
    43 errorMsg = ex.Message;
    44 return false;
    45 }
    46 }
    47 }
    48
    49 }

      调用DBTransactionExtension代码如下:

    1 private void SaveDepot(Depot depot)
    2 {
    3 DataContext.Depots.InsertOnSubmit(depot);
    4
    5 if (false)//TODO:保存库存主表前的逻辑判断,条件不满足时候调用 throw new exception执行TransactionScope回滚。
    6   throw new Exception("自定义错误提示内容,最终由事务获取错误信息后抛给UI");
    7
    8 //条件满足则调用SubmitChanges
    9   DataContext.SubmitChanges();
    10 DepotDetail depotDetail = new DepotDetail();
    11 depotDetail.DepotID = depot.DepotID;
    12 depotDetail.Count = <上海徐汇企业网站设计与制作/span>100;
    13
    14 DataContext.DepotDetails.InsertOnSubmit(depotDetail);
    15 //又调用了一次SubmitChanges
    16   DataContext.SubmitChanges();
    17 }
    18 public Depot InvokeTransaction(Depot depot, out string errorMsg)
    19 {
    20 try
    21 {
    <上海闵行企业网站制作span style="color: #008080;user-select:none;">22
    DBTransactionExtension.Excute(out errorMsg, () => SaveDepot(depot));
    23 return depot;
    24 }
    25 catch (Exception ex)
    26 {
    27 errorMsg = ex.Message;
    28 return null;
    29 }
    30 }

      根据上述调用方法,我们已经可以在LinqToSql中灵活的使用数据库事务了。

      五、TransactionScope类使用的注意事项

      使用TransactionScope时如果调用多次LinqToSql的DataContext对象实例(等同调用多个数据库连接),那么我们必须开启MSDTC否则事务不能正常工作,具体请阅MSDTC开启。注:TransactionScope 适用于多种 Data Provider 比如 oracle 、OleDB、ODBC等。

      最后希望本篇文章能给您带来帮助。

    声明:此博有部分内容为转载,版权归原作者所有~
  • 相关阅读:
    0309. Best Time to Buy and Sell Stock with Cooldown (M)
    0621. Task Scheduler (M)
    0106. Construct Binary Tree from Inorder and Postorder Traversal (M)
    0258. Add Digits (E)
    0154. Find Minimum in Rotated Sorted Array II (H)
    0797. All Paths From Source to Target (M)
    0260. Single Number III (M)
    0072. Edit Distance (H)
    0103. Binary Tree Zigzag Level Order Traversal (M)
    0312. Burst Balloons (H)
  • 原文地址:https://www.cnblogs.com/waw/p/2217014.html
Copyright © 2011-2022 走看看