web-dev-qa-db-ja.com

リポジトリーおよび作業単位パターン-変更を保存する方法

このような質問が何度も寄せられるにもかかわらず、リポジトリと作業ユニットのパターンの関係を理解するのに苦労しています。基本的に、どの部分がデータの変更を保存/コミットするか、まだわかりません-リポジトリまたは作業単位?

私が見たすべての例は、データベース/ ORマッパーと組み合わせてこれらを使用することに関連しているので、より興味深い例を作成しましょう。データファイルのデータをファイルシステムに永続化しましょう。データがどこに行くかは関係がないので、私はこれを行うことができるはずのパターンに従って。

基本的なエンティティの場合:

_public class Account
{
    public int Id { get; set; }
    public string Name { get; set; }
}
_

次のインターフェースが使用されると思います:

_public interface IAccountRepository
{
     Account Get(int id);
     void Add(Account account);
     void Update(Account account);
     void Remove(Account account);
}

public interface IUnitOfWork
{
    void Save();
}
_

そして、私は使用の観点からは次のようになると思います:

_IUnitOfWork unitOfWork = // Create concrete implementation here
IAccountRepository repository = // Create concrete implementation here

// Add a new account
Account account = new Account() { Name = "Test" };
repository.Add(account);

// Commit changes
unitOfWork.Save();
_

すべてのデータがファイルに永続化されることを念頭に置いて、ロジックは実際にこのデータを実際に追加/更新/削除しますか?

  1. Add()Update()およびRemove()メソッドを介してリポジトリに格納されますか?ファイルを読み書きするすべてのコードを1か所に置くことは私には論理的に聞こえますが、それではIUnitOfWorkインターフェイスのポイントは何ですか?
  2. これはIUnitOfWork実装に含まれていますか?このシナリオでは、データ変更の追跡も行いますか?私にとってこれは、作業ユニットがファイルを書き込む必要があるときにリポジトリがファイルを読み取ることができるが、ロジックは2つの場所に分割されていることを示唆しています。
31
Peter Monks

リポジトリは作業単位なしでも機能するため、Saveメソッドを使用することもできます。

public interface IRepository<T>
{
     T Get(int id);
     void Add(T entity);
     void Update(T entity);
     void Remove(T entity);
     void Save();
}

作業単位は、複数のリポジトリがある場合に使用されます(データコンテキストが異なる場合があります)。 Commitメソッドを呼び出してすべての変更をデータベース(この場合はファイル)に永続化するまで、トランザクション内のすべての変更を追跡します。

したがって、リポジトリでAdd/Update/Removeを呼び出すと、エンティティのステータスのみが変更され、追加、削除、またはダーティとしてマークされます... Commitを呼び出すと、作業単位はリポジトリをループして、実際の永続化を実行します。

  • リポジトリが同じデータコンテキストを共有する場合、作業ユニットはデータコンテキストを直接操作して、パフォーマンスを向上させることができます(この場合、ファイルを開いて書き込む)。

  • リポジトリに異なるデータコンテキスト(異なるデータベースまたはファイル)がある場合、作業ユニットは、同じトランザクションスコープで各リポジトリのSaveメソッドを呼び出します。

25
phnkha

私は実際にこれにかなり新しいですが、誰も賢明に投稿していないので:

CRUDが予想どおりにリポジトリで発生するコードですが、Account.Add(たとえば)が呼び出されると、後で追加されるもののリストにAccountオブジェクトが追加されます(変更が追跡されます)。 。

UnitOfWork.Save()が呼び出されると、リポジトリは変更されたもののリストまたは変更されたもののUoWのリスト(パターンの実装方法に応じて異なります)を調べて適切に動作することを許可されます-この場合、 List<Account> NewItemsToAddへの呼び出しに基づいて、何を追加するかを追跡しているフィールド。 UoWが保存してもよいと言ったとき、リポジトリは実際に新しいアイテムをファイルとして永続化でき、成功した場合は追加する新しいアイテムのリストをクリアします。

