前面的两篇随笔,都是只是个铺垫,真正实现增强四项基本功能的重头戏,在本篇随笔中,
本文将通过AOP实现如下的四个基本功能:
/// <para>1、自动管理数据库连接[可选]</para>
/// <para>2、自动管理数据库事务,当接收到异常后(无论什么异常)事务将自动回滚[可选]</para>
/// <para>3、服务级加锁[必选]</para>
/// <para>4、以统一方式处理 服务异常 及 错误, 包括数据库异常 和 主动抛出的异常[必选]</para>
为了在完成3、4两项,需要在Service层基类中,引入几个属性和方法,以便协作完成相应功能。
该Service基类,很简单,不用太多的解释:
///<summary>///扩展的抽象服务类///<para>配合增强类,完成以下功能:</para>///<para>1、自动管理数据库连接[可选]</para>///<para>2、自动管理数据库事务,当接收到异常后(无论什么异常)事务将自动回滚[可选]</para>//////<para>3、服务级加锁[必选]</para>///<para>4、以统一方式处理服务异常及错误处理,包括数据库异常 和 主动抛出的异常[必选]</para>///</summary>publicabstractclassServiceAbstract : MarshalByRefObject
{///<summary>///是否发生错误///</summary>publicboolError {get;PRotectedset; }///<summary>///错误提示信息(友好的,用户可见)///</summary>publicstringErrorMsg {get;protectedset; }///<summary>///错误详情///<para>所有错误,均通过异常抛出</para>///</summary>publicException ErrorEx {get;protectedset; }///<summary>///重置错误信息///</summary>publicvoidResetError()
{this.Error =false;this.ErrorMsg =string.Empty;this.ErrorEx =null;
}///<summary>///设置错误信息///</summary>///<param name="msg"></param>///<param name="ex"></param>publicvoidSetError(stringmsg, Exception ex)
{this.Error =true;this.ErrorEx =ex;this.ErrorMsg =msg;
}///<summary>///获取服务级别的锁定对象,以完成系统应用层加锁(具体而言是Service层加锁)///</summary>///<returns></returns>publicabstractobjectGetLockObject();
}
Service基类
为了统一处理错误,引入一自定义异常,所有需要抛出错误的地方,都抛出该异常即可。
///<summary>///自定义的服务异常///</summary>[Serializable]publicclassServiceException : Exception
{///<summary>///为异常提供附加数据///<para>用户不可见</para>///</summary>publicintCode {get;set; }///<summary>///为异常提供附加数据///<para>用户不可见</para>///</summary>publicstringTag {get;set; }publicServiceException() { }publicServiceException(stringmessage) :base(message) { }publicServiceException(stringmessage, Exception inner) :base(message, inner) { }protectedServiceException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
:base(info, context) { }
}
自定义异常
重头戏:抽象的增强类:
///<summary>///抽象的服务增强类///<para>增强以下功能:</para>///<para>1、自动管理数据库连接[可选]</para>///<para>2、自动管理数据库事务,当接收到异常后(无论什么异常)事务将自动回滚[可选]</para>//////<para>3、服务级加锁[必选]</para>///<para>4、以统一方式处理 服务异常 及 错误, 包括数据库异常 和 主动抛出的异常[必选]</para>///</summary>publicabstractclassServiceAdviceAbstract<T> : AdviceAbstractwhereT : Exception
{privatestaticobjectobjLock =newobject();#region属性///<summary>///是否保持(长)连接,即自动管理连接///</summary>publicboolKeepConnection {get;privateset; }///<summary>///是否使用事务,即自动管理事务///</summary>publicboolUseTransaction {get;privateset; }///<summary>///是否在当前方法的增强中打开了连接///</summary>protectedboolCurrentKeepConnection {get;set; }///<summary>///是否在当前方法的增强中开启了事务///</summary>protectedboolCurrentUseTransaction {get;set; }#endregion#region构造函数///<summary>//////</summary>///<param name="keepConnection">是否保持(长)连接,即自动管理连接</param>///<param name="useTransaction">是否使用事务,即自动管理事务</param>publicServiceAdviceAbstract(boolkeepConnection,booluseTransaction)
{this.KeepConnection =keepConnection;this.UseTransaction =useTransaction;
}#endregionpublicsealedoverrideIMessage Invoke(MarshalByRefObject target, IMethodCallMessage callMessage)
{
ServiceAbstract service= targetasServiceAbstract;//服务类型校验 其抛出的异常不会被捕获Check(service);returnLockInvoke(service, callMessage);
}#region可以扩展的虚函数///<summary>///执行Lock加锁调用///</summary>///<param name="target"></param>///<param name="callMessage"></param>///<returns></returns>protectedvirtualIMessage LockInvoke(ServiceAbstract target, IMethodCallMessage callMessage)
{lock(target.GetLockObject())
{returnCatchAdviceInvoke(target, callMessage);
}
}///<summary>///执行Try...Catch增强调用///</summary>///<param name="target"></param>///<param name="callMessage"></param>///<returns></returns>protectedvirtualIMessage CatchAdviceInvoke(ServiceAbstract target, IMethodCallMessage callMessage)
{try{
BeforeInvokeBeProxy(target);
IMessage message=DelayProxyUtil.InvokeBeProxy(target, callMessage);
AfterInvokeBeProxy(target);returnmessage;
}//调用方法时,内部抛出的异常catch(TargetInvocationException targetEx)
{stringmsg =string.Empty;if(!(targetEx.InnerExceptionisServiceException))
{if(targetEx.InnerExceptionisDbException)
{
msg="数据异常:";
}elseif(targetEx.InnerExceptionisT)
{
msg="服务异常:";
}else{
msg="系统异常:";
}
}returnReturnError(msg +targetEx.InnerException.Message, targetEx.InnerException, target, callMessage);
}catch(ServiceException sEx)
{returnReturnError(sEx.Message, sEx, target, callMessage);
}catch(DbException dbEx)
{returnReturnError("数据异常:"+dbEx.Message, dbEx, target, callMessage);
}catch(T tEx)
{returnReturnError("服务异常:"+tEx.Message, tEx, target, callMessage);
}catch(Exception ex)
{returnReturnError("系统异常:"+ex.Message, ex, target, callMessage);
}
}///<summary>///调用被代理对象方法前执行///</summary>///<param name="target"></param>protectedvirtualvoidBeforeInvokeBeProxy(ServiceAbstract target)
{
target.ResetError();this.CurrentKeepConnection =false;this.CurrentUseTransaction =false;if(!this.KeepConnection && !this.UseTransaction)
{return;
}//已经开启了事务if(this.HasBeginTransaction())
{//不需要在当前方法的增强中进行任何处理return;
}//已经打开了连接if(this.HasOpenConnection())
{if(this.UseTransaction)
{this.BeginTransaction(true);this.CurrentUseTransaction =true;return;
}return;
}//即没有开启事务,又没有打开连接if(this.UseTransaction)
{this.BeginTransaction(false);this.CurrentKeepConnection =true;this.CurrentUseTransaction =true;
}elseif(this.KeepConnection)
{this.OpenConnection();this.CurrentKeepConnection =true;
}
}///<summary>///调用被代理对象方法后执行///</summary>///<param name="target"></param>protectedvirtualvoidAfterInvokeBeProxy(ServiceAbstract target)
{//当前增强 只打开了连接if(this.CurrentKeepConnection && !this.CurrentUseTransaction)
{this.CloseConnection();
}//当前增强 只开启了事务elseif(!this.CurrentKeepConnection &&this.CurrentUseTransaction)
{this.CommitTransaction(true);
}//当前增强 既打开了连接,又开启了事务elseif(this.CurrentKeepConnection &&this.CurrentUseTransaction)
{this.CommitTransaction(false);
}
}///<summary>///返回错误信息///<para>拦截所有异常,将错误信息存储到 ExtensionServiceAbstract 对象中,并返回被调用方法的默认值</para>///</summary>///<param name="msg"></param>///<param name="ex"></param>///<param name="target"></param>///<param name="callMessage"></param>///<returns></returns>protectedvirtualIMessage ReturnError(stringmsg, Exception ex,
ServiceAbstract target, IMethodCallMessage callMessage)
{try{//当前增强 只打开了连接if(this.CurrentKeepConnection && !this.CurrentUseTransaction)
{this.CloseConnection();
}//当前增强 只开启了事务elseif(!this.CurrentKeepConnection &&this.CurrentUseTransaction)
{this.RollBackTransaction(true);
}//当前增强 既打开了连接,又开启了事务elseif(this.CurrentKeepConnection &&this.CurrentUseTransaction)
{this.RollBackTransaction(false);
}
}catch(Exception e)
{
Console.WriteLine(e.Message);
}//如果 逻辑上下文中已经进行了Try...Catch调用,//则 将捕获的异常向上层抛出//if (this.HasTryCatch)//{//return DelayProxyUtil.ReturnExecption(ex, callMessage);//}target.SetError(msg, ex);//记录日志WriteLog(ex);returnDelayProxyUtil.ReturnDefaultValue(target, callMessage);
}///<summary>///记录日志///</summary>///<param name="ex"></param>protectedvirtualvoidWriteLog(Exception ex)
{
}///<summary>///校验被代理的对象的类型///</summary>///<param name="service"></param>protectedvirtualvoidCheck(ServiceAbstract service)
{if(service ==null)
{thrownewServiceException("服务增强类 AdviceAbstractGeneric 只能用于 MyBatisServiceAbstract类型的子类型");
}
}#endregion#region管理数据库连接和事务///<summary>///打开连接///</summary>protectedabstractvoidOpenConnection();///<summary>///关闭连接///</summary>protectedabstractvoidCloseConnection();///<summary>///开启事务///</summary>protectedabstractvoidBeginTransaction(boolonlyBeginTransaction);///<summary>///提交事务///</summary>protectedabstractvoidCommitTransaction(boolonlyCommitTransaction);///<summary>///回滚事务///</summary>protectedabstractvoidRollBackTransaction(boolonlyRollBackTransaction);///<summary>///是否打开了连接///</summary>///<returns></returns>protectedabstractboolHasOpenConnection();///<summary>///是否开启了事务///</summary>///<returns></returns>protectedabstractboolHasBeginTransaction();#endregion}
抽象的增强类
虽然,该类是抽象类,但四项基本功能,都已经完成了。
另外,需要指出一点潜在bug:
当Service方法嵌套调用Service方法的时候,如果内层Service方法,抛出了异常,
会被内层方法的增强所捕获,而外层Service方法接收不到错误信息。
正因如此,可能外层方法的事务无法像预料的那样进行回滚。
当然,解决该问题,相对而言也算简单,下篇随笔再做说明。
还有一点需要说明:
我当前的增强是基于iBatisNet的,其数据库操作都是基于单例模式实现的(借助了HttpContext),
所以我将数据库连接及事务管理的相关方法,放在了‘增强继承体系’中,
如果使用其他方式处理数据库连接或事务,比较麻烦、可以考虑将相关方法,迁移到‘Service基类中’,放入‘Service继承体系’。
借用Service层,连接Dao层,实现真正的数据库操作(包括 连接 和 事务)。
具体的实现方式,就留给大家去探究了。
附源码(MVC4的项目 没有packages文件夹):http://files.cnblogs.com/files/08shiyan/AOPDemo.zip
该源码中,没有针对当前随笔,做相应的 demo.
未完待续...
下篇随笔,将实现简单的属性注入及 被代理对象延迟初始化。