こだかたろうです : New EntityFramework
不信任投票で挙がっていた点がかなり改善されそうですね。
レイジーロードがサポートされることはなおきさんから聞いていましたが、まさか PI もサポートされるとは。
気になるのは PI レベルがどのくらいなのかですね。
How to: Define Custom Persistence-Ignorant Objects (Entity Framework) を見る限り POCO は余裕でクリアしてますが、Requirements for Using Persistence-Ignorant Objects (Entity Framework) を見るとやはりいくつか制約があります。
以下、英語苦手なのできっと間違ってる所があります。(つか、自分で意味がよくわかってない所もあるので…。誰かつっこみお願いします T-T)
- カスタムデータクラスは public でなければならない。
- カスタムデータクラスは sealed にできない。
- カスタムデータクラスは abstract にできない。
- カスタムデータクラスは IEntityWithChangeTracker や IEntityWithRelationships を実装できない。
- 概念モデル内のエンティティ型のプロパティにマッピングされた各プロパティは public でなければならない。
- カスタムデータクラスはデフォルトコンストラクタを提供しなければならない。
- 概念モデル内で定義されたナビゲーションプロパティは、対応するナビゲーションプロパティがカスタムデータクラスになければならない。関連オブジェクトの遅延ロードをサポートするためには、このプロパティを virtual として宣言する必要があり、sealed と宣言することはできない。
- 一対多または多対多の関連を表すナビゲーションプロパティは、ICollection<T> (T は関連端の型) を実装する型を返さなければならない。
- データモデル内のエンティティ型のプロパティにマッピングされたプロパティは、virtual で、getter 及び setter を実装しなければならない。
- 概念モデル内の複合プロパティは、参照型を返すカスタムデータクラスのプロパティにマッピングしなければならない。複合プロパティを、値型や列挙型にマッピングすることはできない。
しかし、循環参照を伴うドメインオブジェクトでは、リッチコンストラクタの使用に問題が生じる。
例えば、次の Hoge クラスと Fuga クラスのような場合だ。
class Hoge
{
readonly Fuga _f;
public Hoge(Fuga f)
{
if (f == null)
{
throw new ArgumentNullException("f");
}
this._f = f;
}
}
class Fuga
{
readonly Hoge _h;
public Fuga(Hoge h)
{
if (h == null)
{
throw new ArgumentNullException("h");
}
this._h = h;
}
}
Hoge のコンストラクタでは Fuga のインスタンスを必要とする。Fuga のコンストラクタでは Hoge のインスタンスを必要とする。これは通常、決してインスタンス化することができない。
しかし、.NET では、コンストラクタを呼び出さずにインスタンス化することができる。そして、リフレクションを使用すればコンストラクタを後から呼び出すこともできる。
つまり、次のようなコードでリッチコンストラクタの循環参照問題は克服できる。
// using System;
// using System.Runtime.Serialization;
// using System.Reflection;
Hoge h = (Hoge)FormatterServices.GetUninitializedObject(typeof(Hoge));
Fuga f = new Fuga(h);
ConstructorInfo ctor = typeof(Hoge).GetConstructor(new[] { typeof(Fuga) });
ctor.Invoke(h, new[] { f });
なお、コンストラクタを後から呼び出す代わりに、リフレクションで直接フィールドを設定するという方法も取れる。しかし、直接フィールドを設定する理由はないし、メリットもない。むしろ、循環参照の問題さえなければ普通にリッチコンストラクタでインスタンス化しているわけなのだから、リッチコンストラクタを使うべきだろう。