什么是多&QUOT的最佳做法;包括"在实体框架-s? [英] What is the best practice for multiple "Include"-s in Entity Framework?

查看:133
本文介绍了什么是多&QUOT的最佳做法;包括"在实体框架-s?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

比方说,我们在数据模型的四个实体:分类,书籍,作者和BookPages。此外,假设分类书籍,书籍,作者和图书,BookPages关系是一到多。

如果一个类别实体实例从数据库检索 - 包括书,Books.BookPages和Books.Authors - 这将成为一个严​​重的性能问题。此外,不包括它们将导致对象参考未被设置为一个对象的一个​​实例异常。

什么是使用多个包括方法调用的最佳做法?


  • 写了一个方法GetCategoryById,包括里面的所有物品(性能问题)

  • 写了一个方法GetCategoryById和发送的关系的列表,包括(也许,但似乎仍然没有足够的优雅)

  • 写入方法,如GetCategoryByIdWithBooks,GetCategoryByIdWithBooksAndBooksPages和GetCategoryByIdWithBooksAndAuthors(不实用)

修改:由第二个选项我的意思是这样的:

 公共静态范畴GetCategoryById(ModelEntities分贝,诠释的categoryId,PARAMS字符串[] includeFields)
{
    VAR类别= db.Categories;    的foreach(在includeFields串includeField)
    {
        类别= categories.Include(includeField);
    }    返回categories.SingleOrDefault(ⅰ= GT; i.CategoryId ==的categoryId);
}

当调用我们需要一个code是这样的:

 类别theCategory1 = CategoryHelper.GetCategoryById(DB,5,书);
类别theCategory2 = CategoryHelper.GetCategoryById(DB,5,书,Books.Pages);
类别theCategory3 = CategoryHelper.GetCategoryById(DB,5,书,Books.Authors);
类别theCategory4 = CategoryHelper.GetCategoryById(DB,5,书,Books.Pages,Books.Authors);

有没有这种做法的任何明显的缺点?


解决方案

  

写了一个方法GetCategoryById和发送的关系的列表,包括(也许,但似乎仍然没有足够的优雅)


  
  

写入方法,如GetCategoryByIdWithBooks,GetCategoryByIdWithBooksAndBooksPages和GetCategoryByIdWithBooksAndAuthors(不实用)


这两者的结合是目前我的做法。 知道我想要什么属性,包括每个方面,所以我宁愿手工code它们(如你所说你自己,延迟加载并不总是一个选项,如果是,你会重复同样的重复包括()式的语法时,从数据模型映射到DTO的)。

此分离使你想到的数据集的要公开什么困难,给定的数据访问 - code这样的通常是隐藏的服务之下。

通过使用包含虚拟方法的基类,你可以覆盖运行所需的包括()取值:

 使用System.Data.Entity的;公共类DataAccessBase< T>
{
    //例如,这重定向到一个DbContext.Set< T>()。
    公众的IQueryable< T>数据集{搞定;私人集; }    公众的IQueryable< T>包括(Func键<&IQueryable的LT;吨>中的IQueryable< T>>包含= NULL)
    {
        如果(包括== NULL)
        {
            //如果省略,应用默认的include()方法
            //(会调用覆盖包括()时存在)
            包括=包括:
        }        回报包括:(数据集);
    }    公共虚拟的IQueryable< T>包括(IQueryable的< T>实体)
    {
        //提供了可选的entities.Include(F => f.Foo)必须包含所有的实体
        返回实体;
    }
}

您就可以实例化和使用这个类原样,或延长它:

 使用System.Data.Entity的;公共类BookAccess:DataAccessBase<图书>
{
    //重写指定的include()s到每本书上运行
    公共重写IQueryable的<图书>包括(IQueryable的<图书>实体)
    {
        返回base.Include(实体)
                   .INCLUDE(E => e.Author);
    }    //一个单独的include() - 方法
    私人IQueryable的<图书> IncludePages(IQueryable的<图书>实体)
    {
        返回entities.Include(E => e.Pages);
    }    //从外部访问这个方法来检索每本书的所有页面
    公共IEnumerable的<图书> GetBooksWithPages()
    {
        VAR书=包括(IncludePages);
    }
}

现在可以实例就可以了 BookAccess 和调用方法:

  VAR bookAccess =新BookAccess();VAR allBooksWithoutNavigationProperties = bookAccess.DataSet;
变种allBooksWithAuthors = bookAccess.Include();
变种allBooksWithAuthorsAndPages = bookAccess.GetBooksWithPages();

在你的情况,你可能需要创建单独的 IncludePages GetBooksWithPages -alike为每个视图的方法对您的收藏。或者,只是把它写为一个方法,在 IncludePages 方法存在可重用性。

您可以链接这些方法你喜欢的方式,因为他们每个人(以及实体框架的包括()扩展方法)返回另一个的IQueryable< T>

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.

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.

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

  • Write a single method GetCategoryById and include all items inside (performance issue)
  • Write a single method GetCategoryById and send a list of relationships to include (maybe, but still seems not elegant enough)
  • Write methods like GetCategoryByIdWithBooks, GetCategoryByIdWithBooksAndBooksPages and GetCategoryByIdWithBooksAndAuthors (not practical)

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?

解决方案

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

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

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.

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);
    }
}

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();

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.

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>.

这篇关于什么是多&QUOT的最佳做法;包括&QUOT;在实体框架-s?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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