使用 .Set() 而不是 .Set<T>() 从基于类型的 DbSet 中选择

Selecting from type-based DbSet using .Set() instead of .Set<T>()

我正在创建一个将 OData 表达式转换为 .NET 表达式树的高级搜索 (Expression<Func<T, bool>>)。我将此表达式作为谓词传递到我的 EF6 .Select() 方法中,它按预期工作。

但是,在实现此功能时,我发现 LINQ 方法仅适用于 IQueryable<TSource>。这适用于 .Set<T>(),但我不知道运行时的类型,所以我需要使用 .Set()

我可能可以使用反射来调用 .Set<T>() 然后调用它,但这似乎有点 hack,所以如果可能的话我宁愿直接通过 .Set() 来完成.

如果我理解正确,你有 LambdaExpression 而不是 Expression<Func<T, bool>> 并且你想将它用作 Where 但在 IQueryable 上(DbSet class 实施)而不是 IQueryable<T>.

您只需要知道 IQueryable<T> 扩展方法只是将 MethodCallExpression 发送到查询表达式树中相应的 Queryable 方法。

例如,要在 IQueryable 上模拟 WhereSelect,您可以使用以下自定义扩展方法:

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);
    }
}