What is the best practice for multiple “Include”-s in Entity Framework?

后端 未结 4 921
轻奢々
轻奢々 2020-12-30 04:41

Let\'s say we have four entities in data model: Categories, Books, Authors and BookPages. Also assume Categories-Books, Books-Authors and Books-BookPages relationships are o

4条回答
  •  情书的邮戳
    2020-12-30 05:18

    In db first approach, say you create BookStore.edmx and add the Category and Book entity and it generate context like public partial class BookStoreContext : DbContext then it is a simple good practice if you can add partial class like this:

    public partial class BookStoreContext
    {
        public IQueryable GetCategoriesWithBooks()
        {
            return Categories.Include(c => c.Books);
        }
    
        public IQueryable GetCategoriesWith(params string[] includeFields)
        {
            var categories = Categories.AsQueryable();
            foreach (string includeField in includeFields)
            {
                categories = categories.Include(includeField);
            }
            return categories;
        }
    
        // Just another example
        public IQueryable GetBooksWithAllDetails()
        {
            return Books
                .Include(c => c.Books.Authors)
                .Include(c => c.Books.Pages);
        }
    
        // yet another complex example
        public IQueryable GetNewBooks(/*...*/)
        {
            // probably you can pass sort by, tags filter etc in the parameter.
        }
    }
    

    Then you can use it like this:

    var category1 = db.CategoriesWithBooks()
                          .Where(c => c.Id = 5).SingleOrDefault();
    var category2 = db.CategoriesWith("Books.Pages", "Books.Authors")
                          .Where(c => c.Id = 5).SingleOrDefault(); // custom include
    

    Note:

    • You can read some of simple (so many complicated one out there) Repository pattern just to extend IDbSet Categories to group common Include and Where instead of using static CategoryHelper. So you can have IQueryable db.Categories.WithBooks()
    • You should not include all child entities in GetCategoryById because it does not self explain in the method name and that will cause performance issue if user of this method is not brother about Books entites.
    • Even though you not include all, if you use lazy loading, you still can have potential N+1 performance issue
    • If you have 1000 of Books better you page your load something like this db.Books.Where(b => b.CategoryId = categoryId).Skip(skip).Take(take).ToList() or even better you add the method above to be like this db.GetBooksByCategoryId(categoryId, skip, take)

    I myself prefer explicitly loading entities since I will 'aware' what is currently loaded but lazy loading is only useful if you have conditional loading children entities and should be used within a small scope of db context otherwise I can't control the db hit and how big the result.

提交回复
热议问题