2016-10-24 99 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

相关问题