4

我在我的數據模型中實現了一些每種類型的表繼承(基本上有一個BaseEntity類型,包含我的項目的所有基本信息和從BaseEntity項目繼承的Employer類型)。一切似乎設置正確,當使用實體(通過ADO.net數據服務或通過Linq實體),我可以看到Employer類型,似乎沒有問題。當我創建一個新的Employer實體並嘗試保存它時,問題就開始了。無法保存實體框架繼承的類型

在似乎不是.AddToEmployer項目(僅限於和AddObjectAddToBaseEntity)的上下文中。

如果我使用AddObject("Employer", NewEmployer)我得到和錯誤消息:

的EntitySet的名稱「DataEntities.Employer」找不到。

如果我使用AddToBaseEntity(NewEmployer)我得到一個錯誤信息:

無法確定相關的操作有效的排序。由於外鍵約束可能存在依賴關係,所以模型需求會產生生成的值。

我錯過了設置繼承的步驟嗎?有沒有一些特定的方法來保存被繼承的對象?我究竟做錯了什麼?我認爲基本問題是我應該有一個AddToEmployer,我需要做些什麼才能讓它暴露出來?看起來很奇怪,因爲我可以在客戶端看到Employer類型,並且可以執行諸如以下操作,這似乎很奇怪:

var NewEmployer = new Employer() - 這似乎表明我可以看到Employer類型正常。

回答

2

我改變了一些東西,並能夠得到這個工作。我不太確定基本問題是什麼,但是想發佈我所做的參考。

重建表格:我重建了只有ID/Key列和單個數據列的表格。

刪除了額外的自動遞增字段:我在BaseEntity和僱主上有一個自動遞增的ID。我刪除了Employer上的自動遞增ID,並將Employer.BaseEntityID列和外鍵恢復爲BaseEntity.BaseEntityID。 (這似乎是罪魁禍首,但我在這個被允許的印象之下)

不幸的是,這然後導致的問題,實體框架中enherited類不能有導航屬性(所有的導航屬性必須在基地實體),所以繼承將證明對我們的需求是不可用的。

1

您沒有將僱主定義爲實體集,就像實體類型一樣。這就是你在上下文對象中缺少AddToEntity的原因。總是爲一個類層次結構設置一個實體,在這種情況下,它是BaseClass實體集。

如果您想獲得實體集'Employer',您可以嘗試手動編輯edmx文件並添加新的實體集'Employer',然後將實體類型'Employer'設置爲屬於該實體集。這不應該很難,我已經做了很多次。

我不確定是否有一些更常規的解決方案。

2

那麼你只能得到一個實體集pr。基類所以.AddToBaseEntity就是這樣的解決方案。

但它聽起來像你在你的模型中有一個循環依賴,所以Entity框架不能找出保存的順序。

檢查您是否有派生實體的baseentity的外鍵並更新模型。

7

我的名字是Phani,我在ADO.NET數據服務團隊工作。

ResolveNameResolveType方法將幫助您自定義客戶端在發送到服務器的負載中寫入的類型信息以及服務器的響應負載如何實現。

他們幫助您解決在客戶端類型和在許多情況下是有用的,一對夫婦的例子是:

  1. 實體的類型層次結構是在客戶端上的不同比服務器。
  2. 服務公開的實體類型參與繼承,並且您希望使用客戶端上的派生類型。

ResolveName用於更改我們在向服務器發出請求時放置在網絡上的實體的名稱。

考慮這個數據模型: 在服務器

public class Employee { 
    public int EmployeeID {get;set;} 
    public string EmployeeName {get;set;} 
} 

public class Manager:Employee { 
    public List<int> employeesWhoReportToMe {get;set;} 
} 

當您使用客戶端在提交更改到服務器與Manager實體類型, 的情況下工作,我們期望類型信息是存在在實體參與繼承的有效載荷中。

context.AddObject("Employees",ManagerInstance); <-- add manager instance to the employees set. 
context.SaveChanges(); 

然而,當客戶端序列化這個有效載荷,它把在「僱員」作爲不被期望是什麼服務器上的類型名稱 。 因此,你必須提供一個名字解析的客戶端上,

context.ResolveName = delegate(Type entityType){ 
    //do what you have to do to resolve the type to the right type of the entity on the server 
    return entityType.FullName; 
} 

一個類型解析器以同樣的方式使用。

context.ResolveType = delegate(string entitySetName){ 
    //do what you have to do to convert the entitysetName to a type that the client understands 
    return Type.GetType(entitySetName); 
} 
1

即將在兩年後,但在保持其相關的搜索流量的利益,這裏有一個方法,我很快在一個方便的類工作圍繞這一點,我們使用快速填充我們與分期資料數據庫。

不確定早期版本,但Entity Framework 4允許您將對象作爲基礎對象轉儲到上下文中,然後框架計算出服務器端引用。因此,您不會使用AddToInheritedObjects()(反正不推薦),而是使用ObjectSet <> .Add()方法。

這裏是一個輔助類例如:

public ContextHelper 
{ 
     … 
     _context = ModelEntities(); 

     public T Create<T>() where T : class 
     { 
      // Create a new context 
      _context = new ModelEntities(); 

      // Create the object using the context (can create outside of context too, FYI) 
      T obj = _context.CreateObject<T>(); 

      // Somewhat kludgy workaround for determining if the object is 
      // inherited from the base object or not, and adding it to the context's 
      // object list appropriately.  
      if (obj is BaseObject) 
      { 
       _context.AddObject("BaseObjects", obj); 
      } 
      else 
      { 
       ObjectSet<T> set = _context.CreateObjectSet<T>(); 
       set.AddObject(obj); 
      } 

      return obj; 
     } 
     … 
} 

因此,假設您有以下:

class ObjectOne : BaseObject {} 
class ObjectTwo {} 

你可以很容易的實體添加到上下文:

ContextHelper ch = ContextHelper() 
ObjectOne inherited = ch.Create<ObjectOne>(); 
ObjectTwo toplevel = ch.Create<ObjectTwo>(); 
… 

記憶,當然,ContextHelper應該有一個公共的Save()方法,它調用_context.SaveChanges() - o您應該有其他方法將對象更改爲數據存儲。

這可能不是對任何關於繼承的問題的直接回應,但希望能給人們一個回答具體問題的出發點。

+0

作爲一個說明,我忘了提及 - 實現IDisposable使這真的很好。 – JohnMetta 2010-12-23 20:16:39