EF LINQ include multiple and nested entities

2019-01-06 09:05发布

问题:

Ok, I have tri-leveled entities with the following hierarchy: Course -> Module -> Chapter

Here was the original EF LINQ statement:

Course course = db.Courses
                .Include(i => i.Modules.Select(s => s.Chapters))
                .Single(x => x.Id == id); 

Now, I want to include another entity called Lab which is associated with a course.

How do I include the Lab entity?

I tried the following but it didn't work:

Course course = db.Courses
                .Include(i => i.Modules.Select(s => s.Chapters) && i.Lab)
                .Single(x => x.Id == id); 

Any ideas on including the 2nd Entity?

Any piece of advise or information would be highly appreciated. Thanks!

回答1:

Have you tried just adding another Include:

Course course = db.Courses
                .Include(i => i.Modules.Select(s => s.Chapters))
                .Include(i => i.Lab)
                .Single(x => x.Id == id);

Your solution fails because Include doesn't take a boolean operator

Include(i => i.Modules.Select(s => s.Chapters) &&          i.Lab)
                           ^^^                  ^             ^ 
                          list           bool operator    other list

Update To learn more, download LinqPad and look through the samples. I think it is the quickest way to get familiar with Linq and Lambda.

As a start - the difference between Select and Include is that that with a Select you decide what you want to return (aka projection). The Include is a Eager Loading function, that tells Entity Framework that you want it to include data from other tables.

The Include syntax can also be in string. Like this:

           db.Courses
            .Include("Module.Chapter")
            .Include("Lab")
            .Single(x => x.Id == id);

But the samples in LinqPad explains this better.



回答2:

Include is a part of fluent interface, so you can write multiple Include statements each following other

 db.Courses.Include(i => i.Modules.Select(s => s.Chapters))
           .Include(i => i.Lab)
           .Single(x => x.Id == id); 


回答3:

You can also try

db.Courses.Include("Modules.Chapters").Single(c => c.Id == id);


回答4:

In Entity Framework Core (EF.core) you can use .ThenInclude for including next levels.

var blogs = context.Blogs
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
    .ToList();

More information: https://docs.microsoft.com/en-us/ef/core/querying/related-data

Note: Say you need multiple ThenInclude() on blog.Posts, just repeat the Include(blog => blog.Posts) and do another ThenInclude(post => post.Other).

var blogs = context.Blogs
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Other)
 .ToList();


回答5:

One may write an extension method like this:

    /// <summary>
    /// Includes an array of navigation properties for the specified query 
    /// </summary>
    /// <typeparam name="T">The type of the entity</typeparam>
    /// <param name="query">The query to include navigation properties for that</param>
    /// <param name="navProperties">The array of navigation properties to include</param>
    /// <returns></returns>
    public static IQueryable<T> Include<T>(this IQueryable<T> query, params string[] navProperties)
        where T : class
    {
        foreach (var navProperty in navProperties)
            query = query.Include(navProperty);

        return query;
    }

And use it like this even in a generic implementation:

string[] includedNavigationProperties = new string[] { "NavProp1.SubNavProp", "NavProp2" };

var query = context.Set<T>()
.Include(includedNavigationProperties);