为什么我的Entity Framework Code First代理集合为null,为什么我不能设置它?

Rob*_*ent 41 collections entity-framework poco proxies

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

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

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

这在数据上下文之外工作但是如果我使用查询检索对象,虽然测试是真的,但是当我尝试设置它时,我得到以下异常:'类型'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; }
}
Run Code Online (Sandbox Code Playgroud)

我从上面的代码中省略了其他值类型属性.我在上下文类中没有其他类的映射.

Pan*_*ndo 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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,collection属性不再是自动的,而是具有支持字段.如果您保护setter更好,防止任何代码(代理除外)随后修改这些属性.您会注意到构造函数不再是必需的并被删除.

  • 我刚刚在EF6中遇到过这种行为,这个答案仍然有效! (3认同)

Rob*_*ent 13

我在这里找到了解决这个问题的方法:Code First添加到集合中?如何使用Code First和存储库?

我从除了集合和延迟加载对象之外的所有属性中删除了"虚拟",即所有本机类型.

但我仍然不明白你如何能够得到一个你无法使用的空集合并且无法将其设置为有效集合的情况.

我也在一个MSDN论坛上找到了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);
Run Code Online (Sandbox Code Playgroud)

我仍然感到困惑,即使我的代码现在正在运行,因为上面的修复.