Entity Frameworkでマップされたデータベースに接続されたビジネスルール(基本的には別のDLLにあります)を備えたエンジンの上に、ASP.NET(Webフォーム)でWebサイトを構築しています(3番目の別のプロジェクトで)。
私は最初にEntity Frameworkコンテキストを持つエンジンを設計し、次にさまざまなレポートを表示するWebサイトで作業を続けました。私はひどい設計ミスを犯したと信じています。
このエンジンのモックアップと、レポートページのコードの背後を示します。
エンジン(別のDLL):
public Engine
{
DatabaseEntities _engineContext;
public Engine()
{
// Connection string and procedure managed in DB layer
_engineContext = DatabaseEntities.Connect();
}
public ChangeSomeEntity(SomeEntity someEntity, int newValue)
{
//Suppose there's some validation too, non trivial stuff
SomeEntity.Value = newValue;
_engineContext.SaveChanges();
}
}
そして報告する:
public partial class MyReport : Page
{
Engine _engine;
DatabaseEntities _webpageContext;
public MyReport()
{
_engine = new Engine();
_databaseContext = DatabaseEntities.Connect();
}
public void ChangeSomeEntityButton_Clicked(object sender, EventArgs e)
{
SomeEntity someEntity;
//Wrong way:
//Get the entity from the webpage context
someEntity = _webpageContext.SomeEntities.Single(s => s.Id == SomeEntityId);
//Send the entity from _webpageContext to the engine
_engine.ChangeSomeEntity(someEntity, SomeEntityNewValue); // <- oops, conflict of context
//Right(?) way:
//Get the entity from the engine context
someEntity = _engine.GetSomeEntity(SomeEntityId); //undefined above
//Send the entity from the engine's context to the engine
_engine.ChangeSomeEntity(someEntity, SomeEntityNewValue); // <- oops, conflict of context
}
}
Webページには独自のコンテキストがあるため、エンジンに別のコンテキストのエンティティを与えるとエラーが発生します。私はたまたまknowそれを行わず、独自のコンテキストからEngineエンティティのみを提供します。しかし、これは非常にエラーが発生しやすい設計です。私は今、自分のやり方の誤りを見ています。私は正しい道を知りません。
私は考えています:
安全性、使いやすさ、理解、安定性、速度について、ここで最も妥協するものは何ですか
あなたが必要な場所から100万マイル離れているとは思いません。
実際に何をする必要があるかは、必要な抽象化のレベルと、このアプリケーションの将来をどう予測するかによって異なります。
単一のhttpリクエストで複数のコンテキストを使用したくないのは明らかです。それはあなたに多くの痛みを引き起こします。
最低限、私はあなたのエンジンを変更して、コンテキストの依存関係を(コンストラクターまたはセッターを通じて)注入します。これにより、モックされたコンテキストに挿入することで、テスト目的でエンジンを分離できます。
次に、コンストラクターでエンジンインスタンスを新しくするBasePageクラスを作成することを検討します(新しいコンテキストで渡す)。後続の各ページはBasePageを継承するため、デフォルトでEngineインスタンスが作成されます。
次に、エンジンクラスで作業して、ページに必要なすべての機能を提供します(少しモノリシックになった場合はサブクラス化する可能性があります)。次に、UIと基になるデータベースの間のすべての対話がエンジンを介して行われることを確認します。このようにして、たとえばEngine.Ajax.Get ...などのように、必要に応じてエンジンを拡張できます。
私が言うように、あなたが必要とする抽象化のレベルは、あなたがする必要がある他の設計上の考慮事項を明らかにするかもしれません。あなたはコンテキストの上にリポジトリと作業単位パターンを実装したいと思うかもしれません(技術的にはEFコンテキストはすでにこれを提供しますが、EFに固執するかどうかわからない場合、または代替のデータアクセスオプションを導入する必要があることがわかっている場合は、それを考慮する必要があります)。
依存関係を注入していて、それらの依存関係が「揮発性」である場合は、AutoFacやWindsorなどの制御コンテナーの反転を使用することを検討できます。
MVCではなくWebFormsを使用する理由があると思いますが、私の最初のヒントはこれです:可能であれば、ASP.Net MVCを使用します。
右、それを私の胸から取り出して、続けましょう。
Entity Frameworkを使用しているときに、エンティティストアを使用してデータベースに直接アクセスする2番目の方法は、アーキテクチャを疑うのは当然です。考慮すべき単純なルールは次のとおりです:データベースとの間の唯一の方法
WebFormsのような疲れたテクノロジーに悩まされている場合でも、MVCアーキテクチャを使用することで、MVCブックから有用なページを取り出すことができます。エンジンがモデルレイヤーのほとんどの作業を行っているように聞こえます。実際、もう少し進んでViewModelタイプのアプローチを使用したいように思えます。ViewModelはモデルのように動作しますが、フロントエンドからの作業を容易にする追加の機能があるため、事実上、 DTOですが、フロントエンドやその他のビュー関連のロジックで書きたくないプロパティがある場合は、ViewModelクラスに入ります。
次に、エンティティレイヤーがViewModelオブジェクトを取得できることを確認します(または、エンティティのインターフェイスを作成するので、BookRecord
オブジェクトがある場合は、IBookRecord
インターフェイスを作成して、BookRecord
エンティティが実装し、BookRecordViewModel
も実装します。可能であれば、常にインターフェースを渡す方が優れており、DI、モッキング、および一般的に回復力を構築するのに役立つすべてのものを本当に容易にします。
これによる唯一の真の違いは、エンジンの更新を管理するためのロジックを処理する必要があるということです。インターフェイスからBookRecordViewModel
をプッシュダウンするとき、Idフィールドが設定されているかどうかと、それが設定されているかどうかを確認する必要があります。新しいレコードとして追加しない場合は、クエリと更新を行います。すべてをすでにEngineプロジェクトに実装している場合もありますが、その場合はまったく問題ありません。