DbSet <tentity>中的结果数

时间:2019-04-29 23:46:05

标签: c# entity-framework linq dbcontext dbset

我正在阅读一些有关DBEntities和DbContext的示例代码。 DbSet从数据库中提取的行数是否有限制?在下面的代码示例中,假设存在一个DbSet<History> historyDbSet<Logs> logs,在创建dbcontext时,dbcontext.logsdbcontext.history的数据库中是否会包含所有日志?如果是这样,如果表具有数百万行,该怎么办。在linq或任何udpates期间保存上下文是否会影响性能?

public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Standard> Standards { get; set; }
public virtual DbSet<Student> Students { get; set; }
public virtual DbSet<StudentAddress> StudentAddresses { get; set; }
using (var context = await _contextFactory.CreateContext())
{
     context.History.Add(history);
     context.SaveChanges();
}

3 个答案:

答案 0 :(得分:1)

实体框架不需要插入任何行即可执行Add()方法和SaveChanged()所做的插入。它应该执行您在SQL中执行的操作,以向相关表中添加一行。

答案 1 :(得分:1)

在您的示例中,它不会“爆炸”

以下行基本上只将一个项目添加到一个空的变更跟踪器中:

 context.History.Add(history);

如果要执行

context.History.ToList()

然后将查询作为“从历史记录中选择*”执行,如果它包含数百万行,则肯定会遇到性能问题。

关键是EF足够“聪明”,不会将内存中的所有内容整体加载。您可以附加一个探查器(或启用EF日志记录)以查看正在执行的实际查询。随便摆弄一下,以获得一些经验。

如果使用调试器扩展集合,则基本上不需要应用任何过滤器,而是可以检索整个集合。滥用导航属性,您甚至可以将整个数据库加载到内存中。

字幕差异在IQueryable与其他类似IEnumerable的界面之间。

虽然对象仍然仅为IQueryable,但实际查询仍在执行,并且可以使用过滤器进行扩展。就像我说的;一旦开始枚举,就会执行实际查询,因此,未经过滤的dbset将返回表中的所有行。


还请注意提到的linq方法

。跳过

还有

。接

还有其他一些东西,例如组,联接,位置等。

答案 2 :(得分:0)

您必须意识到DbSet<Student>并不代表您的Students集合,它代表了数据库中的Students表。这意味着您可以查询Students的属性序列。

如果需要,您可以查询完整的序列,但这会导致性能问题,如果不是内存问题。

因此,如果您要求提供Student数据,则必须牢记所获取数据的用途:不要选择已经知道其值的属性,不要选择项您不打算使用的。

一个示例:具有SchoolsStudents且具有一对多关系的数据库,每个School具有零个或多个Students,每个{{1 }}仅参加一个Student

School
  

在实体框架中,表的列由非虚拟属性表示。虚拟属性表示表之间的关系(一对多,多对多,...)

请勿执行以下操作!

class School
{
     public int Id {get; set;}
     public string Name {get; set;}
     ...

     // every School has zero or more Students (one-to-many)
     public virtual ICollection<Student> Students {get; set;}
}

class Student
{
     public int Id {get; set;}
     public string Name {get; set;}
     ...

     // Every Student attends exactly one School, using foreign key:
     public int SchoolId {get; set;}
     public virtual School School {get; set;}
}

为什么不呢?这看起来像是完美的代码,不是吗?

也许您正在获取比呼叫者将使用的更多数据:

public IEnumerable<School> GetSchoolByLocation(string city)
{
    return mySchoolWithItsStudents = dbContext.Schools
        .Where(school => school.City == city)
        .Include(school => school.Students)
        .ToList();
}

首先获取牛津所有学校,然后仅保留这所学校,这真是浪费!

此外:您得到的学校及其所有学生,以及学校ID所用的全部内容?

  

尝试尽可能长的返回var mySchoolId = GetSchoolByLocation("Oxford") .Where(school => schoolStreet == "Main Street") .Select(school => school.Id) .FirstOrDefault(); ,并让您的呼叫者决定如何处理返回的数据。

也许他想做IQueryable<...>ToListCount。也许他只想要FirstOrDefaultId。只要您不知道,就不要为他做决定,这只会使您的代码可重用性降低。

  

始终使用Name选择属性,并仅选择实际计划使用的数据。如果您打算更新包含的数据,请仅使用Select

Include

最后,如果您想访问所有var schools = dbContext.Schools.Where(school => ...) // Keep only the Schools that you actually plan to use: .Select(school => new { // only select the properties that you plan to use Id = school.Id, Name = school.Name, ... // Only the Students you plan to use: Students = school.Students.Where(student => ...) .Select(student => new { // Again, only the properties you plan to use Id = student.Id, Name = student.Name, // no need for the foreign key: you already know the value // SchoolId = student.SchoolId, }), }); 来显示它们,但又不想一次获取所有百万名学生,请考虑按Page来获取它们。记住最后获取的页面的最后一项的主键,并使用`.Where(item => item.Id> lastFetchedPrimaryKey).Take(pageSize)获取下一页,直到没有更多页面为止。

这样,您可能会要求50个学生,而只显示25个学生,但至少您没有记忆中的所有百万学生。获取下一页的速度相当快,因为​​主键上已经有一个索引,并且获取的项目已经按主键排序了。

相关问题