ADO.NET事务封装
在数据库工具类编写的过程中,对事务的处理操作想避免各个原子操作的事务对象赋值重复操作,想对外暴露的方法为如下形式
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事务封装的更多相关文章
- OracleHelper(对增删改查分页查询操作进行了面向对象的封装,对批量增删改操作的事务封装)
公司的一个新项目使用ASP.NET MVC开发,经理让我写个OracleHelper,我从网上找了一个比较全的OracleHelper类,缺点是查询的时候返回DataSet,数据增删改要写很多代码(当 ...
- 把事务封装成类似Serializable用法的特性
把事务封装成类似Serializable用法的特性 最近几天上班没事可做就想着整理常用的类库方法,验证.消息.分页.模版引擎.数据库操作.ini操作.文本操作.xml操作等,最后就是现在这个事务特性. ...
- ADO.NET事务
在发布System.Transaction命名空间之前,可以直接用ADO.NET创建事务,也可以通过组件.特性和COM+运行库(位于System.EnterpriseServices命名空间中)进行事 ...
- Dapper的封装、二次封装、官方扩展包封装,以及ADO.NET原生封装
前几天偶然看到了dapper,由于以前没有用过,只用过ef core,稍微看了一下,然后写了一些简单的可复用的封装. Dapper的用法比较接近ADO.NET所以性能也是比较快.所以我们先来看看使用A ...
- SQL Server 2008 R2——VC++ ADO 操作 事务
==================================声明================================== 本文原创,转载在正文中显要的注明作者和出处,并保证文章的完 ...
- ADO.NET 事务控制
在ADO.NET 中,可以使用Connection 和Transaction 对象来控制事务.若要执行事务,请执行下列操作: 1.调用Connection 对象的BeginTransaction 方法 ...
- EF事务封装
public class EFTransaction:ITransaction { DbContextTransaction originalTransaction = null; MyDbConte ...
- ADO执行事务
在工作中遇到,需要批量提交的.在sql2008以后有表变量定义,可以实现.但个人比较习惯用C#,就有下面代码,直接上代码... using (SqlConnection conn = new SqlC ...
- ADO 事务
Ado.Net事务处理.在ADO.NET 中,可以使用Connection 和Transaction 对象来控制事务.若要执行事务,请执行下列操作:• 调用Connection 对象的BeginTra ...
随机推荐
- JVM性能分析 | 一次生产系统Full GC问题分析与排查总结
一次生产系统Full GC问题分析与排查总结 背景 最近某线上业务系统生产环境频频CPU使用率过低,频繁告警,通过重启可以缓解,但是过了一段时间又会继续预警,线上两个服务节点相继出现CPU资源紧张,导 ...
- python3基础
1.print()函数 支持自动换行: 不自动换行:使用 end=“” print格式化输出: 字符串格式化符号: • %c 格式化字符及其ASCII码• %s 格式化字符串• %d 格式化整数• ...
- vuex之state(一)
我的理解 个人认为,不用说得太过深奥,vuex其实就是把一个应用的某些数据统一管理起来,以便其他的组件更方便的操作该数据. 为什么使用vuex 当项目结构越来越复杂,组件的多层嵌套使得数据传递非常繁琐 ...
- 代理IP批量验证程序
#include <afxinet.h> #include <afxwin.h> #include <iostream> #include <fstream& ...
- Asp.Net Core Identity 骚断腿的究极魔改实体类
前言 默认的 Identity 实体类型在大多数时候已经基本够用,很多时候也只是稍微在 IdentityUser 类中增加一些自定义数据字段,比如头像.这次,我要向园友隆重介绍我魔改之后的 Ident ...
- 【译文连载】 理解Istio服务网格(第二章 安装)
全书目录 第一章 概述 本文目录 1.命令行工具安装 2. Kubernetes/OpenShift安装 3. Istio安装 4.示例Java微服务安装 4.1 源码概览 4.2 编译和部署cust ...
- Scala 学习(10)之「集合 」
数组 定长数组 Array:采用()访问,而不是[],下标从 0 开始. val array1 = new Array[String](5) //创建数组 println(array1) //返回数组 ...
- ElasticSearch基础入门学习笔记
前言 本笔记的内容主要是在从0开始学习ElasticSearch中,按照官方文档以及自己的一些测试的过程. 安装 由于是初学者,按照官方文档安装即可.前面ELK入门使用主要就是讲述了安装过程,这里不再 ...
- Mysql 字符问题
先看一下mysql支持的字符范围 *数值类型:1.整形: 类型 大小 范围 ...
- ts的特殊数据类型
四. Ts数据类型 tuple(元组类型):可以给数组指定位置存指定类型数据 例:let arr:[number, string] = [123, ‘123’]; enum(枚举):将数字转化为标识符 ...