Chris K
Chris K

Reputation: 550

Override lazy loading on anonymous type with LINQ to entity

I have three entities generated by Entity Framework. One is event and this contains navigation properties called frogs and user_bookings. I posted a related question before about performing a sub-query which seems to work but it prevents me overriding lazy loading of a property.

var evts = from evt in context.events.Include("frogs")
           where evt.event_id < 10
           select evt;

This works - the navigation property frogs gets loaded.

However, when I alter the LINQ to this:

var evts = from evt in context.events.Include("frogs")
           where evt.event_id < 10
           select new
           {
               Event = evt,
               HasBooked = evt.user_bookings.Any(x => x.user_id == 1)
           };

I get an error trying to access the frogs because the ObjectContext no longer exists. I tried removing virtual from the class definition for the event class, but this just results in an empty list of frogs, when they are definitely there!

Upvotes: 1

Views: 460

Answers (1)

Gert Arnold
Gert Arnold

Reputation: 109099

This is by design. Include is ignored when the query result is a projection, even when the projection contains an entity that could contain the Included properties.

I don't know why EF implemented it this way. If the projection doesn't contain any entities, but is just some type (anonymous or not), there is no Include target, so ignoring it makes sense. But if the projection does contain an Include target (Event in your case) it seems to me that they could have decided to make that work. But, well, they didn't.

Maybe it's because the rules when Include actually has an effect are less obvious than you might expect. In your case, the shape of the query changes after the Include, so it's ignored.

You could work around this by also querying the frogs:

from evt in context.events.Include("frogs")
where evt.event_id < 10
select new
{
   Event = evt,
   Frogs = evt.frogs,
   HasBooked = evt.user_bookings.Any(x => x.user_id == 1)
};

Now each Event will also have its frogs collection filled (because of relationship fixup). But there are two gotchas. The collections are not marked as loaded, so -

  • if lazy load can occur afterwards, it will occur, making the initial load useless.
  • if lazy loading can't occur any more (because the context is disposed) you will get an exception.

This means that to make this work you have to disable lazy loading.

Upvotes: 3

Related Questions