如何在Linq to Entity Framework中使用Func表达式?

10

我正在尝试编写一个Linq到实体扩展方法,它需要一个Func来选择一个属性Id并将其与一组Ids进行比较。

public class A
{
    public int AId { get; set; }
}

public class B
{
    public int BId { get; set; }
}

Extension Method

public static IQueryable<T> WithId<T>(this IQueryable<T> entities,
    Func<T, int> selector, IList<int> ids)
    {
        Expression<Func<T, bool>> expression = x => ids.Contains(selector(x));
        return entities.Where(expression); // error here (when evaluated)
    }

调用方法

var ids = new List<int> { 1, 2, 3 };
DbContext.EntityAs.WithId(e => e.AId, ids);
DbContext.EntityBs.WithId(e => e.BId, ids);

我遇到的问题是在 Entity Framework 中无法调用该函数。
如何使用属性选择器(Func)评估查询?

你可以在EF查询中调用的代码范围受限于它仍然需要被翻译成SQL。在你的情况下,EF不知道如何自动翻译一个IList。 - Sten Petrov
我不确定你说的是对还是错。DbContext.EntityAs.Where(e => ids.Contains(e.Id)) 已经被EF正确地翻译了。我只是在尝试创建一个可重复使用的函数,以便我可以定义要选择哪个属性。 - David
因为 EF 知道如何在可枚举的情况下执行 select x where x in (1,2,3) 或在另一个实体关系的情况下执行 select x where x in (select y)。在您的情况下,EF 需要编译类似于 select x where x in (select y where F(y) in (F(1),F(2),...)) 的内容。虽然手动完成这个过程是可能的,但 EF 目前还不支持这种情况。 - Sten Petrov
它应该只是评估选择F(y)在(1,2,3)中的x,其中F(y)将被评估为x.AId或x.BId?有没有办法在表达式树中手动构建它? - David
1个回答

21

您需要传递一个 Expression<Func<T, int>> 而不是一个 Func<T, int>,并自己构建完整的表达式。以下代码可以解决问题:

public static IQueryable<T> WithId<T>(this IQueryable<T> entities,
    Expression<Func<T, int>> propertySelector, ICollection<int> ids)
{
    var property =
        (PropertyInfo)((MemberExpression)propertySelector.Body).Member;

    ParameterExpression parameter = Expression.Parameter(typeof(T));

    var expression = Expression.Lambda<Func<T, bool>>(
        Expression.Call(
            Expression.Constant(ids),
            typeof(ICollection<int>).GetMethod("Contains"), 
            Expression.Property(parameter, property)), 
        parameter);

    return entities.Where(expression);
}

当你在使用O/RM时想要保持代码DRY(Don't Repeat Yourself),你通常需要操作表达式树。下面是另一个有趣的例子


太棒了。我正在尝试从http://blogs.msdn.com/b/miah/archive/2009/02/06/dynamic-expression-trees.aspx和https://dev59.com/vEfRa4cB1Zd3GeqP9HJF构建表达式树,但无法弄清如何构建Collection/List包含。谢谢! - David
4
我来告诉你一个小秘密:我只是编写了LINQ查询,然后编译并打开Reflector查看C#编译器生成的内容。你也可以在调试器中看到这些信息,但Reflector更容易使用。 - Steven
你如何在propertySelector中使用子属性,例如x => x.Child.Id?我尝试创建一个子类型的参数,并在调用Expression.Call时使用它,但它无法正确地评估表达式。理想情况下,我只想得到SQL输出,如WHERE x.Child.Id in (1,2,3)。 - David
@DavidLiddle:看一下这篇博客文章。我认为那个回答了你的问题。 - Steven
1
@hbob 将表达式包装在 Expression.Not 中。 - Steven
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接