主頁(yè) > 知識(shí)庫(kù) > 編寫輕量ajax組件第三篇實(shí)現(xiàn)

編寫輕量ajax組件第三篇實(shí)現(xiàn)

熱門標(biāo)簽:地圖標(biāo)注項(xiàng)目怎么樣 機(jī)器人電銷騙局揭秘 ai機(jī)器人電銷資源 超級(jí)大富翁地圖標(biāo)注 騰訊地圖標(biāo)注位置能用多久 個(gè)人怎樣在百度地圖標(biāo)注地名 硅語(yǔ)電話機(jī)器人公司 云呼外撥網(wǎng)絡(luò)電話系統(tǒng) 越南河內(nèi)地圖標(biāo)注

通過(guò)之前的介紹,我們知道要執(zhí)行頁(yè)面對(duì)象的方法,核心就是反射,是從請(qǐng)求獲取參數(shù)并執(zhí)行指定方法的過(guò)程。實(shí)際上這和asp.net mvc框架的核心思想很類似,它會(huì)解析url,從中獲取controller和action名稱,然后激活controller對(duì)象,從請(qǐng)求獲取action參數(shù)并執(zhí)action。在web form平臺(tái)上,我們把方法寫在.aspx.cs中,要實(shí)現(xiàn)的就是在頁(yè)面對(duì)象還未生成的情況下,執(zhí)行指定的方法,然后返回結(jié)果。

  我們先看實(shí)現(xiàn)后幾個(gè)調(diào)用例子,這些功能也可以組合使用:      

 [AjaxMethod]
    public void Test1(int index)
    {
      //簡(jiǎn)單調(diào)用
    }

    [AjaxMethod]
    public string Test2(Test test)
    {
      return "參數(shù)為一個(gè)Test實(shí)例";
    }

    [AjaxMethod(OutputCache = 20)]
    public string Test3(int index)
    {
      return "輸出結(jié)果緩存20秒";
    }

    [AjaxMethod(ServerCache = 20)]
    public string Test4()
    {
      return "在服務(wù)端緩存20秒";
    }

    [AjaxMethod(SessionState=SessionState.None)]
    public void Test5()
    {
      //Session未被加載
    }

    [AjaxMethod(SessionState = SessionState.ReadOnly)]
    public void Test6()
    {
      //Session只能讀不能寫
    }

    [AjaxMethod(SessionState = SessionState.ReadWrite)]
    public void Test7()
    {
      //Session可以讀寫
    }

    [AjaxMethod(IsAsync = true)]
    public void Test8()
    {
      //異步調(diào)用
    }  

  前面我們已經(jīng)熟悉基本的執(zhí)行流程,現(xiàn)在直接進(jìn)入主題。

Ajax約定

  通常現(xiàn)在主流瀏覽器在使用ajax發(fā)送異步請(qǐng)求時(shí),請(qǐng)求頭都會(huì)帶上一個(gè):X-Requested-With:XMLHttpRequest 的標(biāo)記。我們也可以直接通過(guò)這個(gè)標(biāo)記來(lái)判斷是不是ajax請(qǐng)求,不過(guò)項(xiàng)目中可能有用其它的組件,為了不相互影響,我們加入一個(gè)自定義的請(qǐng)求頭。這里為:

internal static class AjaxConfig
 {
  /// summary>
  /// 請(qǐng)求頭Ajax標(biāo)記鍵
  /// /summary>
  public const string Key = "AjaxFlag";

  /// summary>
  /// 請(qǐng)求頭Ajax標(biāo)記值
  /// /summary>
  public const string Value = "XHR";

  /// summary>
  /// 請(qǐng)求頭Ajax方法標(biāo)記
  /// /summary>
  public const string MethodName = "";
 }

  意思是如果http 的請(qǐng)求頭包含一個(gè) AjaxFlag : XHR,就是我們要處理的。另外http header的MethodName就表示我們要執(zhí)行的方法的名稱。

