web-dev-qa-db-ja.com

Entity Frameworkを使用したASP.NETプロジェクトのデザインパターン

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エンティティのみを提供します。しかし、これは非常にエラーが発生しやすい設計です。私は今、自分のやり方の誤りを見ています。私は正しい道を知りません。

私は考えています:

  • エンジンで接続を作成し、それをWebページに渡します。常にエンジンをインスタンス化し、プロパティからコンテキストにアクセスできるようにし、それを共有します。考えられる問題:その他の競合?スロー? AJAXに拡張したい場合の並行性の問題?
  • Webページから接続を作成してエンジンに渡す(依存関係の注入だと思いますか?)
  • IDを通じてのみ話します。必ずしも実用的ではない冗長性を作成し、古風に聞こえます。しかし、それと同時に、私はとにかくフェッチする必要があるIDとして、ページからのものを既に回復しています。

安全性、使いやすさ、理解、安定性、速度について、ここで最も妥協するものは何ですか

2
MPelletier

あなたが必要な場所から100万マイル離れているとは思いません。

実際に何をする必要があるかは、必要な抽象化のレベルと、このアプリケーションの将来をどう予測するかによって異なります。

単一のhttpリクエストで複数のコンテキストを使用したくないのは明らかです。それはあなたに多くの痛みを引き起こします。

最低限、私はあなたのエンジンを変更して、コンテキストの依存関係を(コンストラクターまたはセッターを通じて)注入します。これにより、モックされたコンテキストに挿入することで、テスト目的でエンジンを分離できます。

次に、コンストラクターでエンジンインスタンスを新しくするBasePageクラスを作成することを検討します(新しいコンテキストで渡す)。後続の各ページはBasePageを継承するため、デフォルトでEngineインスタンスが作成されます。

次に、エンジンクラスで作業して、ページに必要なすべての機能を提供します(少しモノリシックになった場合はサブクラス化する可能性があります)。次に、UIと基になるデータベースの間のすべての対話がエンジンを介して行われることを確認します。このようにして、たとえばEngine.Ajax.Get ...などのように、必要に応じてエンジンを拡張できます。

私が言うように、あなたが必要とする抽象化のレベルは、あなたがする必要がある他の設計上の考慮事項を明らかにするかもしれません。あなたはコンテキストの上にリポジトリと作業単位パターンを実装したいと思うかもしれません(技術的にはEFコンテキストはすでにこれを提供しますが、EFに固執するかどうかわからない場合、または代替のデータアクセスオプションを導入する必要があることがわかっている場合は、それを考慮する必要があります)。

依存関係を注入していて、それらの依存関係が「揮発性」である場合は、AutoFacやWindsorなどの制御コンテナーの反転を使用することを検討できます。

2
Norman Noble

MVCではなくWebFormsを使用する理由があると思いますが、私の最初のヒントはこれです:可能であれば、ASP.Net MVCを使用します。

右、それを私の胸から取り出して、続けましょう。

Entity Frameworkを使用しているときに、エンティティストアを使用してデータベースに直接アクセスする2番目の方法は、アーキテクチャを疑うのは当然です。考慮すべき単純なルールは次のとおりです:データベースとの間の唯一の方法

WebFormsのような疲れたテクノロジーに悩まされている場合でも、MVCアーキテクチャを使用することで、MVCブックから有用なページを取り出すことができます。エンジンがモデルレイヤーのほとんどの作業を行っているように聞こえます。実際、もう少し進んでViewModelタイプのアプローチを使用したいように思えます。ViewModelはモデルのように動作しますが、フロントエンドからの作業を容易にする追加の機能があるため、事実上、 DTOですが、フロントエンドやその他のビュー関連のロジックで書きたくないプロパティがある場合は、ViewModelクラスに入ります。

次に、エンティティレイヤーがViewModelオブジェクトを取得できることを確認します(または、エンティティのインターフェイスを作成するので、BookRecordオブジェクトがある場合は、IBookRecordインターフェイスを作成して、BookRecordエンティティが実装し、BookRecordViewModelも実装します。可能であれば、常にインターフェースを渡す方が優れており、DI、モッキング、および一般的に回復力を構築するのに役立つすべてのものを本当に容易にします。

これによる唯一の真の違いは、エンジンの更新を管理するためのロジックを処理する必要があるということです。インターフェイスからBookRecordViewModelをプッシュダウンするとき、Idフィールドが設定されているかどうかと、それが設定されているかどうかを確認する必要があります。新しいレコードとして追加しない場合は、クエリと更新を行います。すべてをすでにEngineプロジェクトに実装している場合もありますが、その場合はまったく問題ありません。

1
glenatron