web-dev-qa-db-ja.com

リポジトリパターン:データコンテキストを基になるレイヤーに公開する

私のチームはレガシープロジェクトを更新中です。データアクセスレイヤーに_Entity Framework_とともにリポジトリパターンを組み込むことにしました。以下は、この組織の概要です。

enter image description here

_IRepository<TEntity>_は、エンティティセットで一般的な操作を実行するために使用される汎用インターフェイスです。

_public interface IRepository<TEntity> where TEntity : class
{
    ObjectSet<TEntity> EntitySet { get; set; }

    TEntity Get(object key);

    void Insert(TEntity entity);

    void Update(TEntity entity);

    void Save();
}
_

たとえば、Employeeエンティティがある場合、_EmployeeRepository:IRepository<Employee>_オブジェクトを作成できます。次に、DataFactoryクラスをラッパーオブジェクトとして使用して、ObjectContextの生成、破棄、および例外処理を行います。

_public class DataFactory<TContext, TEntity> : IDisposable
        where TContext : ObjectContext, new()
        where TEntity : class
{
    private bool _disposed = false;
    private TContext _context;
    private IRepository<TEntity> _repository;

    public DataFactory()
    {
        _context = new TContext();
        IRepository<TEntity> repoistory;

        bool isRepositoryFound = TryGetRepository(out repoistory);

        if (!isRepositoryFound)
        {
            throw new InvalidOperationException(
                string.Format("Unable to find repository of type {0}.", typeof(TEntity).FullName));
        }

        _repository = repoistory;
    }

    public DataFactory(TContext context, IRepository<TEntity> repository)
    {
        _context = context;
        _repository = repository;
    }

    public void Do(Action<TContext, IRepository<TEntity>> action)
    {
        try
        {
            action(_context, _repository);
        }
        catch (Exception ex)
        {
            ProcessException(ex);
        }
    }

    public TResult DoAndReturn<TResult>(Func<TContext, IRepository<TEntity>, TResult> action)
    {
        try
        {
            return action(_context, _repository);
        }
        catch (Exception ex)
        {
            ProcessException(ex);
            return default(TResult);
        }
    }

    private bool TryGetRepository(out IRepository<TEntity> repository)
    {
        Type repositoryType = Assembly.GetExecutingAssembly().GetTypes().SingleOrDefault(t =>
            typeof(IRepository<TEntity>).IsAssignableFrom(t));

        if (repositoryType == null)
        {
            repository = null;
            return false;
        }

        repository = (IRepository<TEntity>)Activator.CreateInstance(repositoryType, _context );
        return true;
    }
}
_

最後に、このクラスをビジネスレイヤーで使用するには、次のようなものが必要です。

_using (var factory = new DataFactory<MyDataContext, Employee>())
{
    factory.Do((context, repository) =>
    {
       // Interact with the repository
    });
}
_

私の同僚と私は、データコンテキストオブジェクトをDo()およびDoAndReturn()メソッドの基になるレイヤーに公開する必要があるかどうかについて長い会話をしました。コンテキストに直接アクセスできると便利な場合もありますが(たとえば、特定のエンティティセットの遅延読み込みをオン/オフにする必要がある場合)、そうすることで、データアクセスレイヤー(これは、共通のIRepositoryコントラクトを提供することによって実現されます)。これにより、ObjectContextを介してオブジェクトに直接アクセス/操作できるようになりました。

彼はこれらのメソッドの2つの異なるバージョンを持つことを提案しました:リポジトリのみを公開するものと、コンテキストとリポジトリの両方を公開するものです。これは許容できるアプローチですか?任意の提案をいただければ幸いです。

6
PoweredByOrange

あなたのセットアップは1つの質問を叫びます:なぜあなたは抽象化を導入したのですか?あなたがそれに答えることができない(そして私があなたが投稿した質問の性質を考えると、あなたが答えることができると確信していないと私は感じている)場合、それらを取り除きます。エンティティフレームワークと他のORMは、データベースの相互作用、作業単位などのために独自の抽象化を形成します。同じものを実際に抽象化する別の抽象化クラスのセットにそれをラップすると、さらに複雑になります。私が推測するかもしれませんが、この抽象化を作成すると、DB指向のクラスを公開せずにDB作業を注文できるため、BLレベルでコンテキストを操作しなくても、リポジトリを通じて作業を保存できると思います。しかし、一般にそれは単なる誤りです。ラッパーを通じてDB指向のコードを呼び出すことは、実際には何も抽象化していません。

何が役立ち、何をしなかったかは、ビジネスロジックでORMを使用するためのクラスを作成することです。この典型的な例は、異なるビジネスロジックメソッドが同じデータベーストランザクション以上に参加することを確実にするロジックです。ビジネスロジックによって生成された作業は、同じ作業単位に収集されます。

それを実現するために、ビジネスロジックアクションをクラスにモデル化することから始めます。一連のコマンドが生成されます。 BLはDB指向のコードにアクセスできません。何をすべきかを定式化するだけです。次に、アクション/コマンドオブジェクトは、DB指向のロジックを使用し、実際にコマンド/アクションを実行するクラスに送信されます。

リポジトリパターンは「良い」ですが、IMHOはエンティティ依存関係自体の隣に依存関係の個別の階層を作成するため、非常に迅速にトラブルに巻き込まれます(例:Customer.Orders。リポジトリでは、そのOrdersにアクセスできません。それらはOrdersリポジトリの一部であるため、コレクション)であり、それらがreallyと格闘していることに対する実際の回答ではありません。

EFに問題の簡単な解決策となる個別の作業単位のクラスがあればすばらしいと思いますが、残念ながら、MSもAmblerのORMの設計に従い、中央のセッション/コンテキスト/ユニットで終了しました仕事のハイブリッド。

4
Frans Bouma