AjaxMethodAttribute標(biāo)記屬性

  標(biāo)記屬性是給反射用的,在這里定義我們需要的一些功能。我們希望有:

  1. 可以配置Session狀態(tài)

  2. 支持異步Handler

  3. 支持Get緩存

  4. 支持服務(wù)端緩存

  定義如下,用AttributeUsag標(biāo)記該標(biāo)記只能用于方法上。

  /// summary>
  /// ajax方法標(biāo)記屬性
  /// /summary>
  [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
  public class AjaxMethodAttribute : Attribute
  {
    public AjaxMethodAttribute()
    {      
    }

    private SessionState _sessionState = SessionState.None;
    private int _outputCache = 0;
    private int _serverCache = 0;
    private ContentType _contentType = ContentType.Plain;
    private bool _isUseAsync = false;

    /// summary>
    /// session狀態(tài)
    /// /summary>
    public SessionState SessionState 
    {
      get { return _sessionState; }
      set { _sessionState = value; }
    }

    /// summary>
    /// 客戶端緩存時(shí)間,以秒為單位。該標(biāo)記只對(duì)get請(qǐng)求有效
    /// /summary>
    public int OutputCache 
    {
      get { return _outputCache; }
      set { _outputCache = value; }
    }

    /// summary>
    /// 服務(wù)端緩存時(shí)間,以秒為單位
    /// /summary>
    public int ServerCache 
    {
      get { return _serverCache; }
      set { _serverCache = value; }
    }    

    /// summary>
    /// 輸出類型(默認(rèn)為text/plain)
    /// /summary>
    public ContentType ContentType 
    {
      get { return _contentType; }
      set { _contentType = value; }
    }

    /// summary>
    /// 使用啟用異步處理
    /// /summary>
    public bool IsAsync 
    {
      get { return _isUseAsync; }
      set { _isUseAsync = value; }
    }
  }

  /// summary>
  /// Session狀態(tài)
  /// /summary>
  public enum SessionState
  {
    None,
    ReadOnly,
    ReadWrite    
  }

  /// summary>
  /// 輸出內(nèi)容類型
  /// /summary>
  public enum ContentType
  {
    Plain,
    Html,
    XML,
    Javascript,
    JSON
  }

各種處理程序和AjaxHandlerFactory

  按照上一篇的說(shuō)法,具體的Handler主要分為兩類,異步和非異步;這兩類下,對(duì)于Session的狀態(tài)又有3三種,不支持、只支持讀(實(shí)現(xiàn)IReadOnlySessionState接口)、支持讀寫(實(shí)現(xiàn)IRequiresSessionState接口)。IReadOnlySessionState和IRequiresSessionState都只是標(biāo)記接口(無(wú)任何方法,其實(shí)應(yīng)該用標(biāo)記屬性實(shí)現(xiàn)比較合理)。異步的Handler需要實(shí)現(xiàn)IHttpAsyncHandler接口,該接口又實(shí)現(xiàn)了IHttpHandler。Handler的ProcessRequest方法(或BeginProcessRequest)就是我們要執(zhí)行方法的地方。定義如下:

  非異步狀態(tài)的Handler:

   

 //不支持Session
  internal class SyncAjaxHandler : IHttpHandler
  {
    private Page _page;
    private CacheMethodInfo _cacheMethodInfo;

    internal SyncAjaxHandler(Page page, CacheMethodInfo cacheMethodInfo)
    {
      _page = page;
      _cacheMethodInfo = cacheMethodInfo;
    }

    public void ProcessRequest(HttpContext context)
    {
      //執(zhí)行方法(下面詳細(xì)介紹)
      Executor.Execute(_page, context, _cacheMethodInfo);
    }

    public bool IsReusable
    {
      get { return false; }
    }

    public static SyncAjaxHandler CreateHandler(Page page, CacheMethodInfo cacheMethodInfo, SessionState state)
    {
      switch (state)
      {
        case SessionState.ReadOnly:
          return new SyncAjaxSessionReadOnlyHandler(page, cacheMethodInfo);
        case SessionState.ReadWrite:
          return new SyncAjaxSessionHandler(page, cacheMethodInfo);
        default:
          return new SyncAjaxHandler(page, cacheMethodInfo);
      }
    }
  }

  //支持只讀Session
  internal class SyncAjaxSessionReadOnlyHandler : SyncAjaxHandler, IReadOnlySessionState
  {
    internal SyncAjaxSessionReadOnlyHandler(Page page, CacheMethodInfo cacheMethodInfo)
      : base(page, cacheMethodInfo)
    {
    }
  }

  //支持讀寫Session
  internal class SyncAjaxSessionHandler : SyncAjaxHandler, IRequiresSessionState
  {
    internal SyncAjaxSessionHandler(Page page, CacheMethodInfo cacheMethodInfo)
      : base(page, cacheMethodInfo)
    {
    }
  } 

  異步狀態(tài)的Handler:

  

 //不支持Session
  internal class ASyncAjaxHandler : IHttpAsyncHandler, IHttpHandler
  {
    private Page _page;
    private CacheMethodInfo _cacheMethodInfo;

    internal ASyncAjaxHandler(Page page, CacheMethodInfo cacheMethodInfo)
    {
      _page = page;
      _cacheMethodInfo = cacheMethodInfo;
    }

    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
      //執(zhí)行方法(下面詳細(xì)介紹)
      ActionPage, HttpContext, CacheMethodInfo> action = new ActionPage, HttpContext, CacheMethodInfo>(Executor.Execute);
      IAsyncResult result = action.BeginInvoke(_page, context, _cacheMethodInfo, cb, action);
      return result;
    }

    public void EndProcessRequest(IAsyncResult result)
    {
      ActionPage, HttpContext, CacheMethodInfo> action = result.AsyncState as ActionPage, HttpContext, CacheMethodInfo>;
      action.EndInvoke(result);
    }

    public void ProcessRequest(HttpContext context)
    {
      throw new NotImplementedException();
    }

    public bool IsReusable
    {
      get { return false; }
    }

    public static ASyncAjaxHandler CreateHandler(Page page, CacheMethodInfo cacheMethodInfo, SessionState state)
    {
      switch (state)
      {
        case SessionState.ReadOnly:
          return new ASyncAjaxSessionReadOnlyHandler(page, cacheMethodInfo);
        case SessionState.ReadWrite:
          return new ASyncAjaxSessionHandler(page, cacheMethodInfo);
        default:
          return new ASyncAjaxHandler(page, cacheMethodInfo);
      }
    }
  }

  //支持只讀Session
  internal class ASyncAjaxSessionReadOnlyHandler : ASyncAjaxHandler, IReadOnlySessionState
  {
    internal ASyncAjaxSessionReadOnlyHandler(Page page, CacheMethodInfo cacheMethodInfo)
      : base(page, cacheMethodInfo)
    {
    }
  }

  //支持讀寫Session
  internal class ASyncAjaxSessionHandler : ASyncAjaxHandler, IRequiresSessionState
  {
    internal ASyncAjaxSessionHandler(Page page, CacheMethodInfo cacheMethodInfo)
      : base(page, cacheMethodInfo)
    {
    }
  }  

  AjaxHandlerFactory實(shí)現(xiàn)了IHandlerFactory接口,用來(lái)根據(jù)請(qǐng)求生成具體的Handler,它需要在web.config進(jìn)行注冊(cè)使用。AjaxHandlerFactory的GetHandler是我們攔截請(qǐng)求的第一步。通過(guò)請(qǐng)求頭的AjaxFlag:XHR來(lái)判斷是否需要我們處理,如果是,則創(chuàng)建一個(gè)Handler,否則按照普通的方式進(jìn)行。由于我們的方法是寫在.aspx.cs內(nèi)的,我們的請(qǐng)求是.aspx后綴的,也就是頁(yè)面(Page,實(shí)現(xiàn)了IHttpHandler)類型,Page是通過(guò)PageHandlerFactory創(chuàng)建的,PageHandlerFactory也實(shí)現(xiàn)了IHandlerFactory接口,表示它是用來(lái)創(chuàng)建處理程序的。所以我們需要用PageHandlerFactory來(lái)創(chuàng)建一個(gè)IHttpHandler,不過(guò)PageHandlerFactory的構(gòu)造函數(shù)是protected internal類型的,我們無(wú)法直接new一個(gè),所以需要通過(guò)一個(gè)CommonPageHandlerFactory繼承它來(lái)實(shí)現(xiàn)。

  通過(guò)PageHandlerFactory獲得Page后,結(jié)合方法名稱,我們就可以反射獲取AjaxMethodAttribute標(biāo)記屬性了。然后根據(jù)它的相關(guān)屬性生成具體的Handler。具體代碼如下:

  

 internal class CommonPageHandlerFactory : PageHandlerFactory { }

  internal class AjaxHandlerFactory : IHttpHandlerFactory
  {
    public void ReleaseHandler(IHttpHandler handler)
    {
    }

    public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
    {
      HttpRequest request = context.Request;
      if (string.Compare(request.Headers[AjaxConfig.Key], AjaxConfig.Value, true) == 0)
      {
        //檢查函數(shù)標(biāo)記
        string methodName = request.Headers[AjaxConfig.MethodName];
        if (methodName.IsNullOrEmpty())
        {
          Executor.EndCurrentRequest(context, "方法名稱未正確指定!");
          return null;
        }
        try
        {          
          CommonPageHandlerFactory ajaxPageHandler = new CommonPageHandlerFactory();
          IHttpHandler handler = ajaxPageHandler.GetHandler(context, requestType, url, pathTranslated);
          Page page = handler as Page;
          if (page == null)
          {
            Executor.EndCurrentRequest(context, "處理程序類型必須是aspx頁(yè)面!");
            return null;
          }
          return GetHandler(page, methodName, context);
        }
        catch
        {
          Executor.EndCurrentRequest(context, url + " 不存在!");
          return null;
        }
      }
      if (url.EndsWith(".aspx", StringComparison.CurrentCultureIgnoreCase))
      {
        CommonPageHandlerFactory orgPageHandler = new CommonPageHandlerFactory();
        return orgPageHandler.GetHandler(context, requestType, url, pathTranslated);
      }
      return null;
    }

    /// summary>
    /// 獲取自定義處理程序
    /// /summary>
    /// param name="page">處理頁(yè)面/param>
    /// param name="methodName">處理方法/param>
    /// param name="context">當(dāng)前請(qǐng)求/param>
    private IHttpHandler GetHandler(Page page, string methodName, HttpContext context)
    {
      //根據(jù)Page和MethodName進(jìn)行反射,獲取標(biāo)記屬性(下面詳細(xì)介紹)
      CacheMethodInfo methodInfo = Executor.GetDelegateInfo(page, methodName);
      if (methodInfo == null)
      {
        Executor.EndCurrentRequest(context, "找不到指定的Ajax方法!");
        return null;
      }
      AjaxMethodAttribute attribute = methodInfo.AjaxMethodAttribute;
      if (attribute.ServerCache > 0)
      {
        //先查找緩存
        object data = CacheHelper.TryGetCache(context);
        if (data != null)
        {
          Executor.EndCurrentRequest(context, data);
          return null;
        }
      }
      if (attribute.IsAsync)
      {
        //異步處理程序
        return ASyncAjaxHandler.CreateHandler(page, methodInfo, attribute.SessionState);
      }
      return SyncAjaxHandler.CreateHandler(page, methodInfo, attribute.SessionState);
    }
  }

  上面的CacheMethodInfo是用于緩存調(diào)用方法的相關(guān)信息的,第一篇我們有提到過(guò)優(yōu)化緩存的一些方法,其中就包括緩存+委托。但這里我們并不直接緩存方法的MethodInfo,因?yàn)榫彺鍹ethodInfo的話,需要通過(guò)Invoke去執(zhí)行,這樣的效率比較低。這里我緩存的是方法的委托,該委托的簽名為:Funcobject, object[], object>,該委托的返回值為object類型,表示可以返回任意的類型(我們可以在組件內(nèi)部進(jìn)行處理,例如如果是引用類型(非string),就將其序列化為json,但這里并沒(méi)有實(shí)現(xiàn))。該委托接收兩個(gè)參數(shù),第一個(gè)參數(shù)是方法所屬的對(duì)象,如果是靜態(tài)方法就是null;第二個(gè)參數(shù)是方法的參數(shù),定義為object[]表示可以接收任意類型的參數(shù)。通過(guò)委托執(zhí)行方法,與直接調(diào)用方法的效率差別就不是很大(對(duì)委托不熟悉的朋友可以參見(jiàn):委托)。CacheMethodInfo的定義如下:

  

 /// summary>
  /// 緩存方法信息  
  /// /summary>
  sealed class CacheMethodInfo
  {
    /// summary>
    /// 方法名稱
    /// /summary>
    public string MethodName { get; set; }

    /// summary>
    /// 方法委托
    /// /summary>
    public Funcobject, object[], object> Func { get; set; }

    /// summary>
    /// 方法參數(shù)
    /// /summary>
    public ParameterInfo[] Parameters { get; set; }

    /// summary>
    /// Ajax標(biāo)記屬性
    /// /summary>
    public AjaxMethodAttribute AjaxMethodAttribute { get; set; }
  }

