【问题标题】:Why are collection navigation properties not initialized for lazy loading proxy classes为什么没有为延迟加载代理类初始化集合导航属性
【发布时间】:2014-07-26 08:46:45
【问题描述】:

在我的一个项目中,我使用实体框架和实体上的虚拟导航属性。这意味着实体是从数据库中加载或使用IDbSet<T>.Create() 创建的,并返回 DynamicProxy。因为我只将导航属性设为虚拟,所以此代理会延迟加载且不进行更改跟踪(所有属性都需要为 virtual 才能获得更改跟踪代理)。

我的假设是 DynamicProxy 负责初始化虚拟 ICollection<T> 属性,就像从数据库加载实体时一样。但是当我使用IDbSet<T>.Create() 创建一个新实体时,这些导航属性仍然​​是null

然后我尝试将所有属性设置为virtual,因此我得到了一个带有更改跟踪的 DynamicProxy,令我惊讶的是这些导航属性已被初始化。

看下面的例子:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;

static class Program
{
    static void Main()
    {
        using (var db = new BloggingContext())
        {
            var changeTrackingBlog = db.ChangeTrackingBlogs
                                       .Create(); // returns a DynamicProxy
            var changeTrackingBlogPostCount = changeTrackingBlog
                              .Posts
                              .Count; // Posts has type EntityCollection<Post>

            var lazyLoadingBlog = db.LazyLoadingBlogs
                                    .Create(); // returns a DynamicProxy
            var lazyLoadingBlogPostCount = lazyLoadingBlog.Posts
                                                .Count; // Posts == null
        } 
    }
}

public class BloggingContext : DbContext
{
    public IDbSet<Post> Posts { get; set; }
    public IDbSet<ChangeTrackingBlog> ChangeTrackingBlogs { get; set; }
    public IDbSet<LazyLoadingBlog> LazyLoadingBlogs { get; set; }
}

public class Post
{
    [Key]
    public int PostId { get; set; }

    public virtual ChangeTrackingBlog ChangeTrackingBlog { get; set; }
    public virtual LazyLoadingBlog LazyLoadingBlog { get; set; }
}

public class ChangeTrackingBlog
{
    [Key]
    public virtual int BlogId { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

public class LazyLoadingBlog
{
    // Not all properties are virtual, so no Change tracking, just lazy loading
    [Key]
    public int BlogId { get; set; } 

    public virtual ICollection<Post> Posts { get; set; }
}

我希望有人能解释这里发生了什么。

【问题讨论】:

  • 当您将所有属性设为虚拟时,您是否确定这些集合导航属性已初始化,从而允许创建更改跟踪代理?我认为你错了。这些集合属性仅在您使用延迟加载时自动实例化并访问它们。如果不是,它们仍然为空。
  • @JotaBe:好点,但Posts 属性在更改跟踪代理的情况下被访问之前被初始化。
  • 这就是我的意思:总是初始化集合属性,除非你通过延迟加载来加载它,否则你会得到一个“空引用异常”。除非它们是惰性的、急切的或显式加载的,否则永远不会初始化 AFAIK 集合属性。把它放在你的类构造函数中不会产生任何干扰。

标签: c# .net entity-framework ef-code-first poco


【解决方案1】:

避免空引用的常用方法是在构造函数中初始化集合:

public LazyLoading()
{
   Posts = new List();
}

我相信使用支持字段可能是更好的做法 - 与在某些情况下不调用匿名构造函数有关(序列化或其他东西 - 为含糊而道歉)。所以我这样做:

public class LazyLoadingBlog
{
    private ICollection<Post> _Posts = new List<Post>();

    public virtual ICollection<Post> Posts 
    { 
       get { return _Posts ; }
       //protected set lets EF override for lazy loading
       protected set { _Posts = value; 
    }
} 

很遗憾,我无法解释为什么在将所有属性标记为虚拟时不会出现错误...

【讨论】:

  • 谢谢!在构造函数中初始化属性意味着从构造函数访问虚拟成员,这可能会导致其他问题,所以我不喜欢这种解决方案。我想我可以将自动属性更改为带有支持字段的属性。我仍然想知道为什么会有区别:)
  • 我认为这实际上是我选择支持字段的主要原因 - 由于虚拟呼叫,我收到了 CodeRush 的警告。是的,我也想知道区别
  • 我在构造函数中测试了初始化集合。这不适用于具有更改跟踪的代理,因为它会引发 InvalidOperationException:附加信息:无法设置类型“ChangeTrackingBlog_E2538CFF8F25D3B52885F4D5A327D1183626232C1097A36B54E666D62E02C6FD”上的属性“Posts”,因为集合已设置为 EntityCol我认为您是对的,使用支持字段创建属性是解决此问题的唯一方法。
猜你喜欢
  • 1970-01-01
  • 2020-05-10
  • 1970-01-01
  • 2016-06-04
  • 2018-11-21
  • 2014-12-23
  • 1970-01-01
  • 2015-04-01
  • 2017-08-13
相关资源
最近更新 更多