先說下情境,C#中為什麼要使用Aop,而我又是在哪裡使用Aop?
本人只是想攔截實體類的Set的方法,然後在Set之前,調用一下其它方法,把值賦給另一個對象。
而我做的都是在實體類的基類裡處理:比如:
public class OrmBase
讓所有繼承這個基類的實體類都具有Orm操作功能,再加上一個小小特殊的要求處理,屬性Set時,需要對另一對象賦值。
如果說,我這樣實現:在OrmBase中可以提供方法,讓所有的子類的屬性都這樣操作:
public class Users:OrmBase{public int _ID;public int ID {get;set{ base.SetXX(value); }}
不過每個實體都這樣寫,雖然是啥沒問題,不過能簡化的還是簡化。
在能追求簡潔的世界裡,當然更喜歡簡潔的寫法如:
public int ID {get;set;}
因此,直接在基類裡直接攔截子類set方法,在裡面直接調用SetXX就搞定了,如何?呢?又花了一天的時間查資料研究學習並實現。
為此,要攔截,就得折騰Aop:傳統的Aop使用RealProxy,使用非常簡單,但是被忽悠的非常複雜,下面:
1:在要攔截的類頭上加個屬性標識,同時繼承自ContextBoundObject:
[AopAttribute]public class OrmBase:ContextBoundObject
OK,在基類裡加一個,這樣所有子類也算被附加了,加上一個標識,就可以被攔截了,那這個AopAttribute屬性是啥?看下面
2:AopAttribute繼承代理屬性標識類,用來掛在要攔截的類的頭上:
class AopAttribute : ProxyAttribute { public override MarshalByRefObject CreateInstance(Type serverType) { AopProxy realProxy = new AopProxy(serverType); return realProxy.GetTransparentProxy() as MarshalByRefObject; } }
看,裡面就兩行,非常簡單,中間調用了繼承RealProxy的AopProxy類,AopProxy是什麼,怎麼出來的?看下面
3:AopProxy類,就是攔截的訊息處理,先上個簡單版,免的大夥看不懂:
class AopProxy : RealProxy { public AopProxy(Type serverType) : base(serverType) { } public override IMessage Invoke(IMessage msg) { //訊息攔截之後,就會執行這裡的方法。 } }
OK,簡單吧,就這麼兩個類,就可以實現攔截了,不過重點就是這裡攔截之後的代碼,稍為複雜點,一般照抄就行了,攔截的代碼如下:
if (msg is IConstructionCallMessage) // 如果是建構函式,按原來的方式返回即可。
{
IConstructionCallMessage constructCallMsg = msg as IConstructionCallMessage;
IConstructionReturnMessage constructionReturnMessage = this.InitializeServerObject((IConstructionCallMessage)msg);
RealProxy.SetStubData(this, constructionReturnMessage.ReturnValue);
return constructionReturnMessage;
}
else if (msg is IMethodCallMessage) //如果是方法調用(屬性也是方法調用的一種)
{
IMethodCallMessage callMsg = msg as IMethodCallMessage;
object[] args = callMsg.Args;
IMessage message;
try
{
if (callMsg.MethodName.StartsWith("set_") && args.Length == 1)
{
//這裡檢測到是set方法,然後應怎麼調用對象的其它方法呢?
}
object o = callMsg.MethodBase.Invoke(GetUnwrappedServer(), args);
message = new ReturnMessage(o, args, args.Length, callMsg.LogicalCallContext, callMsg);
}
catch (Exception e)
{
message = new ReturnMessage(e, callMsg);
}
return message;
}
return msg;
為了調用原始對象的其它方法,我花了近一天的時間查資料,可惜網路上並沒有相應的資訊,多數的人應用,都是引向一個其它方法(一個不需要調用原始對象的方法)
目前網路上Aop資訊太少,C#的更少,關於如何擷取原始對象,然後調用原始對象的,找不到一篇相關文章,我特糾結。
於是,我按傳統方式,想盡辦法的想擷取到原始對象,再調用,經過九九八十一招,還是失敗了。
(一開始是想:通過反射從類型再建立一個實體這種不靠譜的嘗試: 造成死迴圈,每次new攔截,在攔截裡又new)
中間省一大堆......痛苦的經曆和嘗試.......
只要用心想,方法總有的,最終還是被我發現了:
1:擷取要調用的方法:
在建構函式中,根據傳進來的serverType,擷取到SetXX的方法MethodInfo:
method = serverType.GetMethod("SetXX", BindingFlags.NonPublic | BindingFlags.Instance);
2:在攔截方法中調用:
if (callMsg.MethodName.StartsWith("set_") && args.Length == 1)
{
method.Invoke(GetUnwrappedServer(), new object[] { callMsg.MethodName.Substring(4), args[0] });//對屬性進行調用
}
過程很複雜,嘗試過N百種方式,結果很簡單,分享很重要!
為此,解決了ORM對子類的屬性攔截,並實現了在屬性賦值時調用執行個體其它方法。