Entity Frameworkの継承型を保存できません
-
03-07-2019 - |
質問
データモデルにtype-per-type継承を実装しました(基本的に、アイテムのすべての基本情報を含むBaseEntity
タイプと、Employer
アイテムを継承する.AddToEmployer
タイプがあります)。すべてが正しくセットアップされているように見え、エンティティを使用する場合(ADO.net Data ServicesまたはLinq to Entitiesのいずれかを使用)、AddObject
タイプが表示され、問題ないように見えます。この問題は、新しいAddToBaseEntity
エンティティを作成して保存しようとすると始まります。
AddObject("Employer", NewEmployer)
アイテムではないように見えるコンテキスト(AddToBaseEntity(NewEmployer)
またはAddToEmployer
のみ)。
var NewEmployer = new Employer()
を使用すると、次のエラーメッセージが表示されます:
EntitySet名 'DataEntities.Employer'が見つかりませんでした。
<=>を使用すると、次のエラーメッセージが表示されます:
依存操作の有効な順序を決定できません。外部キーの制約、モデルの要件、または生成された値の保存により、依存関係が存在する場合があります。
継承を設定するステップを見逃していませんか?継承されたオブジェクトを保存する特定の方法はありますか?何が間違っていますか?基本的な問題は<=>が必要だと思いますが、それを公開するにはどうすればよいですか?私はクライアント側で雇用者タイプを見ることができ、次のようなことができるので、それがオプションではないことは奇妙に思えます:
<=>-これは、雇用者のタイプがうまく見えることを示唆しているようです。
解決 3
いくつかの点を変更し、これを機能させることができました。基本的な問題が何であるかは特にわかりませんが、参照のために自分がしたことを投稿したかったです。
テーブルの再構築:ID / Key列と単一のデータ列のみでテーブルを再構築しました。
余分な自動インクリメントフィールドを削除しました:BaseEntityとEmployerに自動インクリメントIDがありました。 Employerの自動インクリメントIDを削除し、Employer.BaseEntityID列とBaseEntity.BaseEntityIDに戻る外部キーを取得しました。 (これが犯人だったようですが、これは許可された印象を受けました)
残念ながら、これはエンティティフレームワークの継承されたクラスがナビゲーションプロパティを持つことができないという問題につながります(すべてのナビゲーションプロパティはベースエンティティ上にある必要があります)。 >
他のヒント
私の名前はPhaniであり、ADO.NET Data Servicesチームで働いています。
ResolveName
およびResolveType
メソッドは、クライアントがサーバーに送信するペイロードに書き込むタイプ情報と、サーバーからの応答ペイロードを具体化する方法をカスタマイズするのに役立ちます。
クライアントで型を解決するのに役立ち、多くのシナリオで役立ちます。いくつかの例を次に示します。
- エンティティのタイプ階層は、サーバーとは異なるクライアント上にあります。
- サービスによって公開されるエンティティタイプは継承に参加するため、クライアントで派生タイプを操作したい。
<=>を使用して、サーバーにリクエストを送信するときにワイヤに配置するエンティティの名前を変更します。
このデータモデルを検討します。 サーバー上
public class Employee {
public int EmployeeID {get;set;}
public string EmployeeName {get;set;}
}
public class Manager:Employee {
public List<int> employeesWhoReportToMe {get;set;}
}
クライアントを使用してManager Entity Typeのインスタンスを操作する場合、 サーバーに変更を送信すると、エンティティが継承に参加するときにペイロードにタイプ情報が存在することが期待されます。
context.AddObject("Employees",ManagerInstance ); <-- add manager instance to the employees set.
context.SaveChanges();
ただし、クライアントがこのペイロードをシリアル化すると、<!> quot; Employee <!> quot;型名として これはサーバーで予期されているものではありません。 したがって、クライアントに名前リゾルバを提供する必要があります。
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);
}
エンティティセットprのみを取得します。基本クラスなので、.AddToBaseEntityはそのようなソリューションです。
ただし、モデルに循環依存関係があるように思われるため、Entityフレームワークは保存する順序を判断できません。
派生エンティティのベースエンティティへの外部キーがあることを確認し、モデルを更新します。
エンティティタイプのように、エンティティセットとしてEmployerが定義されていません。これは、コンテキストオブジェクトにAddToEntityが欠落している方法です。 1つのクラス階層に常に1つのエンティティセットがあります。この場合は、BaseClassエンティティセットです。
エンティティセット「Employer」を取得する場合は、edmxファイルを手動で編集し、新しいエンティティセット「Employer」を追加してから、エンティティタイプ「Employer」をそのエンティティセットに属するように設定できます。それは難しくないはずです、私は何度もやったことがあります。
もっと定期的な解決策があるかどうかわかりません。
2年以上後になりますが、検索トラフィックとの関連性を維持するために、データベースにステージングデータをすばやく入力するために使用していた便利なクラスでこれをすばやく回避する方法を次に示します。
以前のバージョンについてはわかりませんが、Entity Framework 4では、オブジェクトをベースオブジェクトとしてコンテキストにダンプできます。その後、フレームワークはサーバー側の参照を算出します。したがって、AddToInheritedObjects()(とにかく非推奨)は使用せず、ObjectSet <!> lt; <!> gt; .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には_context.SaveChanges()を呼び出すパブリックSave()メソッドが必要であること、またはオブジェクトの変更をデータストアにプッシュする他の方法が必要であることを思い出してください。
これは、継承に関する特定の質問への直接的な回答ではないかもしれませんが、具体的に答えるための出発点を人々に提供することを望みます。