我偶然发现了一个我无法解释的谜语,也许这里有人可以。 这是一个(相当冗长但完整的)代码片段:
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
谁能告诉我为什么?
附带问题:我正在寻找一种方法来保持过滤几种不同类中使用的表达式。
答案 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();
我仍然对这种情况发生的原因感到困惑。