7

私はまだこれに頭を悩ませています。次のようにレイヤー(dll)を分離したい:

1) MyProject.Web.dll - MVC Web アプリ (コントローラー、モデル (編集/表示)、ビュー)
2) MyProject.Services.dll - サービス層 (ビジネス ロジック)
3) MyProject.Repositories.dll - リポジトリ
4) MyProject。 Domain.dll - POCO クラス
5) MyProject.Data.dll - EF4

ワークフロー:

1) コントローラーはサービスを呼び出して、オブジェクトを取得し、モデルの表示/編集を行います。
2) サービスはリポジトリを呼び出して、オブジェクトを取得/保持します。
3) リポジトリは EF を呼び出して、SQL Server との間でオブジェクトを取得/永続化します。

私のリポジトリは IQueryable(Of T) を返し、その中で ObjectSet(Of T) を利用します。

これを見ると、レイヤーは正確に次のレイヤーとPOCOクラスを含むライブラリに依存していますか?

いくつかの懸念事項:

1) リポジトリが EF で正しく動作するためには、それらは System.Data.Objects に依存します。これで、リポジトリ レイヤーで EF と密結合になりました。それは悪いことですか?

2) UnitOfWork パターンを使用しています。それはどこに住むべきですか?これには ObjectContext としての Property Context があるため、EF にも密接に結合されています。悪い?

3) これを簡単にするために DI を使用するにはどうすればよいですか?

テストのために、これを可能な限り疎結合にしたいと考えています。助言がありますか?

- - - - - 編集 - - - - -

ここで正しい軌道に乗っているかどうか教えてください。また、サービスには IRepository(Of Category) の権利が注入されますが、それと EFRepository(Of T) の具象クラスとの違いをどのように認識しますか? UnitOfWork と Service と同じですか?

誰かが私が理解できるところまでこれを理解するのを手伝ってくれたら、それは些細なことのように思えるでしょうが、私はこれに頭を悩ませています!!

コントローラ

Public Class CategoryController
    Private _Service As Domain.Interfaces.IService

    Public Sub New(ByVal Service As Domain.Interfaces.IService)
        _Service = Service

    End Sub

    Function ListCategories() As ActionResult
        Dim Model As New CategoryViewModel

        Using UOW As New Repositories.EFUnitOfWork
            Mapper.Map(Of Category, CategoryViewModel)(_Service.GetCategories)
        End Using

        Return View(Model)
    End Function

End Class

サービス

Public Class CategoryService

    Private Repository As Domain.Interfaces.IRepository(Of Domain.Category)
    Private UnitOfWork As Domain.Interfaces.IUnitOfWork

    Public Sub New(ByVal UnitOfWork As Domain.Interfaces.IUnitOfWork, ByVal Repository As Domain.Interfaces.IRepository(Of Domain.Category))
        UnitOfWork = UnitOfWork
        Repository = Repository

    End Sub

    Public Function GetCategories() As IEnumerable(Of Domain.Category)
        Return Repository.GetAll()
    End Function

End Class

リポジトリと UnitOfWork

Public MustInherit Class RepositoryBase(Of T As Class)
    Implements Domain.Interfaces.IRepository(Of T)

End Class

Public Class EFRepository(Of T As Class)
    Inherits RepositoryBase(Of T)

End Class

Public Class EFUnitOfWork
    Implements Domain.Interfaces.IUnitOfWork

    Public Property Context As ObjectContext

    Public Sub Commit() Implements Domain.Interfaces.IUnitOfWork.Commit

    End Sub

End Class
4

4 に答える 4

7