核心方法

1. Eexcutor.GetDelegateInfo 獲取方法相關(guān)信息

  該方法用于遍歷頁(yè)面類,獲取所有AjaxMethodAttribute標(biāo)記的方法信息,生成一個(gè)CacheMethodInfo對(duì)象,包括標(biāo)記信息、方法名稱、參數(shù)信息,以及最重要的方法委托。該對(duì)象會(huì)緩存在一個(gè)哈希表中,下次獲取時(shí),直接從內(nèi)存獲得。

       

 /// summary>
    /// 獲取頁(yè)面標(biāo)記方法信息
    /// /summary>
    /// param name="page">頁(yè)面對(duì)象/param>
    /// param name="methodName">方法名稱/param>
    internal static CacheMethodInfo GetDelegateInfo(Page page, string methodName)
    {
      if (page == null)
      {
        throw new ArgumentNullException("page");
      }
      Type type = page.GetType();
      //ajaxDelegateTable是一個(gè)Hashtable
      Dictionarystring, CacheMethodInfo> dic = ajaxDelegateTable[type.AssemblyQualifiedName] as Dictionarystring, CacheMethodInfo>;
      if (dic == null)
      {
        dic = new Dictionarystring, CacheMethodInfo>();
        //遍歷頁(yè)面的所有MethodInfo
        IEnumerableCacheMethodInfo> infos = (from m in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
                           let ca = m.GetCustomAttributes(typeof(AjaxMethodAttribute), false).FirstOrDefault()
                           where ca != null
                           select new CacheMethodInfo
                           {
                             //方法標(biāo)記屬性
                             AjaxMethodAttribute = ca as AjaxMethodAttribute,
                             //方法名稱
                             MethodName = m.Name,
                             //方法參數(shù)信息
                             Parameters = m.GetParameters()
                           });

        if (infos.IsNullOrEmpty())
        {
          return null;
        }          
        for (int i = 0, length = infos.Count(); i  length; i++)
        {          
          CacheMethodInfo cacheMethodInfo = infos.ElementAt(i);          
          string name = cacheMethodInfo.MethodName;
          MethodInfo methodInfo = type.GetMethod(name);
          if (!dic.ContainsKey(name))
          {            
            //根據(jù)MethodInfo獲取方法委托
            cacheMethodInfo.Func = ReflectionUtil.GetMethodDelegate(methodInfo);
            dic.Add(name, cacheMethodInfo);
          }
        }
        ajaxDelegateTable[type.AssemblyQualifiedName] = dic;
      }
      CacheMethodInfo currentMethodInfo = null;
      dic.TryGetValue(methodName, out currentMethodInfo);
      return currentMethodInfo;      
    }

  獲取方法的委托的是通過(guò)一個(gè)ReflectionUtil獲得的,該類主要用來(lái)優(yōu)化反射,它通過(guò)Expression,可以將MethodInfo編譯成Funcobject,object[],object>委托,為Type編譯一個(gè)Funcobject>委托,用于創(chuàng)建實(shí)例對(duì)象。

  通過(guò)Expression優(yōu)化反射

  Expression(表達(dá)式樹(shù))允許我們將代碼邏輯以表達(dá)式的形式存儲(chǔ)在樹(shù)狀結(jié)構(gòu)里,然后在運(yùn)行時(shí)去動(dòng)態(tài)解析,實(shí)現(xiàn)動(dòng)態(tài)編輯和執(zhí)行代碼。熟悉ORM框架的朋友對(duì)Expression肯定很熟悉,因?yàn)榇蟛糠址椒ǘ加幸粋€(gè)ExpressionTDelegate>類型的參數(shù)。訪問(wèn)關(guān)系型數(shù)據(jù)庫(kù)的本質(zhì)還是sql語(yǔ)句,orm的工作就是為開(kāi)發(fā)人員屏蔽這個(gè)過(guò)程,以面向?qū)ο蟮姆绞饺プx寫數(shù)據(jù)庫(kù),而不是自己編寫sql語(yǔ)句。例如,Users.Where(u => u.Age > 18) 就可查詢年齡大于18的用戶。這里不對(duì)應(yīng)用在orm的過(guò)程進(jìn)行詳解,下面我們介紹如何用Expression并利用它來(lái)生成委托。

  .net定義了許多表達(dá)式類型,這些類型都派生自Expression,Expression是一個(gè)抽象類,而且是一個(gè)工廠類,所有類型的表達(dá)式都通過(guò)它來(lái)創(chuàng)建。如圖:

  先看一個(gè) 1 * 2 + 2 例子,我們用表達(dá)樹(shù)來(lái)描述來(lái)描述它:

           

