2014-08-29 233 views
1

我使用的是代碼優先的實體框架,其基本上下文僅由標準IDbSet集合組成,其中T只是一個POCO類。在我的上下文中,我禁用了懶加載。雖然我的模型類中有「導航屬性」,但我已經從它們中刪除了虛擬關鍵字。實體框架加載相關實體

存儲庫中的「全部獲取」方法會執行一些自定義篩選,以確保當前用戶只能看到他們擁有的數據,除非他們是管理員。我遇到了一個特殊的問題,我以管理員身份登錄,該管理員也與某些記錄相關聯。由於我登錄的實體是在上下文中加載的,即使我已禁用延遲加載,虛擬刪除並且未使用包含或加載,結果中與我的配置文件具有關聯的對象具有導航屬性自動設置。

這不是我項目的代碼,只是一個例子來展示我在做什麼的想法。它可能有錯別字和語法錯誤。

public class Record 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
    public Owner Owner { get; set; } //No virtual keyword 
    public Guid OwnerId { get; set; } 
} 

public class Owner 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
    public Collection<Record> Records { get; set; } //No virtual keyword 
} 

public class Context : DbContext 
{ 
    IDbSet<Owner> Owners { get; set; } 
    IDbSet<Record> Records { get; set; } 
    public static Context Create() 
    { 
     Context context = new Context(); 
     context.Configuration.LazyLoadingEnabled = false; //Lazy loading disabled 
     return context; 
    } 
} 

public class Repository 
{ 
    private Context Context { get; set; } 
    public Owner CurrentOwner { get; private set; } 
    public Repository() 
    { 
     Context = Context.Create(); 

     //Code here to get the application user and look up an associated "owner" 
     //entity if the user is an "owner" (they could just be an administrator) 
     //but the GetCurrentOwnerOrNull uses the Context to find the user 
     CurrentOwner = GetCurrentOwnerOrNull(); 
    } 

    public IQueryable<Record> GetRecords(bool asAdmin) 
    { 
     IQueryable<Record> records = Context.Records; //Not including or loading Owner 
     if (asAdmin) 
     { 
      //Verify that the application user is an admin and throw exception otherwise 
     } 
     else 
     { 
      if (CurrentOwner == null) 
      { 
       //Throw a security exception 
      } 
      records = records.Where(r => r.OwnerId == CurrentOwner.Id); 
     } 
     return records; 
    } 
} 

如此反覆,與上面的問題是,如果我是運行該代碼作爲業主,是否不論是不是管理員,那麼這些是我自己的記錄將Owner屬性設置,而不是空。我希望實體框架擺脫我的業務,​​而不是自動設置它。它會導致下游出現問題,尤其是以管理員和所有者身份運行代碼時,所以您可以使用Owner = null和一些擁有Owner設置的記錄返回一些記錄。它很煩人。請讓我停下來。

+0

作爲更新,我只是測試,如果在代碼中GetCurrentOwnerOrNull我創建一個新的上下文來做這個查詢,然後處理它,它擺脫了這個問題......但我離開問題打開現在如果有人知道另一種方法來簡單地在Entity Framework中關閉「功能」。 – 2014-08-29 18:50:31

+0

你是否需要你的上下文*將*保存到緩存中的這些對象上? – 2014-08-29 18:52:16

回答

4

This bug實際上是一個功能。即使實體彼此獨立加載,實體框架也會自動在相同的上下文中連接關聯。

讓我們假設以下幾點:

public class Person 
{ 
    public Person() 
    { 
    this.Pets = new List<Pet>(); 
    } 

    public int Id { get; set; } 
    public string Name { get; set; } 

    public virtual ICollection<Pet> Pets { get; set; } 
} 

public class Pet 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public int PersonId { get; set; } 

    public virtual Person Owner { get; set; } 
} 

讓還假設這些被正確關聯DB-第一或帶屬性/流利API代碼優先。

數據庫:

Persons 
Id Name 
1 Erik Philips 

Pets 
Id Name PersonId 
1 Joe 1 

這是會發生什麼:

var pet = DbContext.Pets.FirstOrDefault(id => id == 1); 
var person = DbContext.Persons.FirstOrDefault(id => id == 1); 

Assert.AreEqual(person.Pets.Count(), 1); 
Assert.IsNotNull(pet.Person); 
Assert.AreEqual(pet.Person, person); 

這可以發生的原因是因爲上下文將保留對象在它的高速緩存。如果您不關心持有這些對象的上下文,則需要使用AsNoTracking()

+0

我嘗試在上下文中將ProxyCreationEnabled設置爲false,但它沒有解決問題。我沒有嘗試AsNoTracking()路由,因爲如果必須確保在每個查詢中都指定了這一點,那麼會非常煩人,但是還有一些其他情況下我們會檢查所有權並導致問題,我會給它一個嘗試。如果只有一個設置可以在上下文級別關閉緩存,但ProxyCreationEnabled似乎不是該設置,那將會很不錯。 – 2014-08-29 19:22:20

+0

好的,我嘗試了AsNoTracking(),它的功能就像一個魅力。標記爲答案...謝謝! – 2014-08-29 19:28:26

+0

很奇怪的人,謝謝你告訴我。我必須弄清楚爲什麼兩者有區別。我假設ProxyCreationEnabled設置爲false會導致所有對象返回AsNoTracking()。 – 2014-08-29 19:29:14