我有这堂课:
public class Subject
{
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[StringLength(50)]
public string Code { get; set; }
public int LevelId { get; set; }
public int? ParentId { get; set; }
public int Order { get; set; }
[ForeignKey("LevelId")]
public Level Level { get; set; }
[ForeignKey("ParentId")]
public Subject Parent { get; set; }
public ICollection<Subject> Children { get; set; }
[Column(TypeName = "datetime2")]
public DateTime? DeletedAt { get; set; }
}
然后我有这两个存储库方法:
public Subject GetById(int subjectId)
{
var subjectFound = _context.Subjects.Where(subj => subj.Id == subjectId)
.SingleOrDefault();
var subject = _mapper.Map<Subject>(subjectFound);
if (subjectFound != null)
{
subject.Children = GetChildren(subject.Id).ToList();
}
return subject;
}
public void Update(Subject existingSubject)
{
var subjectToUpdate = _context.Subjects.Where(subj => subj.Id == existingSubject.Id)
.SingleOrDefault();
if (subjectToUpdate != null)
{
_mapper.Map(existingSubject, subjectToUpdate);
_context.Update(subjectToUpdate);
SaveChildren(subjectToUpdate.Id, existingSubject.LevelId, existingSubject.Children);
_context.SaveChanges();
}
}
如果查看这两种方法的LINQ语句,它们在where子句中只是不同的变量名,但它们具有相同的值,正在搜索Id。
现在发生的事情是在第一个方法(GetById)中,在LINQ查询之后,Children为null,因为我在其中有一个GetChildren调用。
但是在第二种方法(Update)中,在LINQ查询之后,返回的对象具有其所有Children。它们是相同的查询,我一遍又一遍地检查,以确保它们具有相同的值,并且确实具有相同的值。
在拨打这两个号码之前,我没有任何特殊的先前代码;它们只是正常调用。所以我对这种行为感到很困惑。可能导致这种情况的原因是什么?
答案 0 :(得分:2)
首先,EF Core 从不自动包含相关实体。 EF Core不支持延迟加载,因此所有相关实体必须急切加载(通过Include
)或显式加载(Load
)。
也就是说,EF中的DbContext维护一个对象图,并且在查询对象时,它们会保留在该图中。一旦孩子们一次加载,EF就可以再次从图表中重新建立那些相关实体,而不必查询数据库,这可能是你在这里看到的。然而,这不是你应该依赖的东西,因为它非常受欢迎。你基本上很幸运,方法是按照某种顺序调用的。总是最好假设事情不会在那里并采取相应的行动。如果EF不需要实际发出查询来实现它,那很好,但是如果有必要,它应该被赋予权力。
答案 1 :(得分:0)
您可能在 get 方法之后调用了 update 方法。如果 EF 已经将子元素加载到实体中,它们将出现在实体的所有后续加载中。本质一样,同一个对象!
如果你希望导航属性对象总是被加载,你可以在实体的流畅配置中指定:builder.Navigation(x => x.Children).AutoInclude(),在你的DbContext的OnModelCreating方法中设置班级;