利用AOP实现SqlSugar自动事务
本文实例为大家分享了如何利用AOP实现SqlSugar自动事务,供大家参考,具体内容如下
先看一下效果,带接口层的三层架构:
BL层:
publicclassStudentBL:IStudentService { privateILoggermLogger; privatereadonlyIStudentDAmStudentDa; privatereadonlyIValueServicemValueService; publicStudentService(IStudentDAstudentDa,IValueServicevalueService) { mLogger=LogManager.GetCurrentClassLogger(); mStudentDa=studentDa; mValueService=valueService; } [TransactionCallHandler] publicIListGetStudentList(HashtableparamsHash) { varlist=mStudentDa.GetStudents(paramsHash); varvalue=mValueService.FindAll(); returnlist; } }
假设GetStudentList方法里的mStudentDa.GetStudents和mValueService.FindAll不是查询操作,而是更新操作,当一个失败另一个需要回滚,就需要在同一个事务里,当一个出现异常就要回滚事务。
特性TransactionCallHandler就表明当前方法需要开启事务,并且当出现异常的时候回滚事务,方法执行完后提交事务。
DA层:
publicclassStudentDA:IStudentDA { privateSqlSugarClientdb; publicStudentDA() { db=SugarManager.GetInstance().SqlSugarClient; } publicIListGetStudents(HashtableparamsHash) { returndb.Queryable ().AS("T_Student").With(SqlWith.NoLock).ToList(); } }
对SqlSugar做一下包装
publicclassSugarManager { privatestaticConcurrentDictionary_cache= newConcurrentDictionary (); privatestaticThreadLocal _threadLocal; privatestaticreadonlystring_connStr=@"DataSource=localhost;port=3306;InitialCatalog=thy;userid=root;password=xxxxxx;Charset=utf8"; staticSugarManager() { _threadLocal=newThreadLocal (); } privatestaticSqlSugarClientCreatInstance() { SqlSugarClientclient=newSqlSugarClient(newConnectionConfig() { ConnectionString=_connStr,//必填 DbType=DbType.MySql,//必填 IsAutoCloseConnection=true,//默认false InitKeyType=InitKeyType.SystemTable }); varkey=Guid.NewGuid().ToString().Replace("-",""); if(!_cache.ContainsKey(key)) { _cache.TryAdd(key,newSqlClient(client)); _threadLocal.Value=key; returnclient; } thrownewException("创建SqlSugarClient失败"); } publicstaticSqlClientGetInstance() { varid=_threadLocal.Value; if(string.IsNullOrEmpty(id)||!_cache.ContainsKey(id)) returnnewSqlClient(CreatInstance()); return_cache[id]; } publicstaticvoidRelease() { try { varid=GetId(); if(!_cache.ContainsKey(id)) return; Remove(id); } catch(Exceptione) { throwe; } } privatestaticboolRemove(stringid) { if(!_cache.ContainsKey(id))returnfalse; SqlClientclient; intindex=0; boolresult=false; while(!(result=_cache.TryRemove(id,outclient))) { index++; Thread.Sleep(20); if(index>3)break; } returnresult; } privatestaticstringGetId() { varid=_threadLocal.Value; if(string.IsNullOrEmpty(id)) { thrownewException("内部错误:SqlSugarClient已丢失."); } returnid; } publicstaticvoidBeginTran() { varinstance=GetInstance(); //开启事务 if(!instance.IsBeginTran) { instance.SqlSugarClient.Ado.BeginTran(); instance.IsBeginTran=true; } } publicstaticvoidCommitTran() { varid=GetId(); if(!_cache.ContainsKey(id)) thrownewException("内部错误:SqlSugarClient已丢失."); if(_cache[id].TranCount==0) { _cache[id].SqlSugarClient.Ado.CommitTran(); _cache[id].IsBeginTran=false; } } publicstaticvoidRollbackTran() { varid=GetId(); if(!_cache.ContainsKey(id)) thrownewException("内部错误:SqlSugarClient已丢失."); _cache[id].SqlSugarClient.Ado.RollbackTran(); _cache[id].IsBeginTran=false; _cache[id].TranCount=0; } publicstaticvoidTranCountAddOne() { varid=GetId(); if(!_cache.ContainsKey(id)) thrownewException("内部错误:SqlSugarClient已丢失."); _cache[id].TranCount++; } publicstaticvoidTranCountMunisOne() { varid=GetId(); if(!_cache.ContainsKey(id)) thrownewException("内部错误:SqlSugarClient已丢失."); _cache[id].TranCount--; } }
_cache保存SqlSugar实例,_threadLocal确保同一线程下取出的是同一个SqlSugar实例。
不知道SqlSugar判断当前实例是否已经开启事务,所以又将SqlSugar包了一层。
publicclassSqlClient { publicSqlSugarClientSqlSugarClient; publicboolIsBeginTran=false; publicintTranCount=0; publicSqlClient(SqlSugarClientsqlSugarClient) { this.SqlSugarClient=sqlSugarClient; } }
IsBeginTran标识当前SqlSugar实例是否已经开启事务,TranCount是一个避免事务嵌套的计数器。
一开始的例子
[TransactionCallHandler] publicIListGetStudentList(HashtableparamsHash) { varlist=mStudentDa.GetStudents(paramsHash); varvalue=mValueService.FindAll(); returnlist; }
TransactionCallHandler表明该方法要开启事务,但是如果mValueService.FindAll也标识了TransactionCallHandler,又要开启一次事务?所以用TranCount做一个计数。
使用Castle.DynamicProxy
要实现标识了TransactionCallHandler的方法实现自动事务,使用Castle.DynamicProxy实现BL类的代理
Castle.DynamicProxy一般操作
publicclassMyClass:IMyClass { publicvoidMyMethod() { Console.WriteLine("MyMehod"); } } publicclassTestIntercept:IInterceptor { publicvoidIntercept(IInvocationinvocation) { Console.WriteLine("before"); invocation.Proceed(); Console.WriteLine("after"); } } varproxyGenerate=newProxyGenerator(); TestInterceptt=newTestIntercept(); varpg=proxyGenerate.CreateClassProxy(t); pg.MyMethod(); //输出是 //before //MyMehod //after
before就是要开启事务的地方,after就是提交事务的地方
最后实现
publicclassTransactionInterceptor:IInterceptor { privatereadonlyILoggerlogger; publicTransactionInterceptor() { logger=LogManager.GetCurrentClassLogger(); } publicvoidIntercept(IInvocationinvocation) { MethodInfomethodInfo=invocation.MethodInvocationTarget; if(methodInfo==null) { methodInfo=invocation.Method; } TransactionCallHandlerAttributetransaction= methodInfo.GetCustomAttributes(true).FirstOrDefault(); if(transaction!=null) { SugarManager.BeginTran(); try { SugarManager.TranCountAddOne(); invocation.Proceed(); SugarManager.TranCountMunisOne(); SugarManager.CommitTran(); } catch(Exceptione) { SugarManager.RollbackTran(); logger.Error(e); throwe; } } else { invocation.Proceed(); } } } [AttributeUsage(AttributeTargets.Method,Inherited=true)] publicclassTransactionCallHandlerAttribute:Attribute { publicTransactionCallHandlerAttribute() { } }
Autofac与Castle.DynamicProxy结合使用
创建代理的时候一个BL类就要一次操作
proxyGenerate.CreateClassProxy(t);
而且项目里BL类的实例化是交给IOC容器控制的,我用的是Autofac。当然Autofac和Castle.DynamicProxy是可以结合使用的
usingSystem.Reflection; usingAutofac; usingAutofac.Extras.DynamicProxy; usingModule=Autofac.Module; publicclassBusinessModule:Module { protectedoverridevoidLoad(ContainerBuilderbuilder) { varbusiness=Assembly.Load("FTY.Business"); builder.RegisterAssemblyTypes(business) .AsImplementedInterfaces().InterceptedBy(typeof(TransactionInterceptor)).EnableInterfaceInterceptors(); builder.RegisterType(); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。