24

Entity Frameworkデータベースファーストのアプローチを使用して、MVCアプリケーションのDbContext/POCOモデルを生成します。必要に応じて(単体テストの目的で)別の永続性プロバイダーに切り替えることができるように、コントローラーでDbContextに依存することを避けたいと思います。

これを行うには、CastleWindsorIoCコンテナを使用します。DbContextをIUnitOfWorkサービスとして登録し、汎用IRepositoryサービスを登録する予定です。このサービスの実装を使用して、モデル内の集約ルートにアクセスして操作します。

私はWindsorを初めて使用し、EFでの使用に関する多くの情報を見つけることができませんでした。また、いくつか質問があります。

  • EFをアプリケーションから切り離したい場合、これは合理的なアプローチですか?
  • IUnitOfWorkおよび汎用IRepositoryサービスをインストール/登録するにはどうすればよいですか?
4

1 に答える 1

25

だから、いくつかの結論。EF、Windsor、MVCを一緒に使用/ユニットテストしようとしている他の人に役立つ場合に備えて、これを書き留めると思いました。

まず、DbContextはリポジトリと作業単位の両方のパターンを実装しているため、これらの実装が機能するかどうか、または独自の実装を作成する必要があるかどうかを確認する必要があります。

DDDパターンに従って、独自のリポジトリを作成することを選択しました。集約ルートごとに1つです。理由:クエリコードをカプセル化するため、アプリケーションレイヤーにリークするのを防ぐため、およびアプリケーションコントローラーをテストするときにモックを作成しやすくするため。に基づいて汎用リポジトリを作成しましたIRepository<TEntity>。そこにはたくさんの例があります。私はこれが良いものだと思いました:http://architects.dzone.com/articles/implementing-repository

一方、私はIUnitOfWorkサービスを削除し、代わりにデフォルトの実装を選択することにしました。ただし、リポジトリサービスをテストするときにDbContextをモックできるように、IDbContext抽象化を作成しました(Microsoftがこれを自分で行わなかった理由はわかりません)。

IDbContextには、リポジトリで使用したいDbContextのメンバーのみを指定しました。それで:

public interface IDbContext: IDisposable
{
    Database Database { get; }
    DbEntityEntry Entry(object entity);
    IDbSet<TEntity> Set<TEntity>() where TEntity : class;
    int SaveChanges();
}

次に、IDbContextおよびIRepositoryサービス用のWindsorファシリティとインストーラを作成しました。

public class EntityFrameworkFacility: AbstractFacility
{
    protected override void Init()
    {
        Kernel.Register(Component.For<IDbContext>()
                                 .ImplementedBy<MyEntities>()
                                 .LifestylePerWebRequest(),
                        Component.For(typeof(IRepository<>))
                                 .ImplementedBy(typeof(Repository<>))
                                 .LifestylePerWebRequest());
    }
}

public class PersistenceInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.AddFacility<EntityFrameworkFacility>();
    }
}

最後の部分は、Entity Frameworkコンテキストクラスを拡張してIDbContextを実装し、Set()メソッドをシャドウイングしてDbSetではなくIDbSetを返すことでした。

public partial class MyEntities : IDbContext
{
    public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
    {
        return base.Set<TEntity>();
    }
}

これらすべてが整ったら(およびWindsorドキュメントに示されているControllerFactory登録)、必要に応じて、WindsorにIRepositoryオブジェクト(またはIDbContext)をコントローラーコンストラクターに挿入させるのは簡単です。

public ControllerBase(IRepository<Contact> repo)
{
    _repo = repo;
}

リポジトリ単体テストでは、実際のリポジトリインスタンスをモックIDbContextでバックアップできます。

mocks = new MockRepository();
context = mocks.StrictMock<IDbContext>();
repo = new Repository<Contact>(context);

コントローラの単体テストでは、モックリポジトリを使用できます。

mocks = new MockRepository();
repo = mocks.StrictMock<IRepository<Contact>>();
ContactController controller = new ContactController(repo);
于 2013-03-28T20:06:35.840 に答える