C#:数据库事务统一管理的解决方案
c#中常规事务的操作需要每次都打开链接、开启事务、执行语句、提交或回滚事务,在业务层或者数据访问层中每次都这样写会非常繁琐,
在最近遇到一个项目中需要实现类似于EntityFrameWork或者Ibatis.net中的事务管理,既将分散的事务统一管理在一次数据访问会话中:
例如 EntityFramework中管理事务的方式:
using (MyDbContext context = new MyDbContext()) { context.Students.Add(entity1); context.Students.Add(entity2); context.SaveChanges(); }
又例如 Ibatis.net中的事务管理方式:
DaoManager.BeginTransaction(); try { ...
...
... DaoManager.CommitTransaction(); } catch (Exception ex) { DaoManager.RollBackTransaction(); }
很多orm都实现了类似的统一管理事务功能。虽然再自己写一个也是重复造轮子,但是最近项目要用的是可以直接写sql的那种, 所以就自己实现了一个简陋版
实现的思路和方式
既然要统一管理事务,那么每次会话中的连接对象、事务对象就要保持一致; 所以使用一个上下文类来管理每次会话:
DataContext类:
public partial class DataContext { //数据访问对象 private DataAccess _da; public DataContext() { _da = new DataAccess(); } //开启事务 public IDbTransaction BeginTransaction() { return _da.BeginTransaction(); }//提交事务 public void Commit() { _da.Commit(); } //回滚事务 public void RollBack() { _da.RollBack(); } }
上下文类持有一个数据访问的对象,通过这个对象来进行统一访问事务的开启、提交和回滚
DataAccess类:
public delegate void ExcuteNonQuery(string sql, object param);、
public class DataAccess { private IDbConnection conn; private IDbTransaction trans; private Dictionary<string, object> _DalDic; public DataAccess() { conn = new SqlConnection("YOUR DBCONNECTION STRING"); } public IDbTransaction BeginTransaction() { if (conn.State != ConnectionState.Open) conn.Open(); trans= conn.BeginTransaction(); return trans; } public void Commit() { if (trans != null) trans.Commit(); } public void RollBack() { if (trans != null) trans.Rollback(); } public IDal<T> GetDal<T>() where T:class { lock (this) { Type type = typeof(T); IDal<T> Dal = null; if (_DalDic == null) _DalDic = new Dictionary<string, object>(); if (_DalDic.ContainsKey(type.Name)) Dal = _DalDic[type.Name] as IDal<T>; else { Dal = DaoFactory.GetDal<T>(type); Dal.conn = conn; Dal.excuteNonQuery = ExcuteNonQuery; } return Dal; } } private void ExcuteNonQuery(string sql,object t) { conn.Execute(sql, t, trans); } }
访问类管理了链接对象和事务对象。
然后通过GetDal()方法来获取一个实际的Dao层对象,例如下面的studetnDao 、TeacherDao等等。
同时,使用一个委托ExcuteNonQuery(string sql, object param) 将实际的curd操作方法赋值给dao对象中的curd方法:Dal.excuteNonQuery = ExcuteNonQuery;
实际的curd操作也就是上面的ExcuteNonQuery方法,它使用了本对象DataAccess类中的事务对象, 这样就实现了统一事务对象。
DaoFactory类:
获取dal对象使用了简单工厂设计模式
public class DaoFactory { public static IDal<T> GetDal<T>(Type type) { IDal<T> dao = null ; if (type == new UnitInfo().GetType()) { dao = new UnitInfoDao() as IDal<T>; } else if (type == new StudentInfo().GetType()) { dao = new StudentDao() as IDal<T>; }
else if......
......
......
...... return dao; } }
然后就是dal层,里面就是最基础的CRUD:
Dal层的各个对象:
因为要操作实体,所以dal层用了泛型接口,dal层中有一个上面说的委托对象excuteNonQuery。
public interface IDal<TDao> { IDbConnection conn { get; set; } void Edit(TDao t); void Insert(TDao t); IQueryable<TDao> Query(string sql, object paramse); void Delete(TDao t); ExcuteNonQuery excuteNonQuery { get; set; } }
dal的实现类:(这里作为示例, 只实现了update方法)
public class TeacherDao : BaseDao, IDal<TeacherInfo> { public IDbConnection conn { get { return base.Conn; } set { base.Conn = value; } } public ExcuteNonQuery excuteNonQuery { get; set; } public void Delete(TeacherInfo t) { throw new NotImplementedException(); } public void Edit(TeacherInfo t) { string sql = "update TeacherInfo Set Name=@Name where ID=@ID"; if (excuteNonQuery != null) excuteNonQuery(sql, t); else { //你的基本curd实现方法 //Conn.Query<StudentInfo>(sql, t); } } public void Insert(TeacherInfo t) { throw new NotImplementedException(); } public IQueryable<TeacherInfo> Query(string sql, object paramse) { throw new NotImplementedException(); } } public class StudentDao : BaseDao, IDal<StudentInfo> { public IDbConnection conn { get { return base.Conn; } set { base.Conn = value; } } public ExcuteNonQuery excuteNonQuery { get; set; } public void Delete(StudentInfo t) { throw new NotImplementedException(); } public void Edit(StudentInfo t) { string sql = "update StudentInfo Set Name=@Name where ID=@ID"; if (excuteNonQuery != null) excuteNonQuery(sql, t); else { //你的基本crud实现方法 //Conn.Query<StudentInfo>(sql, t); } } public void Insert(StudentInfo t) { throw new NotImplementedException(); } public IQueryable<StudentInfo> Query(string sql, object paramse) { throw new NotImplementedException(); } }
这里就用到了DataAccess类中赋值给dao对象的委托类型。方式就是: 如果有委托,那就执行委托的方法(带有事务的方法),否则的话,就是执行上面注释掉的自己实现的普通的不带事务的crud方法。
DataContext分布类中dao对象的获取:
刚才的DataContext类是分部类, 因为这里面还要加上获取各个dao对象的get属性:例如这里加上了获取StudentDao对象和TeacherDao对象:
public partial class DataContext { public IDal<TeacherInfo> TeacherInfoDao { get { return _da.GetDal<TeacherInfo>(); } } public IDal<StudentInfo> StudentInfoDao { get { return _da.GetDal<StudentInfo>(); } } }
至此的话事务统一管理已经实现了,看一下业务层中的使用:
调用:
public void Test1() { var context = new DataContext(); context.BeginTransaction(); try { context.StudentInfoDao.Edit(new StudentInfo() { Name = "张三", ID = "123" }); context.TeacherInfoDao.Edit(new TeacherInfo() { Name = "陈老师", ID = "234" }); context.Commit(); } catch { context.RollBack(); } }
整个实现的总体思路,就是通过上下文和访问类统一管理dao对象, 使得dao对象如果有带事务的操作方法,那么就执行这个方法; 否则dao对象就执行普通的不带事务的方法。