利用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();
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。