2011-07-21 53 views
2

我在使用Entity Framework 4.1 Code First的ASP.NET MVC 3應用程序中遇到了一個有趣的錯誤。我有三個類/表按順序連接。有一個Invitation參照Project然後參考Company什麼會導致實體框架在現有數據上保存未加載(但延遲加載)的引用?

當我加載一家公司並保存它時,一切都很好。項目也是如此。但是,當邀請被編輯和保存時,它會抹去公司中的字段。他們都只是空白!

當我編輯項目時,我需要顯示公司的一些信息,所以我明確加載了.Include(x => x.Company)。當我編輯邀請時,我不需要該公司,所以我沒有打擾包括它。

我想如果對象沒有加載過,那麼EF不應該有任何理由將其標記爲已編輯,對不對?

更新:通過註釋掉代碼行進行了很多調試後,我將其縮小了一些。

正在清除的實際對象是公司引用的Contact對象。它並沒有真正被清除太多,因爲在構造函數中創建了一個新的聯繫人(所以對於新公司不會爲空)。

所以我想這改變了我的問題:有沒有辦法有一個引用屬性設置爲默認值而不會破壞EF?

public class InvitationController 
{ 
    [HttpPost] 
    public RedirectToRouteResult AcceptInvitation(int id, int companyId, int projectId, Invitation invitation) 
    { 
     // This line triggered the problem by loading a company, without 
     // eagerly loading the contacts. 
     CheckAuthorizationEdit(companyId, CommunicationService.GetById(id)); 

     var dbResponse = InvitationService.GetPreviousResponse(companyId, projectId); 
     dbResponse.WillBid = invitation.WillBid; 
     InvitationService.Save(dbResponse); 

     return RedirectToAction("Response", new { id, companyId }); 
    } 

    private void CheckAuthorizationEdit(int companyId, Communication communication) 
    { 
     var companyIds = communication.DistributionList.Companies.Select(c => c.Id).ToList(); 
     //CheckAuthorization(companyIds); 
    } 
} 

public class InvitationService 
{ 
    public Invitation GetPreviousResponse(int companyId, int projectId) 
    { 
     return (from invitation in _db.Invitations 
       where invitation.ProjectId == projectId && invitation.SenderCompanyId == companyId 
       select invitation).SingleOrDefault(); 
    } 

    public void Save(Invitation invitation) 
    { 
     _db.SaveChanges(); 
    } 
} 

public class Invitation 
{ 
    public int Id { get; set; } 
    public int ProjectId { get; set; } 
    [ForeignKey("ProjectId")] 
    public virtual Project Project { get; set; } 
    // ... 
} 


public class Project 
{ 
    public int Id { get; set; } 
    public int CompanyId { get; set; } 
    [ForeignKey("CompanyId")] 
    public virtual Company Company { get; set; } 
    // ... 
} 

public class Company 
{ 
    public Company() 
    { 
     MainContact = new Contact(); 
    } 

    public int Id { get; set; } 
    public virtual Contact MainContact { get; set; } 
    // ... 
} 

public class Contact 
{ 
    public int Id { get; set; } 
    public string AddressLine1 { get; set; } 
    // ... 
} 
+1

對!或者換句話說:如果對象沒有加載,那麼根本就沒有任何EF可以標記的東西。如果現有公司的字段在數據庫中被覆蓋,那麼在調用'SaveChanges'之前,在'Modified'狀態下的上下文中必須有一個具有相同鍵和空字段的實體。但是如果沒有看到代碼,很難說出發生了什麼。 – Slauma

+0

我會嘗試提取足夠的代碼以發佈,而不會太過壓倒。相關部分分散一點。 –

+0

獲取代碼發佈時,我注意到問題實際上是由引用屬性指向新的id而不是被覆蓋的舊引用的字段引起的。 –

回答

4

如果我理解正確的,你有這樣的事情:

public class Company 
{ 
    public Company() 
    { 
     MainContact = new Contact(); 
    } 

    public int Id { get; set; } 
    public virtual Contact MainContact { get; set; } 
} 

一個簡單的代碼是這樣的...

var company = context.Companies.Find(1); 
context.SaveChanges(); 

...確實會創建一個新的空接點在數據庫中。

我要畫的主要結論是:不要在構造函數中實例化引用導航屬性!(實例化導航集合是可以的,只要你把它們的內容留空即可。在構造函數中實例化複雜類型的屬性也很好,因爲它們不是其他實體。)

如果你想確保創建一個新的公司,也許在Company類的靜態工廠方法是更好的選擇一個新的聯繫人:

public static Company CreateNewCompany() 
{ 
    return new Company { MainContact = new Contact() }; 
} 

這也將工作:

var company = context.Companies.Find(1); 
context.Entry(company.MainContact).State = EntityState.Detached; 
context.SaveChanges(); 

但這樣的程序LO oks真的很可笑。

編輯:

它的方式自動變化檢測導致的行爲。此代碼...

context.Configuration.AutoDetectChangesEnabled = false; 
var company = context.Companies.Find(1); 
context.SaveChanges(); 

...不會創建新的聯繫人。它是在 內部工作的變化檢測,它認爲識別在company作爲一個新的實體,並將其置於上下文中的Added狀態。

+0

對,你已經更新了代碼以包含構造函數。我傾向於工廠方法。 –

相關問題