在数据库工具类编写的过程中,对事务的处理操作想避免各个原子操作的事务对象赋值重复操作,想对外暴露的方法为如下形式

   public bool ExecuteTransition(Action TransitionAction, out string ExceptionStr)

外部传入的数据库操作都使用委托统一打包,内部进行事务操作。我们首先需要明白的是,数据库事务操作在ADO.NET的编码中的体现是,DbConnection为同一个,DbCommand的Transaction为同一个。

首先我们需要识每一个数据库操作的上下文,是否在TransitionAction这个委托中,为了简单明了,在执行TransitionAction时开启一个Task,取得当前线程的ThreadID作为这个事务委托的唯一标识,并生成一个DbTransaction放入一个TransactionDic中,在SqlHelper执行类中执行SQL语句创建Connection时,取得当前的ThreadID去TransactionDic中查找,如果有对应的Transition则说明该SQL语句的执行是在一个事务中,Connection直接取Transition的数据库连接,并给DbCommand的Transition对象赋值

这个解决方案对于TransitionAction中执行方法类中的数据库操作或其他组合操作也是可行的,但是对于嵌套事务还需要进一步改进。

比如我封装好一个框架的工作流方法MethodA,自带事物执行,但是需要与业务更新方法MethodB进行事物组合操作,上述方案并不能满足要求,需要我们进行改进,判断当前的事物TransitionAction是否是嵌套事务,即TransitionActionB实际是打包在TransitionActionA中的。在TransitionAction的DbTransaction添加的过程中,我们需要取到Task之外的ThreadID,这里称作为RootThreadID,同时维护一个ConcurrentDictionary<string, List<string>> TransitionIDMapDic,用于维护RootThreadID与嵌套事务的ThreadID的关系,在创建Task时就可以判断当前的ThreadID是否在TransactionDic,存在就是嵌套事务,需要将当前的TransitionAction合并到Root事物中

TransactionManage 代码

 public class TransactionManage
{
private static ConcurrentDictionary<string, LocalTransaction> TransactionDic = new ConcurrentDictionary<string, LocalTransaction>();
private static ConcurrentDictionary<string, List<string>> TransitionIDMapDic = new ConcurrentDictionary<string, List<string>>();
public static void AddTransition(string TransitionID,string RootThreadID, DbTransaction Transition,Action TransitionAction)
{
LocalTransaction LT = new LocalTransaction();
LT.RootThreadID = RootThreadID;
LT.TransitionID = TransitionID;
LT.Transition = Transition;
//执行列表增加Action
LT.AddTransitionAction(TransitionAction);
TransactionDic.TryAdd(TransitionID, LT);
//增加事务根线程ID与嵌套事务相关事务ID
TransitionIDMapDic.TryAdd(RootThreadID, new List<string>() { TransitionID }); } public static void ContactTransition(string TransitionID, string RootThreadID,Action TransitionAction)
{
LocalTransaction LT = TransactionDic[RootThreadID];
if (!TransactionDic.ContainsKey(LT.RootThreadID))
{
LT.TransitionID = TransitionID;
//执行列表增加Action
LT.AddTransitionAction(TransitionAction);
TransactionDic.TryAdd(TransitionID, LT);
//增加事务根线程ID与嵌套事务相关事务ID
List<string> TransitionIDS = TransitionIDMapDic[LT.RootThreadID];
TransitionIDS.Add(TransitionID);
TransitionIDMapDic[LT.RootThreadID] = TransitionIDS;
}
else
{
ContactTransition(TransitionID, LT.RootThreadID, TransitionAction);
}
} public static string GetRootID(string TransitionID)
{
LocalTransaction LT = TransactionDic[TransitionID];
if (!TransactionDic.ContainsKey(LT.RootThreadID))
{
return LT.RootThreadID;
}
else
{
return GetRootID(LT.RootThreadID);
}
} public static LocalTransaction GetTransition(string TransitionID)
{
LocalTransaction LT = null;
TransactionDic.TryGetValue(TransitionID, out LT);
return LT;
}
public static bool ContainsTransition(string TransitionID)
{
return TransactionDic.ContainsKey(TransitionID);
}
public static void RemoveTransition(string TransitionID)
{
string RootID = GetRootID(TransitionID);
List<string> TransitionIDList = null;
TransitionIDMapDic.TryRemove(RootID, out TransitionIDList);
foreach (string TransitionIDItem in TransitionIDList)
{
LocalTransaction LT = null;
TransactionDic.TryRemove(TransitionIDItem, out LT);
}
}

对外事物执行方法

   public bool ExecuteTransition(Action TransitionAction, out string ExceptionStr)
{
bool IsSuccess = true;
ExceptionStr = string.Empty;
string RootThreadID = Thread.CurrentThread.ManagedThreadId.ToString();
var TrabsitionTask = new Task<LocalTransactionResult>(() =>
{
string TransitionID = Thread.CurrentThread.ManagedThreadId.ToString();
LocalTransactionResult Result = new LocalTransactionResult();
if (!TransactionManage.ContainsTransition(RootThreadID))
{
using (DbConnection connection = DBExecute.CreateConnection(ConnectionString))
{
connection.Open();
DbTransaction Transaction = connection.BeginTransaction();
TransactionManage.AddTransition(TransitionID, RootThreadID, Transaction, TransitionAction);
try
{
TransactionManage.GetTransition(TransitionID).Execute();
Transaction.Commit();
}
catch (System.Exception e)
{
Result.ExecuteStatus = false;
Result.ExceptionMessage = e.Message;
Transaction.Rollback();
}
finally
{
Transaction.Dispose();
connection.Close();
connection.Dispose();
Transaction = null;
TransactionManage.RemoveTransition(TransitionID);
}
return Result;
}
}
else
{
//当前是嵌套事务,不执行,由根事务统一执行
TransactionManage.ContactTransition(TransitionID, RootThreadID, TransitionAction);
Result.ExecuteStatus = true;
Result.ExceptionMessage = string.Empty;
return Result;
} });
TrabsitionTask.Start();
TrabsitionTask.Wait();
IsSuccess = TrabsitionTask.Result.ExecuteStatus;
ExceptionStr = TrabsitionTask.Result.ExceptionMessage;
return IsSuccess; }

完整模块代码地址:https://gitee.com/grassprogramming/FastExecutorCore/tree/master/code/FastCore/FastCore/FastORM

注:个人感觉使用线程的方式虽然很方便但是实际使用过程中多线程可能会出现问题,后续会对执行类进行上下文对象的绑定改造

ADO.NET事务封装的更多相关文章

