2011-10-25 91 views
14

我正在使用實體框架4.0的WPF應用程序。當我嘗試保存該對象時,出現主鍵異常,但主鍵是AutoIncremented字段,我無法理解該異常的原因。阻止實體框架爲導航屬性插入值

因此,在嘗試了這一步之後,以及一些調試和使用SQL分析器之後,我發現在插入我的對象之前,必須在父表中插入一條記錄,因爲我設置了該對象的導航屬性。

所以關鍵是如果試圖插入Employee對象並將其部門設置爲Employee.Department = deptObject,那麼將新記錄設置爲插入到部門對象上。

請好好建議我通過哪些導航屬性對象不會插入數據庫,任何屬性或任何方法,任何東西。

感謝

+0

什麼主鍵異常?這可能是因爲您沒有將模型的主鍵屬性StoreGeneratedPattern設置爲Identity。如果這不是這種情況,請您提供一些代碼示例來查看? – philt5252

回答

40

如果您錯誤地使用分離的實體,這是EF如何工作的方式。我想你使用的是這樣的:

var employee = new Employee(); 
employee.Department = GetDepartmentFromSomewhere(departmentId); 

... 

using (var context = new YourContext()) 
{ 
    context.Employees.AddObject(employee); 
    context.SaveChanges(); 
} 

此代碼準備員工實體,增加了對現有部門的參考,並將新員工保存到數據庫。哪裏有問題?問題是,AddObject不會只添加員工,而是添加整個對象圖。這就是EF的工作原理 - 你不能有對象圖,其中部分對象連接到上下文,而不是部分對象。 AddObject將圖形中的每個對象都添加爲一個新對象(新建一個=在數據庫中插入)。因此,您必須更改操作順序或手動修改實體狀態,以便您的上下文知道該部門已存在。

首先解決方案 - 用於裝載部門同樣的情況下,節省員工:

using (var context = new YourContext()) 
{ 
    var employee = new Employee(); 
    ... 
    context.Employees.AddObject(employee); 

    employee.Department = context.Departments.Single(d => d.Id == departmentId); 
    context.SaveChanges(); 
} 

二的解決方案 - 實體單獨連接上下文後,使實體之間的參考:

var employee = new Employee(); 
... 

var department = GetDepartmentFromSomewhere(departmentId); 

using (var context = new YourContext()) 
{ 
    context.Employees.AddObject(employee); 
    context.Departments.Attach(department); 
    employee.Department = department; 

    context.SaveChanges(); 
} 

三解決方案 - 手動修正部門的狀態,以便上下文不會再次插入:

var employee = new Employee(); 
employee.Department = GetDepartmentFromSomewhere(departmentId); 

... 

using (var context = new YourContext()) 
{ 
    context.Employees.AddObject(employee); 
    context.ObjectStateManager.ChangeObjectState(employee.Department, 
               EntityState.Unchanged); 
    context.SaveChanges(); 
} 
+1

非常感謝先生。我只有一個詞,爲你而出名。 –

+0

我使用從'DbContext'派生的類,下面的和'DbContext'的第三個解決方案具有相同的效果'context.Entry(employee.Department).State = EntityState.Unchanged;' – twnaing

+3

在第三種解決方案中,如果發生if該部門的對象有另一種關係? AddObject是否標記了所有內容?你怎麼可以遞歸地將事情標記回原來的位置? – Bobson

0

當您設置部門的員工 - 我認爲你應該檢驗部門從數據庫中檢索並使它實體。
另外,您可以放置​​deprtment的id(外鍵屬性),而不是設置部門導航屬性。

1

除了Ladislavs已經提供的3個解決方案之外,我還想添加第4個解決方案。事實上,它是來自Naor的簡短答案的詳細版本。我與實體框架版本工作6


分配教研室ID給員工,而不是部門對象

我往往有除了一個「外鍵值」屬性導航屬性在我的模型類中。

所以在Employee類我有一個Department屬性,也int類型的DepartmentId(使INT可空,如果它可能是一個Employee沒有Department):

public class Employee 
{ 
    public int Id { get; set; } 

    public String EmployeeName { get; set; } 


    #region FK properties 

    public Department Department { get; set; } 

    public int? DepartmentId { get; set; } 

    #endregion 
} 

你現在可以做的是隻需設置DepartmentId: 所以不是:

employee.Department = departmentObject; 

剛剛設置:

employee.DepartmentId = departmentObject.Id; 

employee.DepartmentId = departmentid 

現在就加入員工打電話SaveChanges時,只有員工得到保存,並沒有創建新的部門。但是由於分配的部門ID,從EmployeeDepartment的參考值設置正確。


更多信息

我通常會讀,只有當/處理員工訪問Employee類的Department對象。 創建或更新員工時,我會使用Employee類的DepartmentId屬性進行分配。

不分配給EmployeeDepartment酒店有一個缺點:可能因爲調用SaveChanges和重新閱讀它是不可能看到或者使用EmployeeDepartment對象的員工之前使調試更加困難。在EF6

這是指Ladislavs解數3.

隨着EF6


定影實體狀態信息是這樣做的方式:

_context.Entry(employee.Department).State = EntityState.Unchanged;