58
IQueryable<Organization> query = context.Organizations;

Func<Reservation, bool> predicate = r => !r.IsDeleted;

query.Select(o => new { 
    Reservations = o.Reservations.Where(predicate)
}).ToList();

this query throws "Internal .NET Framework Data Provider error 1025" exception but the query below does not.

query.Select(o => new { 
    Reservations = o.Reservations.Where( r => !r.IsDeleted)
}).ToList();

I need to use the first one because I need to check a few if statements for constructing the right predicate. I know that I can not use if statements in this circumstance that is why I pass a delegate as parameter.

How can I make the first query work?

Ilia Anastassov
  • 368
  • 8
  • 16
Freshblood
  • 6,285
  • 10
  • 59
  • 96
  • 2
    I'm not entirely sure if that's the reason (or even the right direction), but `Linq2Objects` operates on Expression Trees, which means you usually pass `Expression> predicate` and not the naked `Func predicate`. You can always try that. :) – Patryk Ćwiek Aug 16 '12 at 15:28
  • 1
    It sounds like you may want a predicate builder. Take a look at this question. http://stackoverflow.com/questions/9184942/nesting-or-using-linq-predicatebuilder – tsells Aug 16 '12 at 16:42
  • @Trustme-I'maDoctor: You're wrong - LINQ to *Objects* uses delegates. It's LINQ to SQL etc that use expression trees, as they need to translate the query into SQL. LINQ to Objects can just execute the predicates, projects etc directly. – Jon Skeet Aug 21 '12 at 06:02
  • @JonSkeet Absolutely, but good distinction. I sometimes forget that not all collections that you use LINQ on are somehow database-backed :) On a completely unrelated side note, your book is a great read, guess I'll be doing more of a reading than anything else in the next few weeks to come. – Patryk Ćwiek Aug 21 '12 at 08:17
  • 1
    @JonSkeet - I'm getting exactly the same behavior as the OP, except I've abstracted the predicate to a static method, since I'm using it in lots of places. I do not want to copy and paste the predicate into all places where it's being used. What's the solution? – Shaul Behr Dec 13 '12 at 12:59
  • @Trustme-I'maDoctor - I think you should get the bounty here. Please post your answer below... – Shaul Behr Dec 13 '12 at 15:52
  • Yeah, it's rediculous that we have to copy/paste the expression everywhere. The method accepts a Func as a lambda expression. It makes no sense that we can't pull that out to a variable and then pass the variable in. I'm running into the issue where the framework developers can't speak English and think Any is the inverse of All, when in reality "Any" means "one or more" and the opposite of that is "None". "All" is too generic because it leaves handling of the empty set ambiguous, and should really be split into two separate methods named "AnyAndAll" or "AllOrNone". – Triynko Sep 03 '15 at 17:48

5 Answers5

47

While the other answers are true, note that when trying to use it after a select statement one has to call AsQueryable() explicitly, otherwise the compiler will assume that we are trying to use IEnumerable methods, which expect a Func and not Expression<Func>.

This was probably the issue of the original poster, as otherwise the compiler will complain most of the time that it is looking for Expression<Func> and not Func.

Demo: The following will fail:

MyContext.MySet.Where(m => 
      m.SubCollection.Select(s => s.SubItem).Any(expr))
         .Load()

While the following will work:

MyContext.MySet.Where(m => 
      m.SubCollection.Select(s => s.SubItem).AsQueryable().Any(expr))
         .Load()
General Grievance
  • 4,555
  • 31
  • 31
  • 45
yoel halb
  • 12,188
  • 3
  • 57
  • 52
26

After creating the bounty (rats!), I found this answer, which solved my problem. (My problem involved a .Any() call, which is a little more complicated than this question...)

In short, here's your answer:

IQueryable<Organization> query = context.Organizations;

Expression<Func<Reservation, bool>> expr = r => !r.IsDeleted;

query.Select(o => new { Reservations = o.Reservations.Where(expr) })
  .ToList();

Read the referenced answer for an explanation of why you need the local variable expr, and you can't directly reference another method of return type Expression<Func<Reservation, bool>>.