私の知る限り、UoWのポイントは、複数のリポジトリ(結合したものがコミットしたい作業の論理単位です)全体で保存を管理することです。

私はあなたの質問が本当に好きです。私はエンティティフレームワークでUow /リポジトリパターンを使用しましたが、EFが実際に実行する量を示しています(SaveChangesが最終的に呼び出されるまで、コンテキストが変更を追跡する方法)。この設計パターンをサンプルに実装するには、変更を管理するためにかなりの量のコードを記述する必要があります。

6
Neil Thompson

ファイルシステムを使用すると、自分でそれを実行したい場合、かなり複雑になる可能性があります。

oWがコミットされたときにのみ書き込みます。

あなたがしなければならないことは、リポジトリにUnitOfWorkのすべてのIO操作をエンキューさせることです。

public class UserFileRepository : IUserRepository
{
    public UserFileRepository(IUnitOfWork unitOfWork)
    {
        _enquableUow = unitOfWork as IEnquableUnitOfWork;
        if (_enquableUow == null) throw new NotSupportedException("This repository only works with IEnquableUnitOfWork implementations.");

    }

    public void Add(User user)
    {
        _uow.Append(() => AppendToFile(user));
    }

    public void Uppate(User user)
    {
        _uow.Append(() => ReplaceInFile(user));
    }
}

そうすることで、すべての変更を同時にファイルに書き込むことができます。

DBリポジトリでこれを行う必要がないのは、トランザクションサポートがDBに組み込まれているためです。したがって、DBにトランザクションを直接開始し、それを使用して作業単位を偽造するように指示できます。

トランザクションサポート

ファイルの変更をロールバックし、同時トランザクション中に異なるスレッド/トランザクションが同じファイルにアクセスするのを防ぐ必要があるため、複雑になります。

4
jgauffin

ええ、物事はトリッキーです。このシナリオを想像してみてください。1つのリポジトリがデータベースに何かを保存し、他はファイルシステムに保存し、3番目はクラウドに保存します。どうやってそれをコミットしますか?

ガイドラインとして、UoWはコミットする必要がありますが、上記のシナリオでは、更新する3つの非常に異なるものがあるため、コミットは単なる幻想です。最終的な整合性を入力します。つまり、最終的にすべてのものが整合します(RDBMSで使用されているのと同じ瞬間ではありません)。

そのUoWは、メッセージ駆動型アーキテクチャではSagaと呼ばれます。要点は、すべてのサガビットを異なる時間に実行できることです。 Sagaは、3つのリポジトリすべてが更新されたときにのみ完了します。

ほとんどの場合RDBMSで作業するため、このアプローチはあまり見られませんが、現在、NoSqlは非常に一般的であるため、古典的なトランザクションアプローチは非常に制限されています。

したがって、1つのRDBMSのみで作業することが確実な場合は、UoWでトランザクションを使用し、関連する接続を各リポジトリに渡します。最後に、UoWはcommitを呼び出します。

トランザクションをサポートしていない複数のRDBMSまたはストレージを使用する必要がある場合は、メッセージ駆動型アーキテクチャと佐賀の概念をよく理解してください。

3
MikeSW

通常、リポジトリはすべての読み取りを処理し、作業単位はすべての書き込みを処理しますが、確実にこれらの2つのうちの1つを使用するだけですべての読み取りと書き込みを処理できます(ただし、リポジトリパターンのみを使用する場合、維持するのは非常に退屈です。 10個のリポジトリでは、さらに悪いことに、一貫性のない読み取りと書き込みが上書きされる可能性があります)。両方を使用したミックスの利点は、ステータス変更の追跡が容易で、同時実行性と一貫性のある問題の処理が容易です。理解を深めるために、リンクを参照できます: Entity Framework 4.1のリポジトリパターンと親/子の関係 および https://softwareengineering.stackexchange.com/questions/263502/unit-of- work-concurrency-how-is-it-handled

1
LIU YUE