请参见以下简化示例:
学生班:
public class Student
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public Grade Grade { get; set; }
}
年级:
public class Grade
{
public int GradeId { get; set; }
public string GradeName { get; set; }
public ICollection<Student> Students { get; set; }
}
上下文类:
public class SchoolContext : DbContext
{
public SchoolContext() : base()
{
Configuration.LazyLoadingEnabled = true;
}
public DbSet<Student> Students { get; set; }
public DbSet<Grade> Grades { get; set; }
}
程序:
static void Main(string[] args)
{
using (var ctx = new SchoolContext())
{
Grade[] grades = ctx.Grades.ToArray();
Console.WriteLine(grades[0].Students == null); // True - As expected
var students = ctx.Students.ToArray();
Console.WriteLine(grades[0].Students == null); // False - ? Did not expect that
}
Console.Read();
}
发生以下情况:
Grades
的列表已保存到数组中Students
导航属性为null
Students
Students
navigation属性。如果不谨慎使用,最终可能会给客户带来非常昂贵的有效负载。 谁能解释 为什么 和 如何 ?导航属性是如何填充到数组中的?
答案 0 :(得分:2)
在执行查询以使用grades[0].Students
从数据库中获取学生后加载ctx.Students.ToArray();
的原因是,您DbContext
是跟踪更改。>
这在Entity Framework docs中有解释:
跟踪行为控制Entity Framework Core是否将有关实体实例的信息保留在其更改跟踪器中。如果跟踪了一个实体,则在SaveChanges()期间,该实体中检测到的任何更改都将保留到数据库中。实体框架核心还将修复从跟踪查询获得的实体与先前加载到DbContext实例中的实体之间的导航属性。
这是EF Core文档,但这也适用于EF6 for .NET Framework。
如果要禁用此行为,可以将实体加载为无跟踪:
ctx.Grades.AsNoTracking().ToArray();
...您还可以默认将其禁用(例如,在DbContext
构造函数中),与延迟加载的方法相同。
您可以执行的另一种方法是从上下文中手动分离对象。 然后,如果您打算进行任何更改并将其保存到数据库中,则应在查询学生之后以及进行更改之前重新附加实体:
using (var ctx = new SchoolContext())
{
Grade[] grades = ctx.Grades.ToArray();
Grade firstGrade = grades[0];
Console.WriteLine(firstGrade.Students == null); // True - as expected
ctx.Grades.Detach(firstGrade); // stop tracking changes for this entity
var students = ctx.Students.ToArray();
Console.WriteLine(firstGrade.Students == null); // True - still null
// Let's reattach so we can track changes and save to database
ctx.Grades.Attach(firstGrade);
firstGrade.GradeName = "Some new value"; // will be persisted, as this is being tracked again
ctx.SaveChanges();
}
此外,值得一提的是启用了延迟加载,如果尚未加载导航属性,则首次访问grades[0].Students
会使EF加载该导航属性(这正是其目的),但是,由于您的导航属性不是virtual
,因此似乎没有发生。