为什么我的entity framework代码第一个代理收集为空,为什么我不能设置它?

我正在使用DBContext并有两个类的属性都是虚拟的。 我可以看到在debugging器中,当我查询上下文时,我正在获取代理对象。 但是,当我尝试添加它时,集合属性仍为空。 我认为代理将确保集合被初始化。

因为我的Poco对象可以在其数据上下文之外使用,所以我在构造函数中添加了一个空集合的检查,并在必要时创build它:

public class DanceStyle { public DanceStyle() { if (DanceEvents == null) { DanceEvents = new Collection<DanceEvent>(); } } ... public virtual ICollection<DanceEvent> DanceEvents { get; set; } } 

这工作之外的数据上下文,但如果我检索一个对象使用查询,虽然testing是真实的,当我尝试设置它时,我得到以下exception:'属性'DanceEvents'types'DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94'不能被设置,因为集合已经被设置为一个EntityCollection。

我可以看到它是空的,我不能添加到它,但我也不能将其设置为一个集合,因为代理说它已经设置。 所以我不能使用它。 我很困惑。

这里是DanceEvent类:

 public class DanceEvent { public DanceEvent() { if (DanceStyles == null) { DanceStyles = new Collection<DanceStyle>(); } } ... public virtual ICollection<DanceStyle> DanceStyles { get; set; } } 

我从上面的代码中省略了其他值types的属性。 我没有上下文类中的这些类的其他映射。

正如您在自己的问题的答案中正确观察到的那样,通过阻止entity framework创build更改跟踪代理,从集合属性中除去“虚拟”关键字可解决此问题。 但是,对于许多人来说这不是一个解决scheme,因为更改跟踪代理可以非常方便,并且可以帮助防止在您忘记在代码中正确位置检测到更改时发生的问题。

更好的方法是修改你的POCO类,以便它们在get访问器中实例化集合属性,而不是在构造函数中。 这里是您的POCO类,修改为允许更改跟踪代理创build:

 public class DanceEvent { private ICollection<DanceStyle> _danceStyles; public virtual ICollection<DanceStyle> DanceStyles { get { return _danceStyles ?? (_danceStyles = new Collection<DanceStyle>()); } protected set { _danceStyles = value; } } } 

在上面的代码中,collection属性不再是自动的,而是有一个后台字段。 最好是保持setter不变,防止任何代码(代理除外)随后修改这些属性。 你会注意到构造函数不再需要,并被删除。

我发现这个问题的解决scheme在这里: 代码首先添加到集合? 如何使用Code First与存储库?

我从除collections集和延迟加载的对象(即所有本地types)之外的所有属性中删除“虚拟”。

但是我仍然不明白,如果你有一个你不能使用的无效集合,并且没有办法将其设置为一个有效的集合的情况,

我也在MSDN论坛上find了来自Rowan Miller的这个答案

嗨,

如果您将所有属性设置为虚拟,那么EF将在运行时生成从您的POCO分类派生的代理类,这些代理允许EF实时查找更改,而不必捕获对象的原始值,然后扫描更改当你保存(这显然有性能和内存使用的好处,但除非你有大量的实体加载到内存中的差异将可以忽略不计)。 这些被称为“更改跟踪代理”,如果您的导航属性是虚拟的,那么仍然会生成一个代理,但它更简单,只是包含一些逻辑来在访问导航属性时执行延迟加载。

由于您的原始代码正在生成更改跟踪代理,EF正在使用特殊的集合typesreplace您的集合属性,以帮助查找有关更改。 因为您尝试将集合设置回构造函数中的一个简单列表,您将得到exception。

除非你看到性能问题,否则我会遵循Terrence的build议,并从非导航属性中删除“虚拟”。

〜罗文

所以,如果我的所有属性都是虚拟的,那么我只会遇到一个完整的“更改跟踪代理”的问题。 但是,鉴于此,为什么我仍然不能在更改跟踪代理上使用虚拟财产? 这段代码在第三行发生了,因为ds2.DanceEvents是null,不能在构造函数中设置:

 DanceStyle ds2 = ctx.DanceStyles.Where(ds => ds.DanceStyleId == 1).Single(); DanceEvent evt = CreateDanceEvent(); ds2.DanceEvents.Add(evt); 

我仍然困惑,即使我的代码现在正在工作,因为上面的修复。

老问题

波科class:

 public partial class MyPOCO { public MyPOCO() { this.MyPocoSub = new HashSet<MyPocoSub>(); } //VIRTUAL public virtual ICollection<MyPocoSub> MyPocoSub { get; set; } } 

和代理代码:

  public override ICollection<MyPocoSubSet> MyPocoSubSets { get { ICollection<MyPocoSubSet> myPocoSubSets = base.MyPocoSubSets; if (!this.ef_proxy_interceptorForMyPocoSubSets(this, myPocoSubSets)) { return base.MyPocoSubSets; } return myPocoSubSets; } set { if (value != this.RelationshipManager.GetRelatedEnd("WindowsFormsApplication.Models.MyPocoSubSet_MyPOCO", "MyPocoSubSet_MyPOCO_Source")) { // EXCEPTION throw new InvalidOperationException("The property 'MyPocoSubSets' on type 'MyPOCO_A78FCE6C6A890855C68B368B750864E3136B589F9023C7B1D90BF7C83FD291AC' cannot be set because the collection is already set to an EntityCollection."); } base.MyPocoSubSets = value; } } 

正如你可以看到在ExtityFramework 5中的代理类中引发的exception。这意味着行为依然存在。