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

时间:2011-08-26 14:38:07

标签: c# .net entity-framework

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

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 Framework数据提供程序错误1025”。我正在使用EntityFramework.4.1.10715.0

谁能告诉我为什么?

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

2 个答案:

答案 0 :(得分:2)

问题在于,您的内部Where已经在Where的“外部ctx.Foos”的上下文中 - 因此对Filter.GetPredicate()的调用最终会成为表达式树,实体框架不知道它意味着什么或如何将其转换为SQL。

这就是它发生的原因......我现在不确定最好的解决方案,除非你可以将谓词提取到你需要的单独变量中。

(另外,使用Any(...)通常比...Count() > 0更具表现力 - 而在LINQ to Objects中它可以产生巨大的差异。)

答案 1 :(得分:1)

你已经暗示了这一点,但只是为了向未来的读者说清楚。 您可以使用该函数在您执行时生成谓词,但您需要将表达式存储在函数中的中间表达式中。

即。改变:

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

我仍然对这种情况发生的原因感到困惑。