4

我正在使用帶有Generic Repository模式的EF6。最近我遇到了一個問題,試圖一次刪除一個複合實體。這裏是一個簡化的情景:EF急切地加載導航屬性問題

public class Parent 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual ICollection<Child> Children { get; set; } 
} 
public class Child 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 

    [ForeignKey("Parent")] 
    public int ParentId { get; set; } 

    public virtual Parent Parent { get; set; } 
} 

刪除與相關的兒童,我做這樣的事情父實體:

public virtual T GetById(int id) 
{ 
    return this.DBSet.Find(id); 
} 
public virtual void Delete(T entity) 
{ 
    DbEntityEntry entry = this.Context.Entry(entity); 

    if (entry.State != EntityState.Deleted) 
    { 
     entry.State = EntityState.Deleted; 
    } 
    else 
    { 
     this.DBSet.Attach(entity); 
     this.DBSet.Remove(entity); 
    } 
} 

首先,我找到ID父對象,然後將它傳遞給刪除方法將其狀態更改爲刪除。 context.SaveChanges()最終提交了刪除。

這工作得很好。查找方法只拉起了父對象和刪除工作,因爲我有兒童上啓用刪除級聯。

但我在兒童類增加了一個屬性的時刻:

[ForeignKey("Gender")] 
public int GenderId { get; set; } 

public virtual Gender Gender { get; set; } 

出於某種原因,EF開始對Parent.Find()方法拉動相關的兒童。因此,我得到以下錯誤:

The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

即使在還原更改(刪除Gender屬性)後問題仍然存在。我無法理解這種奇怪的行爲!

我想要做的就是刪除父對象以及子對象。 還有周圍的一些解決方案,但沒有真正成爲我的目的:

  1. 轉到惰性加載到假 - this.Configuration.LazyLoadingEnabled = FALSE;這工作,但在我的真實應用程序中,我需要此屬性爲true。
  2. 首先迭代所有孩子並刪除它們,然後刪除父母。這似乎充其量是一種解決方法,而且非常冗長。
  3. 使用Remove()而不是將EntityState更改爲Deleted。我需要跟蹤審覈更改,以便EntityState在那裏提供幫助。

有人可以解釋爲什麼EF加載相關的實體,即使我沒有使用它們嗎?

+0

當您恢復時,您是否還恢復了數據庫?你的問題可能與DB有關。 – Sefe

+0

是的,我做到了。我放棄了數據庫並重新創建了它。 –

+0

我懷疑它是與數據庫相關的,因爲我能夠用sql查詢刪除父實體,並在子級上刪除作業級聯。 –

回答

2

看來問題與上下文的生命週期有關。我使用Unit Of Work並使用ninject將其注入到服務層。

kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope(); 

UnitOWork類實現IDisposable。

public bool DeleteView(int viewId) 
    { 
     // This is a workaround. It seems ninject is not disposing the context. 
     // Because of that all the info (navigation properties) of a newly created view is presisted in the context. 
     // Hence you get a referential key error when you try to delete a composite object. 
     using (var context = new ApplicationDbContext()) 
     { 
      var repo = new GenericRepository<CustomView>(context); 
      var view = repo.GetById(viewId); 
      repo.Delete(view); 
      context.SaveChanges(); 
     } 
     //var model = _unitOfWork.CustomViews.GetById(viewId); 
     //_unitOfWork.CustomViews.Delete(model); 
     //_unitOfWork.Save(); 

     return true; 
    } 

評論的代碼拋出和錯誤,而未評論的(使用塊)的作品。此調用之前的一個控制器方法加載CustomView實體(它與Parent具有類似的子結構列表)。並且可以觸發後續的用戶操作以刪除該視圖。

我認爲這與上下文沒有被處理有關。也許這與Ninject或UnitOfWork有關,我還沒有能夠確定。 GetById()可能會從上下文緩存中拉出整個實體或其他東西。

但上述解決方法適用於我。只是把它放在那裏,以便它可以幫助別人。

+0

使用Autofac - 對生命週期,範圍,處置和工作單元(每個請求)提供更成熟和易於理解的解決方案。 –

+0

換句話說,整個帖子是不正確的,與EF沒有任何共同之處,但是模式,注射等等。我認爲一旦賞金到期,這個問題應該被刪除,因爲這只是誤導,我不明白它可以提供什麼幫助有人。 –