为啥我的 Entity Framework Code First 代理集合为空,为啥我不能设置它?
Posted
技术标签:
【中文标题】为啥我的 Entity Framework Code First 代理集合为空,为啥我不能设置它?【英文标题】:Why is my Entity Framework Code First proxy collection null and why can't I set it?为什么我的 Entity Framework Code First 代理集合为空,为什么我不能设置它? 【发布时间】:2011-05-03 10:39:39 【问题描述】:我正在使用 DBContext 并且有两个属性都是虚拟的类。当我查询上下文时,我可以在调试器中看到我正在获取一个代理对象。但是,当我尝试添加一个集合属性时,它仍然为空。我认为代理会确保集合被初始化。
因为我的 Poco 对象可以在其数据上下文之外使用,所以我在构造函数中添加了一个检查集合是否为 null 并在必要时创建它:
public class DanceStyle
public DanceStyle()
if (DanceEvents == null)
DanceEvents = new Collection<DanceEvent>();
...
public virtual ICollection<DanceEvent> DanceEvents get; set;
这在数据上下文之外有效,但如果我使用查询检索对象,虽然测试为真,但当我尝试设置它时,我得到以下异常:'DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94'类型上的属性'DanceEvents'不能设置,因为该集合已设置为 EntityCollection。'
我可以看到它是空的,我不能添加到它,但我也不能将它设置为一个集合,因为代理说它已经设置好了。因此我不能使用它。我很困惑。
这里是 DanceEvent 类:
public class DanceEvent
public DanceEvent()
if (DanceStyles == null)
DanceStyles = new Collection<DanceStyle>();
...
public virtual ICollection<DanceStyle> DanceStyles get; set;
我在上面的代码中省略了其他值类型属性。我在上下文类中没有这些类的其他映射。
【问题讨论】:
【参考方案1】:正如您在回答自己的问题时正确观察到的那样,通过阻止实体框架创建更改跟踪代理,从集合属性中删除“虚拟”关键字可以解决该问题。但是,这对很多人来说并不是一个解决方案,因为更改跟踪代理非常方便,并且可以在您忘记检测代码中正确位置的更改时帮助防止出现问题。
更好的方法是修改您的 POCO 类,以便它们在其 get 访问器中实例化集合属性,而不是在构造函数中。这是您的 POCO 类,已修改为允许创建更改跟踪代理:
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 受保护,以防止任何代码(代理除外)随后修改这些属性。你会注意到构造函数不再需要并且被移除了。
【讨论】:
这是另一种方法,但它没有解释我的评论:“这在数据上下文之外有效,但如果我使用查询检索对象,虽然测试是真的,当我尝试设置它,我得到以下异常:'无法设置类型'DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94'上的属性'DanceEvents',因为集合已设置为EntityCollection。'我可以看到它是空的,我不能添加到它,但我也不能将它设置为一个集合,因为代理说它已经设置了。因此我不能使用它。我很困惑。" 我无法重现您所描述的内容。根据我的经验,当实体被实例化为代理时(由于它被查询返回,或者如果使用 DbSet.Create 方法),它的集合属性将使用 EntityCollection 对象进行实例化。您永远不必设置这些属性——只需从中添加/删除实体即可。 自从我 2 年前写下我的问题后,这种行为可能已经发生了变化。 我刚刚在 EF6 中遇到了这种行为,这个答案仍然有效!【参考方案2】:我在这里找到了解决这个问题的方法:Code First adding to collections? How to use Code First with repositories?
我从所有属性中删除了“虚拟”,除了集合和延迟加载的对象,即所有本机类型。
但我仍然不明白你怎么会遇到这样的情况:你有一个无法使用的空集合并且无法将其设置为有效集合。
我还在 MSDN 论坛上找到了this answer from Rowan Miller
嗨,
如果您将所有属性设为虚拟,则 EF 将在运行时生成从您的 POCO 类派生的代理类,这些代理允许 EF 实时了解更改,而不必捕获对象的原始值,然后保存时扫描更改(这显然具有性能和内存使用优势,但差异可以忽略不计,除非您将大量实体加载到内存中)。这些被称为“更改跟踪代理”,如果您将导航属性设为虚拟,则仍会生成一个代理,但它更简单,并且仅包含一些在您访问导航属性时执行延迟加载的逻辑。
因为您的原始代码正在生成更改跟踪代理,所以 EF 将您的集合属性替换为一种特殊的集合类型,以帮助它找出更改。因为您尝试将集合设置回构造函数中的简单列表,所以您得到了异常。
除非您发现性能问题,否则我会按照 Terrence 的建议,从您的非导航属性中删除“虚拟”。
~罗文
因此,如果我的所有属性都是虚拟的,我似乎只会遇到完整的“更改跟踪代理”的问题。但是鉴于此,为什么我仍然不能在更改跟踪代理上使用虚拟属性?由于 ds2.DanceEvents 为 null 且无法在构造函数中设置,因此该代码在第三行爆炸:
DanceStyle ds2 = ctx.DanceStyles.Where(ds => ds.DanceStyleId == 1).Single();
DanceEvent evt = CreateDanceEvent();
ds2.DanceEvents.Add(evt);
我仍然感到困惑,尽管由于上述修复,我的代码现在可以正常工作。
【讨论】:
【参考方案3】:老问题...
Poco 类:
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 的代理类中引发了异常。这意味着该行为仍然存在。
【讨论】:
以上是关于为啥我的 Entity Framework Code First 代理集合为空,为啥我不能设置它?的主要内容,如果未能解决你的问题,请参考以下文章
修改从 Entity Framework Core 返回的本地实体后,为啥我的执行消失了?
为啥我必须为 Code First / Entity Framework 提供无参数构造函数
为啥我的 Entity Framework Core Database-First 模型自定义更改在 Re-Scaffold 后消失了?任何解决方案
为啥运行时表达式会导致 Entity Framework Core 5 的缓存发生冲突?