/*
       * a * b + 2 
       */

      /*
      直接操作
      int a = 1, b = 2;
      int result = a * 2 + 2;
      */

      /*
      通過(guò)委托調(diào)用
      Funcint, int, int> func = new Funcint, int, int>((a, b) => { return a * b + 2; });
      func(1, 2);
      */

      /*通過(guò)Expression調(diào)用*/

      //定義兩個(gè)參數(shù)
      ParameterExpression pe1 = Expression.Parameter(typeof(int), "a");
      ParameterExpression pe2 = Expression.Parameter(typeof(int), "b");
      //定義一個(gè)常量
      ConstantExpression constExpression = Expression.Constant(2);      

      //參數(shù)數(shù)組
      ParameterExpression[] parametersExpression = new ParameterExpression[]{pe1,pe2};

      //一個(gè)乘法運(yùn)算
      BinaryExpression multiplyExpression = Expression.Multiply(pe1, pe2);

      //一個(gè)加法運(yùn)算
      BinaryExpression unaryExpression = Expression.Add(multiplyExpression, constExpression);

      //將上面的表達(dá)式轉(zhuǎn)換為一個(gè)委托表達(dá)式
      LambdaExpression lambdaExpression = Expression.LambdaFuncint, int, int>>(unaryExpression, parametersExpression);

      //將委托編譯成可執(zhí)行代碼
      Funcint,int,int> func = lambdaExpression.Compile() as Funcint,int,int>;
      Console.WriteLine(func(1, 2));

  可以看到我們最終將其編譯為一個(gè)具體類型的委托了。下面看我們真正用到的方法是如何實(shí)現(xiàn)的,代碼如下:

       

 public static Funcobject, object[], object> GetMethodDelegate(MethodInfo methodInfo)
    {
      if (methodInfo == null)
      {
        throw new ArgumentNullException("methodInfo");
      }
      //定義參數(shù)表達(dá)式,它表示委托的第一個(gè)參數(shù)
      ParameterExpression instanceExp = Expression.Parameter(typeof(object), "instance");

      //定義參數(shù)表達(dá)式,它表示委托的第二個(gè)參數(shù)
      ParameterExpression paramExp = Expression.Parameter(typeof(object[]), "parameters");

      //獲取方法的參數(shù)信息數(shù)組
      ParameterInfo[] paramInfos = methodInfo.GetParameters();

      //參數(shù)表達(dá)式集合
      ListExpression> paramExpList = new ListExpression>();

      int length = paramInfos.Length;
      for (int i = 0; i  length; i++)
      {
        //獲取paramExp參數(shù)數(shù)組的第i個(gè)元素
        BinaryExpression valueObj = Expression.ArrayIndex(paramExp, Expression.Constant(i));

        //將其轉(zhuǎn)換為與參數(shù)類型一致的類型
        UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType);

        //添加到參數(shù)集合
        paramExpList.Add(valueCast);
      }  

      //方法所屬的實(shí)例的表達(dá)式,如果為靜態(tài)則為null
      UnaryExpression instanceCast = methodInfo.IsStatic ? null : Expression.Convert(instanceExp, methodInfo.ReflectedType);

      //表示調(diào)用方法的表達(dá)式 
      MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, paramExpList);

      //將表達(dá)式目錄描述的lambda編譯為可執(zhí)行代碼(委托)
      if (methodCall.Type == typeof(void))
      {
        ExpressionActionobject, object[]>> lambda = Expression.LambdaActionobject, object[]>>(methodCall, instanceExp, paramExp);
        Actionobject, object[]> action = lambda.Compile();
        return (instance, parameters) =>
        {
          action(instance, parameters);
          return null;
        };
      }
      else
      {
        UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
        ExpressionFuncobject, object[], object>> lambda = Expression.LambdaFuncobject, object[], object>>(castMethodCall, instanceExp, paramExp);
        return lambda.Compile();
      }
    }

  具體代碼都有注釋解釋,最終我們獲得了一個(gè)Funcobject,object[],object>類型的委托,它會(huì)作為CacheMethodInfo的屬性進(jìn)行緩存。有興趣測(cè)試反射性能的朋友,也不妨去測(cè)試對(duì)比一下這幾種方式執(zhí)行的效率差別:1.直接執(zhí)行方法 2.Emit 3. 緩存+委托 4.Delegate.DynamicInvoke。

