2011-05-25 77 views
0

我正在嘗試構建一個實用方法,它將使用反射一般地加載實體集合。這個想法是,使用該實用程序的程序員可以指定任何類型的實體,並且此方法將發現正確的EntityQuery,並根據他們請求的內容加載上下文。所以,我從用戶那裏收集了Entity類型和Where子句,現在我正試圖弄清楚如何調用該方法。以下是我有:如何使用反射來調用DomainService.Load方法?

public void Handle(LoadEntityQuery loadQuery, Action<LoadEntityQueryResult> reply) 
{ 
    foreach (var entry in loadQuery.Entities) 
    { 

     Type entityType = entry.Key; 
     Type _contextType = EmployeeJobsContext.Instance.GetType(); 

     MethodInfo _methodInfo = (from x in _contextType.GetMethods() 
           where x.ReturnType.BaseType == typeof(EntityQuery) 
           from y in x.ReturnType.GetGenericArguments() 
           where y == entityType 
           select x).FirstOrDefault(); 
     if (_methodInfo != null) 
     { 
      var query = _methodInfo.Invoke(EmployeeJobsContext.Instance, null); 

      var _loadMethods = from x in _contextType.GetMethods() 
           where x.Name == "Load" && 
            x.GetParameters().Length == 3 
           select x; 
      MethodInfo _loadMethod = null; 

      if (_loadMethods != null) 
      { 
       foreach (MethodInfo item in _loadMethods) 
       { 
        ParameterInfo[] _paramInfo = item.GetParameters(); 
        if (_paramInfo[0].ParameterType.BaseType == typeof(EntityQuery) && 
         _paramInfo[1].ParameterType.IsGenericType && 
         _paramInfo[1].ParameterType.GetGenericArguments().Length == 1 && 
         _paramInfo[1].ParameterType.GetGenericArguments()[0].BaseType == typeof(LoadOperation) && 
         _paramInfo[2].ParameterType == typeof(object)) 
        { 
         _loadMethod = item; 
         break; 
        } 
       } 
      } 

      MethodInfo _loadOpMethod = this.GetType().GetMethod("LoadOperationResult"); 
      Delegate d = Delegate.CreateDelegate(typeof(LoadOpDel), _loadOpMethod); 

      if (_loadMethod != null) 
      { 
       object [] _params = new object[3]; 
       _params[0] = query; 
       _params[1] = d; 
       _params[2] = null; 

       _loadMethod = _loadMethod.MakeGenericMethod(entityType); 
       _loadMethod.Invoke(_context, _params); 
      } 
     }   
    } 
} 

public delegate void LoadOpDel(LoadOperation loadOp); 

public void LoadOperationResult (LoadOperation loadOp) 
{ 
    if (loadOp.HasError == true) 
    { 
     //reply(new LoadEntityQueryResult { Error = loadOp.Error.Message }); 
     loadOp.MarkErrorAsHandled(); 
    } 
} 

foreach循環迭代是一個字典>>,其中關鍵是實體類型和值是Where子句。代碼的第一部分是找到正確的EntityQuery方法並調用它來獲取實際查詢。然後它發現正確的負載超載(我知道,可能有更好的方法來找到方法:))這部分的代碼工作正常,我能夠發現正確的EntityQuery和Load方法。

對於LoadOperation,我想使用LoadOperationResult作爲我的委託方法。但是,當我嘗試運行此代碼時,我收到一個異常,指出委託類型和方法類型簽名不匹配。我很確定我的簽名是正確的,因爲如果我要直接調用Load並正常傳遞函數名稱作爲回調,則此代碼將正確執行。我對反射式編程非常熟悉,但是將泛型和動作回調投入到混合中,在這一點上略高於我的水平。我不知道我做錯了什麼,有沒有人對我有任何指點?我離開嗎?謝謝你的幫助!! Jason

+0

所以'_loadMethod.Invoke(_context,_params);'引發異常或者當你創建委託?我也可能會對此產生困惑,但是你不想在循環之外創建委託嗎? – 2011-05-25 01:28:44

+0