元の回答

  1. いいえ。ただし、サービスをこれに結合することを避けるためISomethingRepositoryに、ドメイン層にインターフェースを用意してください。これは、IoC コンテナーによって解決されます。

  2. Unit of Work パターンは、リポジトリで実装する必要があります。サービスからリポジトリを分離することで提案したのと同じソリューションを使用して、これを分離します。IUnitOfWorkorをドメイン レイヤーに作成しIUnitOfWork<TContext>、実装をリポジトリ レイヤーに配置します。リポジトリがデータ層にデータを保持するだけである場合、リポジトリの実装をデータ層から分離する必要がある理由はわかりませんObjectContextリポジトリ インターフェイスはドメイン ロジックですが、実装はデータの問題です

  3. DI を使用して、サービスをコントローラーに注入し、リポジトリをサービスに注入できます。DI を使用すると、サービスはリポジトリ インターフェースに依存し、データ/リポジトリ アセンブリに結合されずにISomethingRepositoryの実装を受け取ります。EFSomethingRepository基本的に、あなたのIControllerFactory実装は、IoC コンテナーを取得して、コントローラーのすべてのコンストラクター依存関係を提供します。これには、IoC コンテナーがすべてのコントローラーのコンストラクターの依存関係 (サービス) とそのコンストラクターの依存関係 (リポジトリ) も提供する必要があります。すべてのアセンブリはドメイン層 (リポジトリとサービス インターフェイスを持つ) に依存しますが、実装ではなくインターフェイスに依存するため、相互に依存することはありません。依存関係の解決のために別のアセンブリが必要になるか、そのコードを Web プロジェクトに含める必要があります。(別のアセンブリをお勧めします)。Dependency Resolution アセンブリに依存する唯一のアセンブリは UI アセンブリになります。IHttpModuleイベントで依存関係を登録するための実装Application_Start(プロジェクトには bin フォルダー内の dll のコピーが必要ですが、プロジェクト参照は必要ありません)。適切なオープンソースの IoC コンテナはたくさんあります。最適なものは、何を選択するかによって大きく異なります。個人的にはStructureMapが好きです。it と Ninject はどちらも、信頼性が高く、十分に文書化された DI フレームワークです。

Sam Striano の編集への応答

VB でコーディングしてから何年も経っているので、構文が間違っている可能性があります。

Public Class CategoryController
  Private _Service As Domain.Interfaces.IService

  'This is good.
  Public Sub New(ByVal Service As Domain.Interfaces.IService)
      _Service = Service
  End Sub


  Function ListCategories() As ActionResult
      Dim Model As New CategoryViewModel


      Using UOW As New Repositories.EFUnitOfWork

これはコントローラーにある必要はありません。それをリポジトリに移動し、実際のトランザクションを囲みます。また、コントローラーがデータレイヤーに依存することは望ましくありません。

          Mapper.Map(Of Category, CategoryViewModel)(_Service.GetCategories)

これは AutoMapper への呼び出しですか? 元の質問とは関係ありませんが、マッピング機能を ActionFilter に再配置して、戻り値が Return View(_Service.GetCategories) になるようにする必要があります

      End Using

      Return View(Model)
  End Function

Service クラスは問題ありませんでした。

リポジトリと作業単位はほとんど不完全に見えます。リポジトリは ObjectContext を新しく作成し、それを Unit of Work に注入してから、Unit of Work のスコープ内ですべてのトランザクションを実行する必要があります (コントローラーで行ったことと同様)。コントローラーにそれを含めることの問題は、単一のサービス呼び出しが複数の作業単位にスコープされる可能性があることです。Unit of Work の実装方法については、こちらの記事を参照してください。 http://martinfowler.com/eaaCatalog/unitOfWork.html . Martin Fowler の書籍や Web サイトは、この種のトピックに関する優れた情報源です。

于 2011-03-03T19:11:00.367 に答える
1

順番にあなたの懸念に答えるために

1) 必ずしも悪いわけではありませんが、EF に固執する可能性によって異なります。これを減らすためにできることがいくつかあります。比較的低コストの 1 つ (3 にスキップしない場合は、制御の反転設定があると仮定) は、サービスからリポジトリのインターフェイスのみを参照することです。

2) 繰り返しますが、アプリケーションを EF に結合しないように多くの時間を費やすことができると思いますが、この方向の変更が他の変更にも影響しないかどうかを自問する必要があります。ここでも、インターフェイスを通じて間接レイヤーを導入し、後で別のリポジトリ実装と簡単に交換することができます。

3)コントロールの反転により、必要なすべてのテストが再び許可されるはずです。したがって、多くの直接参照をまったく必要とせず、レイヤーを分離してテストする必要はありません。

リクエストされたサンプルの更新。

public class QuestionService : IQuestionService
{

    private readonly IQuestionRepository _questionRepository;

    public QuestionService(IQuestionRepository questionRepository){
           _questionRepository = questionRepository
    }
}

したがって、サービスは、単体テスト内でモックまたは偽造できるインターフェースのみを認識します。それはすべてかなり標準的なIoCのものです。これについては参考文献がたくさんありますが、その多くが初めての場合は、完全なストーリーを提供する本をお勧めします。

于 2011-03-03T19:09:33.890 に答える
0

完全なコード例は、MEF とリポジトリ パターン (EFCodeFirst も使用) でここにあります。

于 2011-03-14T21:01:33.583 に答える
0

MEF を使用することをお勧めします。必要な依存性注入フレームワークを提供しますが、本格的ではありません。単体テストに最適です。関連する質問に対するいくつかの回答を次に示します

于 2011-03-03T19:08:20.100 に答える