Community
  • 1
  • 1
Shaul Behr
  • 36,951
  • 69
  • 249
  • 387
  • seems to me that this is the same as the advice provided by "trust me - I'm a doctor" on the 16'th. Maybe he should get the bounty? – Mike Beeler Dec 13 '12 at 15:08
21

Thanks for pinging me. I guess I was on the right track after all.

Anyway, to reiterate, LINQ to Entities (thanks to Jon Skeet for correcting me when I got mixed up in my own thought process in the comments) operates on Expression Trees; it allows for a projection to translate the lambda expression to SQL by the QueryProvider.

Regular Func<> works well for LINQ to Objects.

So in this case, when you're using the Entity Framework, any predicate passed to the EF's IQueryable has to be the Expression<Func<>>.

Patryk Ćwiek
  • 14,078
  • 3
  • 55
  • 76
  • 2
    I have been commented at upside about Func<> delegate and Expression<> parameters. If you look at again on this line of code Reservations = o.Reservations.Where(predicate) you will realize that o.Reservations is ICollection<> not IQueryable<> because it is just navigation property. Any Where extension method on it is IEnumerable<> extension. So it is right to pass Func<> delegate instead of Expression<> . – Freshblood Dec 18 '12 at 16:33
5

I just experienced this issue in a different scenario.

I have a static class full of Expression predicates which I can then combine or pass to an EF query. One of them was:

    public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
        IEnumerable<EventEnums.AttendeeStatus> statuses)
    {
        return ce => ce.Event.AttendeeStatuses
            .Where(a => a.ClientId == ce.Client.Id)
            .Select(a => a.Status.Value)
            .Any(statuses.Contains);
    }

This was throwing the 1025 error due to the Contains method group call. The entity framework expected an Expression and found a method group, which resulted in the error. Converting the code to use a lambda (which can be implicitly cast to an Expression) fixed the error

    public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
        IEnumerable<EventEnums.AttendeeStatus> statuses)
    {
        return ce => ce.Event.AttendeeStatuses
            .Where(a => a.ClientId == ce.Client.Id)
            .Select(a => a.Status.Value)
            .Any(x => statuses.Contains(x));
    }

Aside: I then simplified the expression to ce => ce.Event.AttendeeStatuses.Any(a => a.ClientId == ce.Client.Id && statuses.Contains(a.Status.Value));

Alex
  • 7,639
  • 3
  • 45
  • 58
0

Had a similar problem. Library of ViewModels that look like this:

public class TagViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }

    public static Expression<Func<SiteTag, TagViewModel>> Select = t => new TagViewModel
    {
        Id = t.Id,
        Name = t.Name,
    };

This works:

var tags = await db.Tags.Take(10).Select(TagViewModel.Select)
    .ToArrayAsync();

But, this won't compile:

var post = await db.Posts.Take(10)
    .Select(p => new {
        Post = p,
        Tags = p.Tags.Select(pt => pt.Tag).Select(TagViewModel.Select)
    })
    .ToArrayAsync();

Because the second .Select is a mess - the first one is actually called off of an ICollection, which is not IQueryable, so it consumes that first Expression as a plain Func, not Expression<Func.... That returns IEnumerable<..., as discussed on this page. So .AsQueryable() to the rescue:

var post = await db.Posts.Take(10)
    .Select(p => new {
        Post = p,
        Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
            .Select(TagViewModel.Select)
    })
    .ToArrayAsync();

But that creates a new, weirder problem: Either I get Internal Framework...Error 1025, or I get the post variable with a fully loaded .Post property, but the .Tags property has an EF proxy object that seems to be used for Lazy-Loading.

The solution is to control the return type of Tags, by ending use of the Anonymous class:

public class PostViewModel
{
    public Post Post { get; set; }
    public IEnumerable<TagViewModel> Tags { get; set; }

Now select into this and it all works:

var post = await db.Posts.Take(10)
    .Select(p => new PostViewModel {
        Post = p,
        Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
            .Select(TagViewModel.Select)
    })
    .ToArrayAsync();
Chris Moschini
  • 36,764
  • 19
  • 160
  • 190