對不起,我沒有具體說明何時發生。當我創建委託時,我得到異常。另外,我需要在循環中創建委託,因爲集合中的每個實體都會調用加載操作。 – Rockdocta 2011-05-25 02:07:18

回答

0

我發現我並不需要使用反射來調用Load方法(否定爲代表的需要),而不是我直接通過創建基於實體類型的通用方法呼叫負載。以下是我想出了有興趣的人:

/// <summary> 
    /// The Action callback for the LoadEntityQuery handler. This callback is used to respond to the 
    /// LoadEntityQuery when all Load calls are complete. See the Handle method 
    /// </summary> 
    private Action<LoadEntityQueryResult> _reply = null; 

    /// <summary> 
    /// Accumulator used to determine when the last entity has been loaded 
    /// </summary> 
    private int EntityCount { get; set; } 

    /// <summary> 
    /// Collective error container for Errors from the LoadOperation. This is value is returned via 
    /// the _reply callback to the calling code. 
    /// </summary> 
    private List<Exception> Errors = null; 

    public void Handle(LoadEntityQuery loadQuery, Action<LoadEntityQueryResult> reply) 
    { 
     _reply = reply; 
     Errors = new List<Exception>(); 
     EntityCount = loadQuery.Entities.Count(); 

     MethodInfo _loadOpMethod = this.GetType().GetMethod("Load", BindingFlags.NonPublic | BindingFlags.Instance); 
     int _entityCount = loadQuery.Entities.Count(); 

     foreach (var entry in loadQuery.Entities) 
     { 
      Type entityType = entry.Key; 
      Type _contextType = EmployeeJobsContext.Instance.GetType(); 

      MethodInfo _methodInfo = (from x in _contextType.GetMethods() 
             where x.ReturnType.BaseType == typeof(EntityQuery) 
             from y in x.ReturnType.GetGenericArguments() 
             where y == entityType 
             select x).FirstOrDefault(); 
      if (_methodInfo != null) 
      { 
       var query = _methodInfo.Invoke(EmployeeJobsContext.Instance, null); 
       MethodInfo _typedLoadOpMethod = _loadOpMethod.MakeGenericMethod(new Type[] { entityType }); 

       _typedLoadOpMethod.Invoke(this, new[] { query, entry.Value}); 
      } 
     } 
    } 

    private void Load<T>(EntityQuery<T> query, Expression<Func<T, bool>> where) where T: Entity 
    { 
     if (where != null) 
      query = query.Where(where); 

     EmployeeJobsContext.Instance.Load(query, (loadOp) => 
      { 
       EntityCount--; 
       if (loadOp.HasError) 
       { 
        Errors.Add(loadOp.Error); 
        loadOp.MarkErrorAsHandled(); 
       } 

       if (EntityCount == 0) 
        _reply(new LoadEntityQueryResult { ErrorList = Errors }); 

      }, null); 
    } 

裝載操作的處理函數觀察過去實體加載完成,然後響應客戶端加載完成(有任何錯誤,他們應該發生)。

0

不知道你正在使用的類的其他內容,我無法真正測試我的解決方案,但是當我將代理創建從for循環中取出時,我可以使其工作。我將您的目標方法更改爲靜態:

public static void LoadOperationResult(LoadOperation loadOp) 

並且創建委託時沒有問題。

我不是,比方說,在這方面特別勝任,但我認爲你想創建一次委託,並在需要時重新使用它。爲什麼一直反覆創建它?

+0

嗨,史蒂夫,感謝您的意見。這是一個很好的觀點,我只需要創建一次委託,然後重複使用它,那麼您創建它的位置/時間是什麼?你還以同樣的方式將它與我的_params對象相關聯嗎?謝謝! – Rockdocta 2011-05-26 12:15:53

+0

@Rockdocta我在for循環之前創建了它。我沒有真正使用它,因爲我沒有其他項目。我運行了代碼,但從來沒有一個非null的_methodInfo變量,所以我沒有真正測試委託。但它被創建。 – 2011-05-26 12:25:20

0

即使Action<LoadOperation>LoadOpDel具有相同的簽名,您不能在它們之間隱式轉換。在C#類型的強制中有時會使這看起來不是真的,但是如果你使用反射類型的強制顯然不會發揮它的魔力。