说起面向切面编程(AOP),我会想起Asp.Net MVC的过滤器。微软提供了一些类给程序员在代码里面来完成切面处理。因为aop更多的是联系于权限,日志,登录等相关话题,在一个系统中,这些往往都是必须存在的,所以aop说起来也是很重要的存在着。前些日子在框架中实现了AOP方面的思想,写了几个类,在这里讨论一下。
先简单介绍一下几个类,在下文中再继续讲一下每个类都做了什么东西。
namespace YZR.Interface { public interface IRWhere { string LogicName { get; set; } string WhereName { get; set; } string ActionName { get; set; } object ResultValue { get; set; } } public interface IRouteData { string TableName { get; set; } List<IRWhere> Wheres { get; set; } RAopEnum Action { get; set; } } public interface IAop { IRouteData Rdata { get; set; } void BeginAction(); void EndAction(); } }
这里定义了几个接口类,IDataRoute描述的是面向切面编程中保存的数据,包括操作的表,条件以及动作。IAop描述的则是切面,这里写了BeginAction()和EndAction()两个切面。IRWhere则是描述条件。
namespace YZR.Core { public class RAop : IAop { private IRouteData rdata; public IRouteData Rdata { get { return rdata; } set { rdata = value; } } public RAop() { } public RAop(RouteData _rdata) { this.rdata = _rdata; } public virtual void BeginAction() { } public virtual void EndAction() { } } }
RAop实现了IAop接口,它将负责框架中对于切面处理被子类继承并重写BeginAction()和EndAction();
这样我们自己定义自己的切面类时,只需要继承RAop类即可。
namespace YZR.Logic { using YZR.Core; using YZR.Data; /// <summary> /// 编写你自己的Aop类 (比如权限) /// </summary> public class Aop:RAop { public Aop() { } public override void BeginAction() { RouteData rd = (RouteData)Rdata; //用户操作的数据将会封装在RouteData,你可以通过Base.Rdata得到 //TODO:通过RouteData进行逻辑处理 #region 权限验证等处理 #endregion base.BeginAction(); } public override void EndAction() { base.EndAction(); } } }
程序中只需要通过RouteData就可以得到切面中保存的信息,比如表,动作,条件等。
例如有以下动作枚举:
public enum RAopEnum { Select, SelectOne, Add, Update, Delete, None }
编写完我们自己的Aop类之后,需要在应用程序中注册==>需要在AppStart.Filter(Instance)注册。
protected void Application_Start(object sender, EventArgs e) { //初始化 AppStart.AppStart.Init(); //将你写的AOP类加载进来 AppStart.AppStart.Fliter(new Aop()); }
举个例子:
在我们的业务代码中实现一下代码:
string drawAndregister = Request["adr"]; string CookieTime = string.Empty; string CookieKey = string.Empty; string JsonStr = "{"State":0,"content":"24小时只能签到一次"}"; IDataBase action = RUtility.Instance.GetDbUtility(TableName); action.ROpen(); List<IRWhere> list = new List<IRWhere>(); list.Add(new RWhere(" and ",Register.IsLuck,"=","0")); DataTable dt = action.Select(list); action.RClose();
在我们自己编写的Aop类中就可以通过RouteData获得Select(list)的相关信息。
那么底层是怎么实现的呢?
从我们的全局文件开始讲起,看一下Filter()做了什么事情:
public static void Fliter(object classObj) { aopContext.Add(classObj); }
using YZR.Data; using YZR.Interface; public class aopContext { public static void Add(object classObj) { IAop iaop; try { iaop = classObj as IAop; } catch (Exception ex) { throw; } aopInstance.Add(iaop); } }
从代码可以看出,最后是aopInstance.Add(new Aop());再来看一下aopInstance是什么东东。
namespace YZR.Data { public class aopInstance { private static List<IAop> aopList=new List<IAop>(); public static List<IAop> AopList { get { return aopInstance.aopList; } set { aopInstance.aopList = value; } } public static void Add(IAop iaop) { aopList.Add(iaop); } } }
在这里aopInstance只是提供了一个List<T>来转载所有实现IAop接口的子类或者接口。
哪你还是会说,你还是不知道到底为什么切面数据会保存到RouteData中去了,我们再来看RMotion(RAction)中做了什么,你就明白了。
static RMotion() { rd = new RouteData(); } private void AopInit() { List<IAop> list = aopInstance.AopList; foreach (IAop aop in list) { aop.Rdata = rd; aop.BeginAction(); } } private void AopEnd() { List<IAop> list = aopInstance.AopList; foreach (IAop aop in list) { aop.EndAction(); } }
public DataTable Select(List<IRWhere> whereList) { rd.Action = RAopEnum.Select; rd.TableName = tableName; rd.Wheres = whereList; rd.ColNames = "*"; AopInit(); StringBuilder sb = new StringBuilder(); StringBuilder sbWhere = new StringBuilder(); if (!whereList.Any()) { sb.AppendFormat("select * from {0} ", tableName); } else { for (int i = 0; i < whereList.Count; i++) { IRWhere where = whereList[i]; sbWhere.AppendFormat(" " + where.LogicName + " " + where.WhereName + " " + where.ActionName + " '" + where.ResultValue + "' "); } sb.AppendFormat("select * from {0} where 1=1 {1} ", tableName, sbWhere.ToString()); } //SqlConnection conn = DbAction.getConn(); if (ston == null)//返回一个新的SqlCommand,不含事务 { com = getCommand(sb.ToString()); } else { //若ston不为null,即含有事务,com已经创建 com.CommandText = sb.ToString(); } //conn.Open(); SqlDataReader dr = com.ExecuteReader(); DataTable dt = new DataTable(); int fieldcout = dr.FieldCount; if (dr.FieldCount > 0) { for (int i = 0; i < dr.FieldCount; i++) { DataColumn dc = new DataColumn(dr.GetName(i), dr.GetFieldType(i)); dt.Columns.Add(dc); } object[] rowobject = new object[dr.FieldCount]; while (dr.Read()) { dr.GetValues(rowobject); dt.LoadDataRow(rowobject, true); } } dr.Close(); ; // conn.Close(); AopEnd(); return dt; }
在YZR.Data中调用Select()方法的时候,在AopInit()会使用aopInstance对象的BeginAction(),在AopEnd()中调用aopInstance对象的EndAction();而且在执行BeginAction()或者EndAction()之前会将切面数据保存到RouteData类型变量中,这样在业务逻辑层中就可以通过RouteData得到所有切面数据。
通过RouteData提供出来的信息,程序代码可以知道现在要执行的是什么任务或动作,这样在Aop类中你可以在BeginAction()处理动作完成之前要做的一些判断或者操作,在EndAction()处理动作操作完成之后的一些操作或者完成信息。