2016-10-24 39 views
1

我正在創建一個將OData表達式轉換爲.NET表達式樹(Expression<Func<T, bool>>)的高級搜索。我將這個表達式作爲謂詞傳遞給我的EF6 .Select()方法,並且按預期工作。使用.Set()而不是.Set來設置基於類型的DbSet選擇<T>()

但是,在實現此功能時,我發現LINQ方法僅適用於IQueryable<TSource>。這適用於.Set<T>(),但我不會在運行時知道該類型,所以我需要使用.Set()

我大概可以使用反射來調用.Set<T>()然後調用它,但是這看起來有點像黑客,所以我寧願直接通過.Set()直接執行它(如果可能的話)。

+0

如何創建'表達 >'如果你不知道'T'? –

+0

@IvanStoev您可以使用類型創建表達式。 – oscilatingcretin

+0

像返回'LambdaExpression'的非泛型'Expression.Lambda'?你想將它綁定到「Where」? –

回答

1

如果我理解正確的話,你有LambdaExpression,而不是Expression<Func<T, bool>>,你想用它作爲WhereIQueryable(其中DbSet類實現),而不是IQueryable<T>

您只需要知道IQueryable<T>擴展方法只需將MethodCallExpression發送到查詢表達式樹中對應的Queryable方法即可。

例如,要效仿IQueryableWhereSelect您可以使用下面的自定義擴展方法:

public static class QueryableExtensions 
{ 
    public static IQueryable Where(this IQueryable source, LambdaExpression predicate) 
    { 
     var expression = Expression.Call(
      typeof(Queryable), "Where", 
      new Type[] { source.ElementType }, 
      source.Expression, Expression.Quote(predicate)); 
     return source.Provider.CreateQuery(expression); 
    } 

    public static IQueryable Select(this IQueryable source, LambdaExpression selector) 
    { 
     var expression = Expression.Call(
      typeof(Queryable), "Select", 
      new Type[] { source.ElementType, selector.Body.Type }, 
      source.Expression, Expression.Quote(selector)); 
     return source.Provider.CreateQuery(expression); 
    } 
} 

你可以做需要的其他Queryable方法類似。

更新:既然你是在有趣,這裏是一個使用表達式的原型獲得泛型方法定義,並從它構建的通用方法的一個例子:

public static class QueryableExtensions 
{ 
    static MethodInfo QueryableMethod<T>(this Expression<Func<IQueryable<object>, T>> prototype, params Type[] types) 
    { 
     return ((MethodCallExpression)prototype.Body).Method 
      .GetGenericMethodDefinition() 
      .MakeGenericMethod(types); 
    } 

    public static IQueryable Where(this IQueryable source, LambdaExpression predicate) 
    { 
     var expression = Expression.Call(
      QueryableMethod(q => q.Where(x => true), source.ElementType), 
      source.Expression, Expression.Quote(predicate)); 
     return source.Provider.CreateQuery(expression); 
    } 

    public static IQueryable Select(this IQueryable source, LambdaExpression selector) 
    { 
     var expression = Expression.Call(
      QueryableMethod(q => q.Select(x => 1), source.ElementType, selector.Body.Type), 
      source.Expression, Expression.Quote(selector)); 
     return source.Provider.CreateQuery(expression); 
    } 
} 
+0

太棒了。完美的作品。任何想法如何使用表達式獲取LINQ方法的MethodInfo,因此您不必使用字符串?我嘗試了幾個小時,但掛上了匹配使用泛型類型的重載簽名(通常,您只能傳遞默認類型,但我不知道如何使用泛型)。 – oscilatingcretin

+0

爲什麼要打擾MethodInfo - 方法的名稱不會改變,而且如果你在C#6上,你總是可以使用'nameof(Queryable.Select)' –

+0

我發誓我試過nameof,但它給了我一些問題。只是再試一次,它的工作原理,所以我做錯了什麼。同意,名字永遠不會改變,但我傾向於儘可能避免魔術串。再次感謝 – oscilatingcretin

相關問題