我正在使用DBContext并且有两个属性都是虚拟的类。我可以在调试器中看到,当我查询上下文时,我正在获取代理对象。但是,当我尝试添加它时,collection属性仍为null。我认为代理将确保集合被初始化。
因为我的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。'
我可以看到它是null并且我无法添加它,但我也不能将它设置为集合,因为代理已经设置了它。因此我无法使用它。我很困惑。
这是DanceEvent类:
public class DanceEvent
{
public DanceEvent()
{
if (DanceStyles == null)
{
DanceStyles = new Collection<DanceStyle>();
}
}
...
public virtual ICollection<DanceStyle> DanceStyles { get; set; }
}
我已从上面的代码中省略了其他值类型属性。我在上下文类中没有其他类的映射。
答案 0 :(得分:48)
正如您在自己的问题的答案中正确观察到的那样,通过阻止实体框架创建更改跟踪代理,从集合属性中删除“virtual”关键字可以解决问题。但是,对于许多人来说,这不是一个解决方案,因为更改跟踪代理可以非常方便,当您忘记在代码中的正确位置检测到更改时,可以帮助防止出现问题。
更好的方法是修改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更好,防止任何代码(代理除外)随后修改这些属性。您会注意到构造函数不再是必需的并且已被删除。
答案 1 :(得分:13)
我在这里找到了解决这个问题的方法: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);
我仍然感到困惑,即使我的代码现在正在运行,因为上面的修复。
答案 2 :(得分: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中代理类中引发的异常。这意味着行为仍然存在。