Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
277 views
in Technique[技术] by (71.8m points)

c# - Entity Framework Include() is not working within complex query

Consider following LINQ query:

var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
           select new
           {
                ItemProp1 = obj,
                ItemProp2 = obj.NavProp2.Any(n => n.Active)
           }).SingleOrDefault();

This runs as expected, but item.ItemProp1.NavProp1 is NULL. As it explains here this is because of the query actually changes after using Include(). but the question is what is the solution with this situation?

Edit:

When I change the query like this, every things works fine:

var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
           select obj).SingleOrDefault();

Regarding to this article I guess what the problem is... but the solution provided by author not working in my situation (because of using anonymous type in final select rather than entity type).

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As you mentioned, Include is only effective when the final result of the query consists of the entities that should include the Include-d navigation properties.

So in this case Include has effect:

var list = _db.SampleEntity.Include(s => s.NavProp1).ToList();

The SQL query will contain a JOIN and each SampleEntity will have its NavProp1 loaded.

In this case it has no effect:

var list = _db.SampleEntity.Include(s => s.NavProp1)
            .Select(s => new { s })
            .ToList();

The SQL query won't even contain a JOIN, EF completely ignores the Include.

If in the latter query you want the SampleEntitys to contain their NavProp1s you can do:

var list = _db.SampleEntity
            .Select(s => new { s, s.NavProp1 })
            .ToList();

Now Entity Framework has fetched SampleEntitys and NavProp1 entities from the database separately, but it glues them together by a process called relationship fixup. As you see, the Include is not necessary to make this happen.

However, if Navprop1 is a collection, you'll notice that...

var navprop1 = list.First().s.Navprop1;

...will still execute a query to fetch Navprop1 by lazy loading. Why is that?

While relationship fixup does fill Navprop1 properties, it doesn't mark them as loaded. This only happens when Include loaded the properties. So now we have SampleEntity all having their Navprop1s, but you can't access them without triggering lazy loading. The only thing you can do to prevent this is

_db.Configuration.LazyLoadingEnabled = false;
var navprop1 = list.First().s.Navprop1;

(or by preventing lazy loading by disabling proxy creation or by not making Navprop1 virtual.)

Now you'll get Navprop1 without a new query.

For reference navigation properties this doesn't apply, lazy loading isn't triggered when it's enabled.

In Entity Framework core, things have changed drastically in this area. A query like _db.SampleEntity.Include(s => s.NavProp1).Select(s => new { s }) will now include NavProp1 in the end result. EF-core is smarter in looking for "Includable" entities in the end result. Therefore, we won't feel inclined to shape a query like Select(s => new { s, s.NavProp1 }) in order to populate the navigation property. Be aware though, that if we use such a query without Include, lazy loading will still be triggered when s.NavProp1 is accessed.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...