2012-07-26 45 views
5

背景:如何使用反射來簡化Linq擴展方法調用?

我有ID的一個表中(作爲一個參數提供表名)返回的行Web服務超過一定ID(也供給作爲參數)更大。我們假設這些ID是連續的。

我使用LINQ to SQL的數據庫交互,所以我想回報新行作爲:

List<WhateverObject> 

因爲我們只知道在運行時的表名,我不能以正常方式使用Linq這使事情變得更加複雜。

問:

的代碼如下(和它的作品)。我怎樣才能簡化它?看起來過於複雜。

private object GetUpdateList(string tableName, int Id, DataClassesDataContext db) 
{ 
    PropertyInfo pi = db.GetType().GetProperty(tableName); 

    var table = pi.GetValue(db, null); 

    // Get type of object within the table. 
    Type genericType = table.GetType().GetGenericArguments()[0]; 

    // The Where method lives on the Enumerable type in System.Linq 
    var whereMethods = typeof(System.Linq.Enumerable) 
     .GetMethods(BindingFlags.Static | BindingFlags.Public) 
     .Where(mi => mi.Name == "Where"); 

    // There are actually 2 where methods - we want the one with 2 parameters 
    MethodInfo whereMethod = null; 
    foreach (var methodInfo in whereMethods) 
    { 
     var paramType = methodInfo.GetParameters()[1].ParameterType; 
     if (paramType.GetGenericArguments().Count() == 2) 
     { 
      // we are looking for Func<TSource, bool>, the other has 3 
      whereMethod = methodInfo; 
      break; 
     } 
    } 

    Func<object, bool> IdEquals = BuildEqFuncFor("Id", Id); 

    whereMethod = whereMethod.MakeGenericMethod(genericType); 
    var result = whereMethod.Invoke(table, new object[] { table, IdEquals }); 

    MethodInfo toListMethod = typeof(System.Linq.Enumerable).GetMethod("ToList").MakeGenericMethod(genericType); 
    return toListMethod.Invoke(result, new object[] { result }); 
} 

// Build lambda expression for use in Linq 
private static Func<object, bool> BuildEqFuncFor(string prop, object val) 
{ 
    // We know we are comparing integers here so cast them. 
    // There is probably a more general solution. 
    return t => (int)t.GetType().InvokeMember(prop, BindingFlags.GetProperty, null, t, null) > (int)val; 
} 

要拿出這種解決方案我不得不引用以下問題:

+0

不要拋棄你的問題,但你是否處於無法重構的情況?對於如此簡單的事情來說,這似乎有很多工作要做。它是否需要如此通用? – 2012-07-26 02:01:51

+0

你會建議什麼? – 2012-07-26 02:07:38

+0

只要使它強制輸入?傳遞表名和ID。從數據庫中選擇它並返回請求的行。返回一個'IEnumerable '並且在一天內調用它。 – 2012-07-26 02:12:49

回答

4

嘗試類似這樣:

private IList GetUpdateList(string tableName, int id, DataClassesDataContext db) 
{ 
    System.Reflection.PropertyInfo pi = db.GetType().GetProperty(tableName); 

    var table = pi.GetValue(db, null); 

    // Get type of object within the table. 
    Type genericType = table.GetType().GetGenericArguments()[0]; 

    var param = Expression.Parameter(genericType, "x"); 
    var predicateExpr = Expression.Lambda(
     Expression.GreaterThan(
      Expression.Property(param, "Id"), 
      Expression.Constant(id)), 
     param); 

    return this 
     .GetType() 
     .GetMethod("GetUpdateListGeneric") 
     .MakeGenericMethod(genericType) 
     .Invoke(this, new[] { table, predicateExpr }) as IList; 
} 

private IList<T> GetUpdateListGeneric<T>(
    Table<T> table, 
    Expression<Func<T, bool>> predicate) where T : class 
{ 
    return table.Where(predicate).ToList(); 
} 
+0

太棒了!我只需編輯一些東西(返回類型),並將Expression.Parameter拉到變量中,然後它就可以工作。 – 2012-07-26 03:51:27

+0

很高興幫助,並感謝修復。 – Jacob 2012-07-26 16:04:35