你的位置:首页 > ASP.net教程

[ASP.net教程]自己实现简单的AOP(三) 实现增强四项基本功能


前面的两篇随笔,都是只是个铺垫,真正实现增强四项基本功能的重头戏,在本篇随笔中,

本文将通过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>  public abstract class ServiceAbstract : MarshalByRefObject  {    /// <summary>    /// 是否发生错误    /// </summary>    public bool Error { get; protected set; }    /// <summary>    /// 错误提示信息(友好的,用户可见)    /// </summary>    public string ErrorMsg { get; protected set; }    /// <summary>    /// 错误详情    /// <para>所有错误,均通过异常抛出</para>    /// </summary>    public Exception ErrorEx { get; protected set; }    /// <summary>    /// 重置错误信息    /// </summary>    public void ResetError()    {      this.Error = false;      this.ErrorMsg = string.Empty;      this.ErrorEx = null;    }    /// <summary>    /// 设置错误信息    /// </summary>    /// <param name="msg"></param>    /// <param name="ex"></param>    public void SetError(string msg, Exception ex)    {      this.Error = true;      this.ErrorEx = ex;      this.ErrorMsg = msg;    }    /// <summary>    /// 获取服务级别的锁定对象,以完成系统应用层加锁(具体而言是Service层加锁)    /// </summary>    /// <returns></returns>    public abstract object GetLockObject();  }

Service基类

 

为了统一处理错误,引入一自定义异常,所有需要抛出错误的地方,都抛出该异常即可。

  /// <summary>  /// 自定义的服务异常  /// </summary>  [Serializable]  public class ServiceException : Exception  {    /// <summary>    /// 为异常提供附加数据    /// <para>用户不可见</para>    /// </summary>    public int Code { get; set; }    /// <summary>    /// 为异常提供附加数据    /// <para>用户不可见</para>    /// </summary>    public string Tag { get; set; }    public ServiceException() { }    public ServiceException(string message) : base(message) { }    public ServiceException(string message, Exception inner) : base(message, inner) { }    protected ServiceException(     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>  public abstract class ServiceAdviceAbstract<T> : AdviceAbstract where T : Exception  {    private static object objLock = new object();    #region 属性    /// <summary>    /// 是否保持(长)连接,即自动管理连接    /// </summary>    public bool KeepConnection { get; private set; }    /// <summary>    /// 是否使用事务,即自动管理事务    /// </summary>    public bool UseTransaction { get; private set; }    /// <summary>    /// 是否在当前方法的增强中打开了连接    /// </summary>    protected bool CurrentKeepConnection { get; set; }    /// <summary>    /// 是否在当前方法的增强中开启了事务    /// </summary>    protected bool CurrentUseTransaction { get; set; }    #endregion    #region 构造函数    /// <summary>    ///     /// </summary>    /// <param name="keepConnection">是否保持(长)连接,即自动管理连接</param>    /// <param name="useTransaction">是否使用事务,即自动管理事务</param>    public ServiceAdviceAbstract(bool keepConnection, bool useTransaction)    {      this.KeepConnection = keepConnection;      this.UseTransaction = useTransaction;    }    #endregion    public sealed override IMessage Invoke(MarshalByRefObject target, IMethodCallMessage callMessage)    {      ServiceAbstract service = target as ServiceAbstract;      // 服务类型校验 其抛出的异常不会被捕获      Check(service);      return LockInvoke(service, callMessage);    }    #region 可以扩展的虚函数    /// <summary>    /// 执行Lock加锁调用    /// </summary>    /// <param name="target"></param>    /// <param name="callMessage"></param>    /// <returns></returns>    protected virtual IMessage LockInvoke(ServiceAbstract target, IMethodCallMessage callMessage)    {      lock (target.GetLockObject())      {        return CatchAdviceInvoke(target, callMessage);      }    }    /// <summary>    /// 执行Try...Catch增强调用    /// </summary>    /// <param name="target"></param>    /// <param name="callMessage"></param>    /// <returns></returns>    protected virtual IMessage CatchAdviceInvoke(ServiceAbstract target, IMethodCallMessage callMessage)    {      try      {        BeforeInvokeBeProxy(target);        IMessage message = DelayProxyUtil.InvokeBeProxy(target, callMessage);        AfterInvokeBeProxy(target);        return message;      }      // 调用方法时,内部抛出的异常      catch (TargetInvocationException targetEx)      {        string msg = string.Empty;        if (!(targetEx.InnerException is ServiceException))        {          if (targetEx.InnerException is DbException)          {            msg = "数据异常:";          }          else if (targetEx.InnerException is T)          {            msg = "服务异常:";          }          else          {            msg = "系统异常:";          }        }        return ReturnError(msg + targetEx.InnerException.Message, targetEx.InnerException, target, callMessage);      }      catch (ServiceException sEx)      {        return ReturnError(sEx.Message, sEx, target, callMessage);      }      catch (DbException dbEx)      {        return ReturnError("数据异常:" + dbEx.Message, dbEx, target, callMessage);      }      catch (T tEx)      {        return ReturnError("服务异常:" + tEx.Message, tEx, target, callMessage);      }      catch (Exception ex)      {        return ReturnError("系统异常:" + ex.Message, ex, target, callMessage);      }    }    /// <summary>    /// 调用被代理对象方法前执行    /// </summary>    /// <param name="target"></param>    protected virtual void BeforeInvokeBeProxy(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;      }      else if (this.KeepConnection)      {        this.OpenConnection();        this.CurrentKeepConnection = true;      }    }    /// <summary>    /// 调用被代理对象方法后执行    /// </summary>    /// <param name="target"></param>    protected virtual void AfterInvokeBeProxy(ServiceAbstract target)    {      // 当前增强 只打开了连接      if (this.CurrentKeepConnection && !this.CurrentUseTransaction)      {        this.CloseConnection();      }      // 当前增强 只开启了事务      else if (!this.CurrentKeepConnection && this.CurrentUseTransaction)      {        this.CommitTransaction(true);      }      // 当前增强 既打开了连接,又开启了事务      else if (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>    protected virtual IMessage ReturnError(string msg, Exception ex,      ServiceAbstract target, IMethodCallMessage callMessage)    {      try      {        // 当前增强 只打开了连接        if (this.CurrentKeepConnection && !this.CurrentUseTransaction)        {          this.CloseConnection();        }        // 当前增强 只开启了事务        else if (!this.CurrentKeepConnection && this.CurrentUseTransaction)        {          this.RollBackTransaction(true);        }        // 当前增强 既打开了连接,又开启了事务        else if (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);      return DelayProxyUtil.ReturnDefaultValue(target, callMessage);    }    /// <summary>    /// 记录日志    /// </summary>    /// <param name="ex"></param>    protected virtual void WriteLog(Exception ex)    {    }    /// <summary>    /// 校验被代理的对象的类型    /// </summary>    /// <param name="service"></param>    protected virtual void Check(ServiceAbstract service)    {      if (service == null)      {        throw new ServiceException("服务增强类 AdviceAbstractGeneric 只能用于 MyBatisServiceAbstract类型的子类型 ");      }    }    #endregion    #region 管理数据库连接和事务    /// <summary>    /// 打开连接    /// </summary>    protected abstract void OpenConnection();    /// <summary>    /// 关闭连接    /// </summary>    protected abstract void CloseConnection();    /// <summary>    /// 开启事务    /// </summary>    protected abstract void BeginTransaction(bool onlyBeginTransaction);    /// <summary>    /// 提交事务    /// </summary>    protected abstract void CommitTransaction(bool onlyCommitTransaction);    /// <summary>    /// 回滚事务    /// </summary>    protected abstract void RollBackTransaction(bool onlyRollBackTransaction);    /// <summary>    /// 是否打开了连接    /// </summary>    /// <returns></returns>    protected abstract bool HasOpenConnection();    /// <summary>    /// 是否开启了事务    /// </summary>    /// <returns></returns>    protected abstract bool HasBeginTransaction();    #endregion  }

抽象的增强类

 

虽然,该类是抽象类,但四项基本功能,都已经完成了。

 

另外,需要指出一点潜在bug:

当Service方法嵌套调用Service方法的时候,如果内层Service方法,抛出了异常,

会被内层方法的增强所捕获,而外层Service方法接收不到错误信息。

正因如此,可能外层方法的事务无法像预料的那样进行回滚。

当然,解决该问题,相对而言也算简单,下篇随笔再做说明。

 

还有一点需要说明:

我当前的增强是基于iBatisNet的,其数据库操作都是基于单例模式实现的(借助了HttpContext),

所以我将数据库连接及事务管理的相关方法,放在了‘增强继承体系’中,

如果使用其他方式处理数据库连接或事务,比较麻烦、可以考虑将相关方法,迁移到‘Service基类中’,放入‘Service继承体系’。

借用Service层,连接Dao层,实现真正的数据库操作(包括 连接 和 事务)。

具体的实现方式,就留给大家去探究了。

 

附源码(MVC4的项目 没有packages文件夹):http://files.cnblogs.com/files/08shiyan/AOPDemo.zip 

该源码中,没有针对当前随笔,做相应的 demo. 

 

未完待续...

下篇随笔,将实现简单的属性注入 及 被代理对象延迟初始化。