  1. OracleHelper(对增删改查分页查询操作进行了面向对象的封装,对批量增删改操作的事务封装)

    公司的一个新项目使用ASP.NET MVC开发,经理让我写个OracleHelper,我从网上找了一个比较全的OracleHelper类,缺点是查询的时候返回DataSet,数据增删改要写很多代码(当 ...

  2. 把事务封装成类似Serializable用法的特性

    把事务封装成类似Serializable用法的特性 最近几天上班没事可做就想着整理常用的类库方法,验证.消息.分页.模版引擎.数据库操作.ini操作.文本操作.xml操作等,最后就是现在这个事务特性. ...

  3. ADO.NET事务

    在发布System.Transaction命名空间之前,可以直接用ADO.NET创建事务,也可以通过组件.特性和COM+运行库(位于System.EnterpriseServices命名空间中)进行事 ...

  4. Dapper的封装、二次封装、官方扩展包封装,以及ADO.NET原生封装

    前几天偶然看到了dapper,由于以前没有用过,只用过ef core,稍微看了一下,然后写了一些简单的可复用的封装. Dapper的用法比较接近ADO.NET所以性能也是比较快.所以我们先来看看使用A ...

  5. SQL Server 2008 R2——VC++ ADO 操作 事务

    ==================================声明================================== 本文原创,转载在正文中显要的注明作者和出处,并保证文章的完 ...

  6. ADO.NET 事务控制

    在ADO.NET 中,可以使用Connection 和Transaction 对象来控制事务.若要执行事务,请执行下列操作: 1.调用Connection 对象的BeginTransaction 方法 ...

  7. EF事务封装

    public class EFTransaction:ITransaction { DbContextTransaction originalTransaction = null; MyDbConte ...

  8. ADO执行事务

    在工作中遇到,需要批量提交的.在sql2008以后有表变量定义,可以实现.但个人比较习惯用C#,就有下面代码,直接上代码... using (SqlConnection conn = new SqlC ...

  9. ADO 事务

    Ado.Net事务处理.在ADO.NET 中,可以使用Connection 和Transaction 对象来控制事务.若要执行事务,请执行下列操作:• 调用Connection 对象的BeginTra ...

随机推荐

  1. (转)Gamma分布,Beta分布,Multinomial多项式分布,Dirichlet狄利克雷分布

    1. Gamma函数 首先我们可以看一下Gamma函数的定义: Gamma的重要性质包括下面几条: 1. 递推公式: 2. 对于正整数n, 有 因此可以说Gamma函数是阶乘的推广. 3.  4.  ...

  2. 使用c++标准IO库实现txt文本文件的读与写操作

    练习c++primer中关于输入输出流的操作. 任务是从固定格式的forreading.txt文档中读取相应的数据,转存到forwriting.txt中去. forreading.txt 格式如下: ...

  3. Spring初识、新建工程

    1.spring与三层架构的关系: spring负责管理项目中的所有对象,是一个一站式的框架,容器中的对象决定了spring的功能. 2.spring核心架构 Spring框架主要由六个模块组成,在开 ...

  4. Hadoop-3.1.2安装步骤

    Hadoop-3.1.2 安装步骤 第一步  准备 服务器配置 1.  在VMware中安装把Centos7安装成功后,需要把界面设置为命令行启动,因为默认的启动方式是图形界面启动 systemctl ...

  5. C# 正则进阶

    .NET 中的正则表达式是基于 Perl 5 的正则表达式. 超时 从 .NET Framework 4.5 开始,正则表达式支持在匹配操作中指定超时时间.如果匹配超时,就会抛出 RegexMatch ...

  6. C++读取MNIST数据集

    MNIST是一个标准的手写字符测试集. Mnist数据集对应四个文件: train-images-idx3-ubyte: training set images  train-labels-idx1- ...

  7. 关于PreparedStatement.addBatch()方法

    Statement和PreparedStatement的区别就不多废话了,直接说PreparedStatement最重要的addbatch()结构的使用. 1.建立链接,(打电话拨号 ) Connec ...

  8. CMake中的两种变量(Variable types in CMake)

    在CMake中存在两种变量:normal variables and cache varialbes .正常变量就像是脚本内部变量,相当于程序设计中定义的局部变量那样.而CMakeLists.txt相 ...

  9. SpringBoot之ApplicationRunner接口和@Order注解

    我们在开发中可能会有这样的情景.需要在容器启动的时候执行一些内容.比如读取配置文件,数据库连接之类的.SpringBoot给我们提供了ApplicationRunner接口来帮助我们实现这种需求.该接 ...

  10. vue的组件传值

    1.父组件向子组件传值 父组件: 123456789101112 <template> <child :name="name"></child> ...