我正在阅读一些有关DBEntities和DbContext的示例代码。 DbSet从数据库中提取的行数是否有限制?在下面的代码示例中,假设存在一个DbSet<History> history
或DbSet<Logs> logs
,在创建dbcontext时,dbcontext.logs
或dbcontext.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();
}
答案 0 :(得分:1)
实体框架不需要插入任何行即可执行Add()方法和SaveChanged()所做的插入。它应该执行您在SQL中执行的操作,以向相关表中添加一行。
答案 1 :(得分:1)
在您的示例中,它不会“爆炸”
以下行基本上只将一个项目添加到一个空的变更跟踪器中:
context.History.Add(history);
如果要执行
context.History.ToList()
然后将查询作为“从历史记录中选择*”执行,如果它包含数百万行,则肯定会遇到性能问题。
关键是EF足够“聪明”,不会将内存中的所有内容整体加载。您可以附加一个探查器(或启用EF日志记录)以查看正在执行的实际查询。随便摆弄一下,以获得一些经验。
如果使用调试器扩展集合,则基本上不需要应用任何过滤器,而是可以检索整个集合。滥用导航属性,您甚至可以将整个数据库加载到内存中。
字幕差异在IQueryable
与其他类似IEnumerable
的界面之间。
虽然对象仍然仅为IQueryable
,但实际查询仍在执行,并且可以使用过滤器进行扩展。就像我说的;一旦开始枚举,就会执行实际查询,因此,未经过滤的dbset将返回表中的所有行。
。跳过
还有
。接
还有其他一些东西,例如组,联接,位置等。
答案 2 :(得分:0)
您必须意识到DbSet<Student>
并不代表您的Students
集合,它代表了数据库中的Students
表。这意味着您可以查询Students
的属性序列。
如果需要,您可以查询完整的序列,但这会导致性能问题,如果不是内存问题。
因此,如果您要求提供Student
数据,则必须牢记所获取数据的用途:不要选择已经知道其值的属性,不要选择项您不打算使用的。
一个示例:具有Schools
和Students
且具有一对多关系的数据库,每个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<...>
或ToList
或Count
。也许他只想要FirstOrDefault
和Id
。只要您不知道,就不要为他做决定,这只会使您的代码可重用性降低。
始终使用
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个学生,但至少您没有记忆中的所有百万学生。获取下一页的速度相当快,因为主键上已经有一个索引,并且获取的项目已经按主键排序了。