实体框架中多个“包含"的最佳实践是什么? [英] What is the best practice for multiple "Include"-s in Entity Framework?

查看:24
本文介绍了实体框架中多个“包含"的最佳实践是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们在数据模型中有四个实体:类别、书籍、作者和书页.还假设 Categories-Books、Books-Authors 和 Books-BookPages 关系是一对多的.

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 one-to-many.

如果从数据库中检索类别实体实例——包括Books"、Books.BookPages"和Books.Authors"——这将成为一个严重的性能问题.此外,不包括它们将导致对象引用未设置为对象的实例"异常.

If a category entity instance is retrieved from database - including "Books", "Books.BookPages" and "Books.Authors" - this will become a serious performance issue. Moreover, not including them will result in "Object reference is not set to an instance of an object" exception.

使用多个 Include 方法调用的最佳实践是什么?

What is the best practice for using multiple Include method calls?

  • 编写一个方法 GetCategoryById 并在其中包含所有项目(性能问题)
  • 编写一个方法 GetCategoryById 并发送要包含的关系列表(也许,但似乎仍然不够优雅)
  • 编写 GetCategoryByIdWithBooks、GetCategoryByIdWithBooksAndBooksPages 和 GetCategoryByIdWithBooksAndAuthors 等方法(不实用)

编辑:第二个选项我的意思是这样的:

EDIT: By second option I meant something like this:

public static Category GetCategoryById(ModelEntities db, int categoryId, params string[] includeFields)
{
    var categories = db.Categories;

    foreach (string includeField in includeFields)
    {
        categories = categories.Include(includeField);
    }

    return categories.SingleOrDefault(i => i.CategoryId == categoryId);
}

调用时我们需要这样的代码:

When calling we need a code like this:

Category theCategory1 = CategoryHelper.GetCategoryById(db, 5, "Books");
Category theCategory2 = CategoryHelper.GetCategoryById(db, 5, "Books", "Books.Pages");
Category theCategory3 = CategoryHelper.GetCategoryById(db, 5, "Books", "Books.Authors");
Category theCategory4 = CategoryHelper.GetCategoryById(db, 5, "Books", "Books.Pages", "Books.Authors");

这种方法有什么明显的缺点吗?

Are there any distinct disadvantages of this approach?

推荐答案

编写一个方法 GetCategoryById 并发送一个要包含的关系列表(也许,但似乎仍然不够优雅)

Write a single method GetCategoryById and send a list of relationships to include (maybe, but still seems not elegant enough)

编写 GetCategoryByIdWithBooks、GetCategoryByIdWithBooksAndBooksPages 和 GetCategoryByIdWithBooksAndAuthors 等方法(不实用)

Write methods like GetCategoryByIdWithBooks, GetCategoryByIdWithBooksAndBooksPages and GetCategoryByIdWithBooksAndAuthors (not practical)

这两者的组合目前是我的方法.知道我想为每个上下文包含哪些属性,所以我宁愿手动编码它们(正如您自己所说,延迟加载并不总是一种选择,如果是,您将从数据模型映射到 DTO 时,重复相同的重复 Include() 类似语法.

A combination of these two is currently my approach. I know what properties I want to include for each context, so I rather hand-code them (as you said yourself, lazy-loading isn't always an option, and if it is, you'll repeat the same repetitive Include()-like syntax when mapping from data models to DTO's).

这种分离使您更加仔细地考虑要公开什么数据集,因为像这样的数据访问代码通常隐藏在服务之下.

This separation causes you to think harder about what datasets you want to expose, given data-access-code like this is usually hidden beneath a service.

通过使用包含虚拟方法的基类,您可以重写以运行所需的Include()s:

By utilizing a base class containing a virtual method you can override to run the required Include()s:

using System.Data.Entity;

public class DataAccessBase<T>
{
    // For example redirect this to a DbContext.Set<T>().
    public IQueryable<T> DataSet { get; private set; }

    public IQueryable<T> Include(Func<IQueryable<T>, IQueryable<T>> include = null)
    {
        if (include == null)
        {
            // If omitted, apply the default Include() method 
            // (will call overridden Include() when it exists) 
            include = Include;
        }
        
        return include(DataSet);
    }
    
    public virtual IQueryable<T> Include(IQueryable<T> entities)
    {
        // provide optional entities.Include(f => f.Foo) that must be included for all entities
        return entities;
    }
}

然后您可以按原样实例化和使用此类,或扩展它:

You can then instantiate and use this class as-is, or extend it:

using System.Data.Entity;

public class BookAccess : DataAccessBase<Book>
{
    // Overridden to specify Include()s to be run for each book
    public override IQueryable<Book> Include(IQueryable<Book> entities)
    {
        return base.Include(entities)
                   .Include(e => e.Author);
    }
    
    // A separate Include()-method
    private IQueryable<Book> IncludePages(IQueryable<Book> entities)
    {
        return entities.Include(e => e.Pages);
    }
    
    // Access this method from the outside to retrieve all pages from each book
    public IEnumerable<Book> GetBooksWithPages()
    {
        var books = Include(IncludePages);
    }
}

现在您可以实例化一个 BookAccess 并在其上调用方法:

Now you can instantiate a BookAccess and call methods on it:

var bookAccess = new BookAccess();

var allBooksWithoutNavigationProperties = bookAccess.DataSet;
var allBooksWithAuthors = bookAccess.Include();
var allBooksWithAuthorsAndPages = bookAccess.GetBooksWithPages();

在您的情况下,您可能希望为集合的每个视图创建单独的 IncludePagesGetBooksWithPages 类似的方法对.或者只是将其编写为一种方法,IncludePages 方法的存在是为了可重用.

In your case, you might want to create separate IncludePages and GetBooksWithPages-alike method pairs for each view of your collection. Or just write it as one method, the IncludePages method exists for reusability.

您可以按照自己喜欢的方式链接这些方法,因为它们中的每一个(以及实体框架的 Include() 扩展方法)都返回另一个 IQueryable.

You can chain these methods all the way you like, since each of them (as well as Entity Framework's Include() extension method) returns yet another IQueryable<T>.

这篇关于实体框架中多个“包含"的最佳实践是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