实体框架延迟加载集合有时为null

时间:2017-08-02 21:29:24

标签: c# entity-framework ef-code-first entity-framework-6

我有两个模型,其中一个有另一个的子集合:

quant

此代码适用于> 99%的小部件:

[Table("ParentTable")]
public class Parent
{
    [Key, Column("Parent")]
    public string Id { get; set; }

    [Column("ParentName")]
    public string Name { get; set; }

    public virtual ICollection<Widget> Widgets { get; set; }
}

[Table("WidgetTable")]
public class Widget
{
    public string Year { get; set; }

    [Column("Parent")]
    public string ParentId { get; set; }

    public string Comments { get; set; }

    [Key, Column("ID_Widget")]
    public int Id { get; set; }

    [ForeignKey("ParentId"), JsonIgnore]
    public virtual Parent Parent { get; set; }
}

通常,var parent = _dbContext.Parents.FirstOrDefault(p => p.Id == parentId); 是一个包含多个项目的集合。但是,在几个实例中,parent.Widgets为空(不是没有项目的集合)。

我使用查询分析器跟踪父查询和查询属于该父查询的小部件。两者都返回我期望的行;但是,一个或两个父ID的模型会导致Widgets集合的空值。什么可能导致延迟加载的集合在某些情况下为空而不是其他情况?

1 个答案:

答案 0 :(得分:4)

当dbContext生命周期在Add,saveChanges中保持打开,然后检索时,通常会出现这种情况。

例如:

var context = new MyDbContext(); // holding Parents.
var testParent = new Parent{Id = "Parent1", Name = "Parent 1"};
context.Parents.Add(testParent);

此时如果您这样做:

var result = context.Parents.FirstOrDefault(x=> x.ParentId == "Parent1"); 
你不会得到父母。选择来自承诺状态..所以......

context.SaveChanges();
var result = context.Parents.FirstOrDefault(x=> x.ParentId == "Parent1"); 

这将返回对您已插入的父项的引用,因为上下文知道此实体并且具有对您创建的对象的引用。它没有进入数据状态。由于您对Widgets的定义只是使用get / set自动属性定义,因此在这种情况下Widgets集合将为#null。

如果你这样做:

context.Dispose();
context = new MyDbContext();
var result = context.Parents.FirstOrDefault(x=> x.ParentId == "Parent1"); 

在这种情况下,新上下文不知道父级,因此它进入数据状态。 EF将为您返回一个延迟加载Widgets的代理列表,其中没有任何内容,因此您返回一个空列表,而不是#null。

在EF中处理集合类时,最好避免使用自动属性或在构造函数中初始化它们以避免此行为;您通常希望在创建父级后分配小部件。初始化默认成员更好,因为您不想鼓励在集合属性上使用setter。

例如:

private readonly List<Widget> _widgets = new List<Widget>();
public virtual ICollection<Widget> Widgets
{
  get { return _widgets; }
  protected set { throw new InvalidOperationException("Do not set the Widget collection. Use Clear() and Add()"); }
}

避免对集合属性执行Set操作,因为这会搞乱实体引用方案。例如,如果您想按年度对Widget集合进行排序,并执行以下操作:

parent.Widgets = parent.Widgets.OrderBy(x=> x.Year).ToList();

看起来很无辜,但是当Widgets引用是一个EF代理时,你只是把它吹走了。 EF现在无法对集合执行更改跟踪。

初始化你的集合,你应该避免使用#null集合引用的意外。我还会看一下dbContext的生命周期。在请求或特定操作的生命周期内保持一个初始化是好的,但是避免使它们保持超过必要的时间。上下文更改跟踪和此类消耗资源,您可以在交叉操作时找到看似间歇性的奇怪行为。

相关问题