开发者

Unexpected behavior when using a predicate on nested entity lists

I have stumbled upon a riddle which I can't explain, maybe someone here will be able to. Here is a (rather lengthy but complete) code snippet:

public class Foo
{
    public int ID { get; set; }
    public string Name { get; se开发者_如何学Ct; }

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

The line that's commented out doesn't work - throws an exception at runtime - "Internal .NET Framework Data Provider error 1025.". I'm using EntityFramework.4.1.10715.0

Can anyone tell me why?

Side question: I'm looking for a way to keep filtering expressions that are used in several different classes.


The problem is that your inner Where is already within the context of an "outer Where" on ctx.Foos - so the call to Filter.GetPredicate() ends up as part of the expression tree, and the Entity Framework has no idea what it means or how to translate it into SQL.

That's why it's happening... I'm not sure of the best solution right now though, unless you can extract the predicate into a separate variable where you need it.

(As an aside, it's generally more expressive to use Any(...) than ...Count() > 0 - and in LINQ to Objects it can make a huge difference.)


You hinted towards this yourself already, but just to make it clear to future readers. You can use the function to generate the predicate as you're doing, but you need to store the expression in an intermediate expression within your function.

i.e. change:

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

into:

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

I'm still puzzled as to why this happens this way.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