2014-06-05 47 views
1

在我的一個項目中,我使用實體框架和實體上的虛擬導航屬性。這意味着實體從數據庫加載或使用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; } 
} 

我希望有人能解釋這裏發生了什麼。

+0

你WURE這些集合的導航屬性初始化時您使所有屬性都變爲虛擬,從而允許創建更改跟蹤代理?我認爲你錯了。這些集合屬性僅在您使用延遲加載並自動訪問它們時纔會自動實例化。如果不是,他們仍然是空的。 – JotaBe

+0

@JotaBe:好點,但是在更改跟蹤代理的情況下,'Posts'屬性在被訪問之前被初始化。 –

+0

這就是我的意思:始終初始化集合屬性,除非通過延遲加載來加載它,否則會得到「空引用異常」。 AFAIK集合屬性從不初始化,除非它們是懶惰,急切或顯式加載的。把它放在你的類構造函數中不會產生任何干擾。 – JotaBe

回答

2

通常的方式,以避免空引用是初始化在構造函數的集合:

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; 
    } 
} 

不幸的是我無法解釋爲什麼,當你標記所有虛擬屬性沒有得到一個錯誤...

+0

謝謝!初始化構造函數中的屬性意味着從構造函數中訪問虛擬成員,這可能會導致其他問題,所以我不喜歡該解決方案。 我想我可以將自動屬性更改爲支持字段的屬性。 但我想知道爲什麼有差異:) –

+0

我認爲這實際上是我與支持領域去的主要原因 - 我因爲虛擬呼叫從CodeRush得到警告。是的,我想知道它的不同 – Colin

+0

我測試了在構造函數中初始化集合。這不適用於包含更改跟蹤的代理,因爲它會引發'InvalidOperationException':其他信息:無法設置'ChangeTrackingBlog_E2538CFF8F25D3B52885F4D5A327D1183626232C1097A36B54E666D62E02C6FD'類型的'Posts'屬性,因爲集合已經設置爲EntityCollection。 我認爲你是對的,使用後臺創建屬性是此問題的唯一解決方案。 –