程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C# Aop簡單掃盲及ORM實體類屬性攔截示例

C# Aop簡單掃盲及ORM實體類屬性攔截示例

編輯:C#入門知識

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

 

 

摘自 路過秋天

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved