Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/260.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# “什么是多个”的最佳实践;包括「-在实体框架中是什么?_C#_Entity Framework_Architecture_Data Access Layer_Ef Database First - Fatal编程技术网

C# “什么是多个”的最佳实践;包括「-在实体框架中是什么?

C# “什么是多个”的最佳实践;包括「-在实体框架中是什么?,c#,entity-framework,architecture,data-access-layer,ef-database-first,C#,Entity Framework,Architecture,Data Access Layer,Ef Database First,假设数据模型中有四个实体:类别、书籍、作者和书页。还假设书籍类别、书籍作者和书籍书页关系是一对多关系 如果从数据库中检索类别实体实例(包括“Books”、“Books.BookPages”和“Books.Authors”),这将成为一个严重的性能问题。此外,不包括它们将导致“对象引用未设置为对象的实例”异常 使用多个Include方法调用的最佳实践是什么 编写一个方法GetCategoryById并包含其中的所有项(性能问题) 编写一个方法GetCategoryById并发送一个要包含的关系列

假设数据模型中有四个实体:类别、书籍、作者和书页。还假设书籍类别、书籍作者和书籍书页关系是一对多关系

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

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

  • 编写一个方法GetCategoryById并包含其中的所有项(性能问题)
  • 编写一个方法GetCategoryById并发送一个要包含的关系列表(可能,但似乎还不够优雅)
  • 编写方法,如GetCategoryByWithBooks、GetCategoryByWithBooks和BooksPages以及GetCategoryByWithBooks和Authors(不实用)
编辑:第二个选项的意思是:

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);
}
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");
public class Book
{
  public int BookID { get; set; }
  //Whatever other information about the Book...
  public virtual Category Category { get; set; }
  public virtual List<Author> Authors { get; set; }
  public virtual List<BookPage> BookPages { get; set; }
}
public partial class BookStoreContext
{
    public IQueryable<Category> GetCategoriesWithBooks()
    {
        return Categories.Include(c => c.Books);
    }

    public IQueryable<Category> GetCategoriesWith(params string[] includeFields)
    {
        var categories = Categories.AsQueryable();
        foreach (string includeField in includeFields)
        {
            categories = categories.Include(includeField);
        }
        return categories;
    }

    // Just another example
    public IQueryable<Category> GetBooksWithAllDetails()
    {
        return Books
            .Include(c => c.Books.Authors)
            .Include(c => c.Books.Pages);
    }

    // yet another complex example
    public IQueryable<Category> GetNewBooks(/*...*/)
    {
        // probably you can pass sort by, tags filter etc in the parameter.
    }
}
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
调用时,我们需要这样的代码:

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);
}
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");
public class Book
{
  public int BookID { get; set; }
  //Whatever other information about the Book...
  public virtual Category Category { get; set; }
  public virtual List<Author> Authors { get; set; }
  public virtual List<BookPage> BookPages { get; set; }
}
public partial class BookStoreContext
{
    public IQueryable<Category> GetCategoriesWithBooks()
    {
        return Categories.Include(c => c.Books);
    }

    public IQueryable<Category> GetCategoriesWith(params string[] includeFields)
    {
        var categories = Categories.AsQueryable();
        foreach (string includeField in includeFields)
        {
            categories = categories.Include(includeField);
        }
        return categories;
    }

    // Just another example
    public IQueryable<Category> GetBooksWithAllDetails()
    {
        return Books
            .Include(c => c.Books.Authors)
            .Include(c => c.Books.Pages);
    }

    // yet another complex example
    public IQueryable<Category> GetNewBooks(/*...*/)
    {
        // probably you can pass sort by, tags filter etc in the parameter.
    }
}
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

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

一些性能考虑是ADO.Net连接器特有的。如果您没有获得所需的性能,我会记住数据库视图或存储过程作为备份

首先,请注意
DbContext
(和
ObjectContext
)对象不是线程安全的

如果您关心的是Clarity而不是性能,那么第一个选项是最简单的

另一方面,如果您担心性能,并且愿意在获取数据后处理上下文对象,那么您可以使用多个同时执行的任务(线程)查询数据,每个任务(线程)使用自己的上下文对象

如果您需要一个上下文来跟踪对数据的更改,您可以直接通过单个查询将所有项添加到上下文中,或者可以使用Attach方法“重建”原始状态,然后进行更改和保存

后者是这样的:

不幸的是,没有一种最佳实践可以满足所有情况

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

编写方法,如GetCategoryByWithBooks、GetCategoryByWithBooks和BooksPages以及GetCategoryByWithBooks和Authors(不实用)

