ObjectContextは、貧血ドメインモデルを回避するためのEFでエンティティを認識します
質問
エンティティフレームワークでは、コンテキストに添付または取得された各オブジェクト(エンティティ)にDBContextを挿入することは可能ですか?
私はnhibernateの男であり、NHでそれが可能であることを知っています - それがEFの世界で愚かな質問であるなら申し訳ありません。
基本的に、エンティティをコンテキストに関連付けるたびに、フレームワーク自体によってコンテキストのインスタンスに設定されるタイプDBContextのプロパティを持つエンティティの一部を持っていることを望んでいます。理想的には、そのようなクラスには、icontextawareマーカーインターフェイスなどでマークされます。
これをやりたい理由は(=目標)、今回は貧血ドメインモデルアンチパターンを避けたいです。 ObjectContextがエンティティに挿入されている場合、DBにアクセスできるため、ドメインクラス自体にクエリとより複雑なロジックを実装できるようになりました。あなたが私の目標を達成する他の方法を知っているなら(特にWebアプリのコンテキストで)そうしてください。しかし、「あなたはこれをしてはいけない」などの答えを避けてください。ありがとう!!!
解決
あなたはあなたのドメインオブジェクトから永続性の懸念を守りたいので、あなたはこれをするべきではありません=)
ただし、必要に応じて、ObjectContextによって発射されたObjectMaterializedイベントに接続できます。 CTP5では、dbcontextのコンストラクターのようにdbcontextをキャストする必要があります。
((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized +=
this.ObjectContext_OnObjectMaterialized;
次に、objectcontext_onobjectmaterialized(object sender、objectmaterializeventargs e)を実装します。 EventArgsを介して、具体化されたばかりのオブジェクトにアクセスできます。そこから、PocoのObjectContext/DBContextプロパティを設定できます。これは、公開または内部である必要があります。
他のヒント
ドメインを特定の持続性テクノロジーと結合することに加えて、そのレベルでコンテキストを注入することには他のコンサートがあります。たとえば、あなたが注入するコンテキストの生涯は何ですか、そしてその文脈は常に各エンティティに対して同じ生涯を持つべきですか?
私はあなたがエンティティであなたのビジネス方法を定義したいと思っているので、あなたは言うことができます customer.MakeCustomerPreferred
. 。ただし、アプリケーションでそのレベルでビジネスロジックを書く必要なく、これを行う他の方法があります。たとえば、ビジネスイベントを使用できます。これが例です:
public class Customer
{
public void MakeCustomerPreferred()
{
var e = new MakeCustomerPreferredEvent()
{
Customer = this
};
DomainEvents.Current.Handle(e);
}
}
public interface IDomainEvent { }
public interface IHandle<T> where T : IDomainEvent
{
void Handle(T instance);
}
public class MakeCustomerPreferredEvent : IDomainEvent
{
prop Customer Customer { get; set; }
}
DomainEvents
クラスはです 周囲のコンテキスト これにより、特定のドメインイベントのすべてのハンドラーを取得して実行できます。
public class DomainEvents
{
public static DomainEvents Current = new DomainEvents();
public virtual void Handle<T>(T instance)
where T : IDomainEvent
{
var handlers =
YourIocContainer.GetAllInstances<IHandle<T>>();
foreach (var handler in handlers)
{
handler.Handle(instance);
}
}
}
これを整えると、ハンドラーをアーキテクチャのより高いレベルで定義し、各ビジネスイベントのゼロ、1つ、またはより多くのハンドラーをプラグインできます。ハンドラーにコンテキストを注入できます。
トピックスターターが要求したアプローチに従うオプションをクライアントに提供します。これを行うために、ExpressApp Framework(XAF)製品に同様のソリューション(ObjectMaterizedおよびObjectStateManagerのその他のイベント)を実装しました。エンティティは「コンテキスト」と同じ寿命を持っているため、ほとんどのシナリオでは問題なく機能します。これはまた、データモデルとビジネスロジックを設計する際に同じ困難に直面しているクライアントの使いやすさを改善するのに役立ちます。
私たちの場合、ドメインモデルは、ORMコンテキストに特別な「オブジェクトスペース」抽象化があるため、特定の永続性テクノロジーと組み合わされていません(エンティティフレームワークに加えて、当社の製品は社内ORMをサポートしています - エクスプレス永続オブジェクト(XPO ))。
そのため、ビジネスロジックのコンテキストを必要とするエンティティによって実装されることになっているはずのiObjectSpacelinkインターフェイス(単一のiObjectspaceプロパティを使用)をクライアントに提供します。
さらに、最も人気のあるビジネスルールのために、IxafentityObjectインターフェイス(oncreated、onloaded、Onsaving Methodを使用)を提供します。以下は、BCLから両方のインターフェイスを実装しているエンティティの例です。
// IObjectSpaceLink
IObjectSpace IObjectSpaceLink.ObjectSpace {
get { return objectSpace; }
set { objectSpace = value; }
}
// IXafEntityObject
void IXafEntityObject.OnCreated() {
KpiInstance kpiInstance = (KpiInstance)objectSpace.CreateObject(typeof(KpiInstance));
kpiInstance.KpiDefinition = this;
KpiInstances.Add(kpiInstance);
Range = DevExpress.ExpressApp.Kpi.DateRangeRepository.FindRange("Now");
RangeToCompare = DevExpress.ExpressApp.Kpi.DateRangeRepository.FindRange("Now");
}
void IXafEntityObject.OnSaving() {}
void IXafEntityObject.OnLoaded() {}
次に、これらのピースを内部的にリンクするフレームワークのコードを次に示します(以下はエンティティフレームワーク6用です)。
private void ObjectContext_SavingChanges(Object sender, EventArgs e) {
IList modifiedObjects = GetModifiedObjects();
foreach(Object obj in modifiedObjects) {
if(obj is IXafEntityObject) {
((IXafEntityObject)obj).OnSaving();
}
}
}
private void ObjectContext_ObjectMaterialized(Object sender, ObjectMaterializedEventArgs e) {
if(e.Entity is IXafEntityObject) {
((IXafEntityObject)e.Entity).OnLoaded();
}
}
private void ObjectStateManager_ObjectStateManagerChanged(Object sender, CollectionChangeEventArgs e) {
if(e.Action == CollectionChangeAction.Add) {
if(e.Element is INotifyPropertyChanged) {
((INotifyPropertyChanged)e.Element).PropertyChanged += new PropertyChangedEventHandler(Object_PropertyChanged);
}
if(e.Element is IObjectSpaceLink) {
((IObjectSpaceLink)e.Element).ObjectSpace = this;
}
}
else if(e.Action == CollectionChangeAction.Remove) {
if(e.Element is INotifyPropertyChanged) {
((INotifyPropertyChanged)e.Element).PropertyChanged -= new PropertyChangedEventHandler(Object_PropertyChanged);
}
if(e.Element is IObjectSpaceLink) {
((IObjectSpaceLink)e.Element).ObjectSpace = null;
}
}
OnObjectStateManagerChanged(e);
}
public virtual Object CreateObject(Type type) {
Guard.ArgumentNotNull(type, "type");
CheckIsDisposed();
Object obj = CreateObjectCore(type);
if(obj is IXafEntityObject) {
((IXafEntityObject)obj).OnCreated();
}
SetModified(obj);
return obj;
}
この情報があなたを助けることを願っています。