在嵌套实体列表上使用谓词时的意外行为

本文关键字:意外 谓词 实体 嵌套 列表 | 更新日期: 2023-09-27 18:06:40

我偶然发现了一个我无法解释的谜题,也许这里有人能解释。下面是一个(相当长但完整的)代码片段:

public class Foo
{
    public int ID { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Foo> InnerFoo { get; set; }
}
public class AppContext : DbContext
{
    public IDbSet<Foo> Foos { get; set; }
}
public class Initializer : DropCreateDatabaseAlways<AppContext>
{
    protected override void Seed(AppContext context)
    {
        var list = new List<Foo>
                       {
                           new Foo {Name = "one", InnerFoo = new List<Foo>{new Foo {Name = "Four"}}},
                           new Foo {Name = "two"},
                           new Foo {Name = "three"},
                       };
        list.ForEach(f => context.Foos.Add(f));
    }
}
public class Filter
{
    public static Expression<Func<Foo, bool>> GetPredicate()
    {
        return p => p.Name != null && p.Name.Length > 3;
    }
}
class Program
{
    static void Main(string[] args)
    {
        Database.SetInitializer(new Initializer());
        using (var ctx = new AppContext())
        {
            var predicate = Filter.GetPredicate();
            var list = ctx.Foos.Where(f => f.InnerFoo.AsQueryable().Where(predicate).Count() > 0).ToList(); // this works
            // var list = ctx.Foos.Where(f => f.InnerFoo.AsQueryable().Where(Filter.GetPredicate()).Count() > 0).ToList(); // this doesn't
            foreach (var s in list)
            {
                Console.WriteLine(s.Name);
            }
        }
    }
}

被注释掉的那行不能工作——在运行时抛出一个异常——"内部。net框架数据提供程序错误1025."我正在使用EntityFramework.4.1.10715.0

谁能告诉我为什么?

附带问题:我正在寻找一种方法来保持在几个不同的类中使用的过滤表达式。

在嵌套实体列表上使用谓词时的意外行为

问题是您的内部Where已经在ctx.Foos上的"外部Where"的上下文中-因此对Filter.GetPredicate()的调用最终作为表达式树的一部分,并且实体框架不知道它意味着什么或如何将其翻译成SQL。

这就是为什么会发生…我不确定现在最好的解决方案,除非你可以提取谓词到一个单独的变量,你需要它。

(顺便说一句,使用Any(...)通常比使用...Count() > 0更有表现力——在LINQ to Objects中,它可以产生巨大的差异。)

你自己已经暗示了这一点,但只是为了让未来的读者清楚。您可以像前面那样使用函数来生成谓词,但是需要将表达式存储在函数中的一个中间表达式中。

。变化:

var list = ctx.Foos.Where(f => f.InnerFoo.AsQueryable().Where(Filter.GetPredicate()).Count() > 0).ToList();

为:

var pred = Filter.GetPredicate();
var list = ctx.Foos.Where(f => f.InnerFoo.AsQueryable().Where(pred).Count() > 0).ToList();

我还是不明白为什么会这样。