我目前的做法是将这两种方法结合起来I知道要为每个上下文包含哪些属性,所以我宁愿手工编写它们(正如您自己所说,延迟加载并不总是一个选项,如果是,您将在从数据模型映射到DTO时重复相同的重复的
include()
-类似语法)

由于这样的数据访问代码通常隐藏在服务下面,这种分离会使您更加仔细地考虑要公开哪些数据集

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

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


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

,正如@Colin在注释中提到的那样,您需要在定义导航属性时使用virtual关键字,以便它们能够处理延迟加载。假设您首先使用代码,您的Book类应该如下所示:

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);
}
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");
public class Book
{
  public int BookID { get; set; }
  //Whatever other information about the Book...
  public virtual Category Category { get; set; }
  public virtual List<Author> Authors { get; set; }
  public virtual List<BookPage> BookPages { get; set; }
}
public partial class BookStoreContext
{
    public IQueryable<Category> GetCategoriesWithBooks()
    {
        return Categories.Include(c => c.Books);
    }

    public IQueryable<Category> GetCategoriesWith(params string[] includeFields)
    {
        var categories = Categories.AsQueryable();
        foreach (string includeField in includeFields)
        {
            categories = categories.Include(includeField);
        }
        return categories;
    }

    // Just another example
    public IQueryable<Category> GetBooksWithAllDetails()
    {
        return Books
            .Include(c => c.Books.Authors)
            .Include(c => c.Books.Pages);
    }

    // yet another complex example
    public IQueryable<Category> GetNewBooks(/*...*/)
    {
        // probably you can pass sort by, tags filter etc in the parameter.
    }
}
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
这实际上会减少从数据库返回的记录,并且更易于理解


希望能有帮助

在db-first方法中,假设您创建BookStore.edmx并添加Category和Book实体,它会生成上下文,如
public partial class BookStoreContext:DbContext
,如果您可以像这样添加分部类,那么这是一个简单的好做法:

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);
}
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");
public class Book
{
  public int BookID { get; set; }
  //Whatever other information about the Book...
  public virtual Category Category { get; set; }
  public virtual List<Author> Authors { get; set; }
  public virtual List<BookPage> BookPages { get; set; }
}
public partial class BookStoreContext
{
    public IQueryable<Category> GetCategoriesWithBooks()
    {
        return Categories.Include(c => c.Books);
    }

    public IQueryable<Category> GetCategoriesWith(params string[] includeFields)
    {
        var categories = Categories.AsQueryable();
        foreach (string includeField in includeFields)
        {
            categories = categories.Include(includeField);
        }
        return categories;
    }

    // Just another example
    public IQueryable<Category> GetBooksWithAllDetails()
    {
        return Books
            .Include(c => c.Books.Authors)
            .Include(c => c.Books.Pages);
    }

    // yet another complex example
    public IQueryable<Category> GetNewBooks(/*...*/)
    {
        // probably you can pass sort by, tags filter etc in the parameter.
    }
}
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
注:

  • 您可以阅读一些简单的(很多复杂的)存储库模式,只是为了扩展
    IDbSet Categories
    ,将common
    Include
    Where
    分组,而不是使用静态
    CategoryHelper
    。因此,您可以使用
    IQueryable db.Categories.WithBooks()
  • 您不应该在
    GetCategoryById
    中包含所有子实体,因为它不会在方法名称中自我解释,如果此方法的用户不是
    书籍
    实体的兄弟,则会导致性能问题
  • 即使您没有包含所有内容,但如果您使用延迟加载,您仍然有潜力
  • 如果你有1000本
    书籍
    那么你最好像这样分页加载db.Books.Where(b=>b.CategoryId=CategoryId).Skip(Skip(Skip).Take(Take).ToList()或者更好地添加上面的方法,使其类似于
    db.GetBooksByCategoryId(CategoryId,Skip,Take)

我自己更喜欢显式加载实体,因为我会“知道”当前加载的是什么,但延迟加载只有在您有条件加载子实体的情况下才有用,并且应该在db上下文的小范围内使用,否则我无法控制db命中率和结果的大小。

能否详细说明为什么会出现这种情况例外?惰性加载对您不起作用吗?正如您已经知道的,当一个人不使用Include方法时,他会得到这个异常。延迟加载当然有效,但使用Include的目的是什么?默认情况下,实体框架可能包含所有关系,并且由于延迟加载,相关的实体在被调用时会被加载。在这种情况下,使用Include没有任何意义。我很好奇,我可能遗漏了什么