先說下場景,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對子類的屬性攔截,並實現了在屬性賦值時調用實例其它方法。
摘自 路過秋天