2. Executor.Execute 執(zhí)行委托

  在執(zhí)行委托前,我們需要先從請(qǐng)求獲取參數(shù),映射到方法。參數(shù)可以是簡(jiǎn)單的類型,如 string Test(int i,int j); 也可以是一個(gè)對(duì)象,如 string Test(User user); 如果是 string Test(User user1, User user2) 也行,提交參數(shù)時(shí)只需要加上 user1或 user2 前綴即可,例如 user1.Name,user2.Name。這里沒(méi)有支持更多的匹配方式,像mvc,它還支持嵌套類型等等,這些可以自己去實(shí)現(xiàn)。如果參數(shù)是一個(gè)對(duì)象,我們可能需要為它的字段進(jìn)行賦值,也可能為它的屬性進(jìn)行賦值。這里我們定義一個(gè)DataMember,用來(lái)表示字段或?qū)傩缘母割?。如?/p>

  internal abstract class DataMember
  {
    public abstract string Name { get; }
    public abstract Type MemberType { get; }
    public abstract void SetValue(object instance,object value);
    public abstract object GetValue(object instance);
  }

  接著定義屬性類型PropertyMember和字段類型FieldMember,分別繼承了DataMember。

  PropertyMember定義:

  internal class PropertyMember : DataMember
  {
    private PropertyInfo property;
    public PropertyMember(PropertyInfo property)
    {
      if (property == null)
      {
        throw new ArgumentNullException("property");
      }
      this.property = property;
    }

    public override void SetValue(object instance, object value)
    {
      if (instance == null)
      {
        throw new ArgumentNullException("instance");
      }
      this.property.SetValue(instance, value, null);
    }

    public override object GetValue(object instance)
    {
      if (instance == null)
      {
        throw new ArgumentNullException("instance");
      }
      return this.property.GetValue(instance,null);
    }

    public override string Name
    {
      get { return this.property.Name; }
    }

    public override Type MemberType
    {
      get { return this.property.PropertyType; }
    }
  }

  FieldMember定義:

   

 internal class FieldMember : DataMember
  {
    private FieldInfo field;
    public FieldMember(FieldInfo field)
    {
      if (field == null)
      {
        throw new ArgumentNullException("field");
      }
      this.field = field;
    }

    public override void SetValue(object instance, object value)
    {
      if (instance == null)
      {
        throw new ArgumentNullException("instance");
      }
      this.field.SetValue(instance, value);
    }

    public override object GetValue(object instance)
    {
      if (instance == null)
      {
        throw new ArgumentNullException("instance");
      }
      return this.field.GetValue(instance);
    }

    public override string Name
    {
      get { return this.field.Name;}
    }

    public override Type MemberType
    {
      get { return this.field.FieldType; }
    }
  }

  定義一個(gè)DataMemberManager,用來(lái)遍歷Type,獲取所有字段和屬性的,實(shí)現(xiàn)如下:

   

 internal static class DataMemberManager
  {
    /// summary>
    /// 獲取實(shí)例字段/屬性集合
    /// /summary>
    /// param name="type">類型/param>
    /// returns>/returns>
    public static ListDataMember> GetDataMember(Type type)
    {
      if (type == null)
      {
        throw new ArgumentNullException("type");
      }
      IEnumerablePropertyMember> propertyMembers = from property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                         select new PropertyMember(property);
      IEnumerableFieldMember> fieldMembers = from field in type.GetFields(BindingFlags.Instance | BindingFlags.Public)
                       select new FieldMember(field);
      ListDataMember> members = new ListDataMember>();
      foreach(var property in propertyMembers)
      {
        members.Add(property);
      }
      foreach (var field in fieldMembers)
      {
        members.Add(field);
      }
      return members;
    }
  }

  在前面我們定義的Handler的ProcessRequest方法中,我們調(diào)用了Executor.Execute,該方法用于執(zhí)行委托,實(shí)現(xiàn)如下:

      

 /// summary>
    /// 核心函數(shù),執(zhí)行Handler的方法
    /// /summary>
    /// param name="page">頁(yè)面對(duì)象/param>
    /// param name="context">請(qǐng)求上下文/param>
    /// param name="cacheMethodInfo">緩存方法原數(shù)據(jù)/param>
    internal static void Execute(Page page, HttpContext context, CacheMethodInfo methodInfo)
    {
      if (page == null)
      {
        throw new ArgumentNullException("page");
      }
      try
      {
        if (methodInfo != null)
        {
          HttpRequest request = context.Request;
          object[] parameters = GetParametersFromRequest(request, methodInfo.Parameters);
          object data = methodInfo.Func(page, parameters);
          int serverCache = methodInfo.AjaxMethodAttribute.ServerCache;
          if (serverCache > 0)
          {
            CacheHelper.Insert(context, methodInfo.AjaxMethodAttribute.ServerCache, data);
          }
          EndCurrentRequest(context, data, methodInfo.AjaxMethodAttribute.OutputCache);
        }
        else
        {
          EndCurrentRequest(context, "找不到合適的Ajax方法!");
        }
      }
      catch (FormatException)
      {
        EndCurrentRequest(context, "調(diào)用方法匹配到無(wú)效的參數(shù)!");
      }
      catch (InvalidCastException)
      {
        EndCurrentRequest(context, "參數(shù)轉(zhuǎn)換出錯(cuò)!");
      }
      catch (System.Threading.ThreadAbortException)
      {
        //do nothing
      }
      catch (Exception ex)
      {
        EndCurrentRequest(context, ex.Message);
      }
    }

  CacheMethodInfo我們已經(jīng)獲得了,現(xiàn)在只要獲得參數(shù)我們就可以執(zhí)行方法。

  GetParameterFromRequest用于從請(qǐng)求獲取object[]參數(shù)數(shù)組。根據(jù)上面所說(shuō)的,如果參數(shù)是一個(gè)簡(jiǎn)單類型,那么直接進(jìn)行轉(zhuǎn)換;如果是實(shí)例對(duì)象,那么我們先要?jiǎng)?chuàng)建new一個(gè)實(shí)例對(duì)象,然后為其字段或?qū)傩再x值。實(shí)現(xiàn)如下:

      

 /// summary>
    /// 從請(qǐng)求獲取參參數(shù)
    /// /summary>
    /// param name="request">HttpRequest/param>
    ///param name="parameters">參數(shù)信息/param>
    /// returns>參數(shù)數(shù)組/returns>
    private static object[] GetParametersFromRequest(HttpRequest request, ParameterInfo[] parameters)
    {
      if (parameters.IsNullOrEmpty())
      {
        return null;
      }
      int length = parameters.Length;
      object[] realParameters = new object[length];
      for (int i = 0; i  length; i++)
      {
        ParameterInfo pi = parameters[i];
        Type piType = pi.ParameterType.GetRealType();
        object value = null;
        if (piType.IsValueType())
        {
          //值類型
          value = ModelUtil.GetValue(request, pi.Name, piType);
          value = value ?? Activator.CreateInstance(piType);
        }
        else if (piType.IsClass)
        {
          //引用類型
          object model = ModelUtil.CreateModel(piType);
          ModelUtil.FillModelByRequest(request, pi.Name, piType, model);
          value = model;
        }
        else
        {
          throw new NotSupportedException(pi.Name + " 參數(shù)不被支持");
        }
        realParameters[i] = value;
      }
      return realParameters;
    }

  ModelUtil會(huì)從Http Request獲取參數(shù),并進(jìn)行類型轉(zhuǎn)換處理:

  

 internal static class ModelUtil
  {
    /// summary>
    /// 緩存構(gòu)造函數(shù)
    /// /summary>
    private static Hashtable constructorTable = Hashtable.Synchronized(new Hashtable());
 
    /// summary>
    /// 根據(jù)名稱從HttpRequest獲取值
    /// /summary>
    /// param name="request">HttpRequest/param>
    /// param name="name">鍵名稱/param>
    /// param name="type">參數(shù)類型/param>
    /// returns>/returns>
    public static object GetValue(HttpRequest request, string name, Type type)
    {
      string[] values = null;
      if (string.Compare(request.RequestType, "POST", true) == 0)
      {
        values = request.Form.GetValues(name);
      }
      else
      {
        values = request.QueryString.GetValues(name);
      }
      if (values.IsNullOrEmpty())
      {
        return null;
      }
      string data = values.Length == 1 ? values[0] : string.Join(",", values);
      return Convert.ChangeType(data, type);
    }

    /// summary>
    /// 創(chuàng)建實(shí)例對(duì)象
    /// /summary>
    /// param name="type">實(shí)例類型/param>
    /// returns>/returns>
    public static object CreateModel(Type type)
    {
      if (type == null)
      {
        throw new ArgumentNullException("type");
      }
      Funcobject> func = constructorTable[type.AssemblyQualifiedName] as Funcobject>;
      if (func == null)
      {  
        func = ReflectionUtil.GetConstructorDelegate(type);
        constructorTable[type.AssemblyQualifiedName] = func;
      }
      if (func != null)
      {
        return func();
      }
      return null;
    }

    /// summary>
    /// 填充模型
    /// /summary>
    /// param name="request">HttpRequest/param>
    /// param name="name">鍵名稱/param>
    /// param name="prefix">參數(shù)類型/param>
    /// parparam name="model">實(shí)例對(duì)象/parparam>
    public static void FillModelByRequest(HttpRequest request, string name, Type type, object model)
    {
      if (model == null)
      {
        return;
      }
      IEnumerableDataMember> members = DataMemberManager.GetDataMember(type);
      if (members.IsNullOrEmpty())
      {
        return;
      }
      object value = null;
      foreach (DataMember member in members)
      {
        value = GetValue(request, string.Format("{0}.{1}", name, member.Name), member.MemberType);
        value = value ?? GetValue(request, member.Name, member.MemberType);
        member.SetValue(model, value);
      }
    }
  }

  如果是引用類型,需要通過(guò)構(gòu)造函數(shù)創(chuàng)建對(duì)象,像前面用于,這里我們也用Expression來(lái)構(gòu)建一個(gè)Funcobject>類型的委托來(lái)優(yōu)化,它調(diào)用了ReflectionUtil.GetConstructorDelegate方法。實(shí)現(xiàn)如下:

  /// summary>
    /// 獲取構(gòu)造函數(shù)委托
    /// /summary>
    /// param name="type">實(shí)例類型/param>
    /// returns>/returns>
    public static Funcobject> GetConstructorDelegate(Type type)
    {
      if (type == null)
      {
        throw new ArgumentNullException("type");
      }
      ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
      if (ci == null)
      {
        throw new MissingMemberException("類型必須有一個(gè)無(wú)參public構(gòu)造函數(shù)!");
      }
      NewExpression newExp = Expression.New(type);
      ExpressionFuncobject>> lambda = Expression.LambdaFuncobject>>(newExp);
      return lambda.Compile();
    }

  最后再輸出結(jié)果時(shí),如果是Get請(qǐng)求,并且需要緩存,我們還需要設(shè)置一下Response.Cache。如下:

    private static void EndRequest(HttpContext context, object data, int outPutCache, ContentType contentType)
    {
      HttpResponse response = context.Response;
      if (outPutCache != 0)
      {
        if (string.Compare(context.Request.HttpMethod, "GET", true) == 0)
        {
          if (outPutCache > 0)
          {
            response.Cache.SetCacheability(HttpCacheability.Public);
            response.Cache.SetMaxAge(new TimeSpan(0, 0, outPutCache));
            response.Cache.SetExpires(DateTime.Now.AddSeconds(outPutCache));
          }
          else
          {
            response.Cache.SetCacheability(HttpCacheability.NoCache);
            response.Cache.SetNoStore();
          }
        }
      }
      response.ContentType = GetContentType(contentType);
      response.ContentEncoding = System.Text.Encoding.UTF8;
      if (data != null)
      {
        response.Write(data);
      }
      response.End();
    }

總結(jié)
  現(xiàn)在不管我們前臺(tái)用什么腳本庫(kù),只要按照約定就可以調(diào)用標(biāo)記方法。上面已經(jīng)介紹了組件的核心部分,您也可以按照自己的想法進(jìn)行擴(kuò)展,也歡迎共同學(xué)習(xí)交流。

您可能感興趣的文章:
  • Ajax核心XMLHTTP組件資料
  • 編寫輕量ajax組件02--淺析AjaxPro
  • 編寫輕量ajax組件01-與webform平臺(tái)上的各種實(shí)現(xiàn)方式比較

標(biāo)簽:林芝 舟山 邢臺(tái) 內(nèi)蒙古 鄭州 遼源 海南 洛陽(yáng)

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《編寫輕量ajax組件第三篇實(shí)現(xiàn)》,本文關(guān)鍵詞  編寫,輕量,ajax,組件,第三,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《編寫輕量ajax組件第三篇實(shí)現(xiàn)》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于編寫輕量ajax組件第三篇實(shí)現(xiàn)的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章