2

次のコードのようなもので実装された作業単位パターンを見てきました。

    private HashSet<object> _newEntities = new HashSet<object>();
    private HashSet<object> _updatedEntities = new HashSet<object>();
    private HashSet<object> _deletedEntities = new HashSet<object>();

そして、これらの各 HashSet にエンティティを追加するメソッドがあります。

コミット時に、UnitOfWork はエンティティごとにいくつかの Mapper インスタンスを作成し、架空の Mapper から Insert、Update、Delete メソッドを呼び出します。

このアプローチの問題点は、Insert、Update、Delete メソッドの名前がハードコードされているため、そのような UnitOfWork は単純な CRUD 操作しかできないように見えることです。しかし、次の使用法が必要な場合はどうでしょうか。

UnitOfWork ouw = new UnitOfWork();
uow.Start();

ARepository arep = new ARepository();
BRepository brep = new BRepository(); 

arep.DoSomeNonSimpleUpdateHere();
brep.DoSomeNonSimpleDeleteHere();

uow.Commit();

3 つの HashSet アプローチは失敗します。これは、挿入、更新、削除操作に対してのみ A エンティティと B エンティティを登録できたからですが、これらのカスタム操作が必要になりました。

したがって、常にリポジトリ操作をスタックしてから、すべてを実行できるとは限らないようですUnitOfWork.Commit();

この問題を解決するには?最初のアイデアは、メソッドのアドレスを保存できることです

arep.DoSomeNonSimpleUpdateHere();
brep.DoSomeNonSimpleDeleteHere(); 

UoWインスタンスでそれらを実行しますuow.Commit()が、すべてのメソッドパラメーターも保存する必要があります. それは複雑に聞こえます。

もう 1 つのアイデアは、リポジトリを完全に UoW 対応DoSomeNonSimpleUpdateHereにすることです。実行中の UoW があることを検出できるので、実行せずDoSomeNonSimpleUpdateHereに操作パラメータと「保留中」ステータスをリポジトリ インスタンスのスタックに保存します (明らかに保存できません)。 UoW は具体的なリポジトリの実装に依存するべきではないため、UoW のすべて)。そして、関連するリポジトリを UoW インスタンスに登録します。UoW が を呼び出すとCommit、トランザクションが開かれ、保留中のリポジトリごとに Flush() のようなものが呼び出されます。現在、Repository のすべてのメソッドには、UoW の検出と後で操作を延期するためのいくつかの要素が必要Commit()です。

簡単な質問は、保留中のすべての変更を UoW の複数のリポジトリに登録し、Commit()それらすべてを 1 つのトランザクションで登録する最も簡単な方法は何ですか?

4

3 に答える 3

3

複雑な更新であっても、1 つまたは複数の DomainObject に対する一連の変更に分解できるようです。DoSomeNonSimpleUpdateHere() を呼び出すと、いくつかの異なる DomainObjects が変更される場合があり、これにより、オブジェクトごとに UnitOfWork.registerDirty(DomainObject) への対応する呼び出しがトリガーされます。以下のサンプル コードでは、DoSomeNonSimpleUpdateHere への呼び出しを、非アクティブなユーザーをシステムから削除するコードに置き換えました。

UnitOfWork uow = GetSession().GetUnitOfWork();
uow.Start();

UserRepository repository = new UserRespository();
UserList users = repository.GetAllUsers();

foreach (User user in users)
{
  if (!user.IsActive())
    users.Remove( user );
}

uow.Commit();

すべてのユーザーを反復処理する必要があることを懸念している場合は、Criteria オブジェクトを使用してデータベースからプルされるユーザーの数を制限する代替アプローチを次に示します。

UnitOfWork uow = GetSession().GetUnitOfWork();
uow.Start();

Repository repository = new UserRespository();
Criteria inactiveUsersCriteria = new Criteria();
inactiveUsersCriteria.equal( User.ACTIVATED, 0 );
UserList inactiveUsers = repository.GetMatching( inactiveUsersCriteria );
inactiveUsers.RemoveAll();

uow.Commit();

UserList.Remove および UserList.RemoveAll メソッドは、削除された各ユーザーの UnitOfWork に通知します。UnitOfWork.Commit() が呼び出されると、_deletedEntities で見つかった各ユーザーが削除されます。このアプローチにより、特殊なケースごとに SQL クエリを記述する必要なく、任意の複雑なコードを作成できます。UnitOfWork は、すべての非アクティブなユーザーに対して 1 つのステートメントだけではなく、複数の削除ステートメントを実行する必要があるため、ここではバッチ更新を使用すると便利です。

于 2009-11-30T23:43:36.483 に答える
1

この問題があるという事実は、リポジトリ パターンをそのまま使用しているのではなく、複数のテーブル データ ゲートウェイのようなものを使用していることを示唆しています。通常、リポジトリは集約ルートをロードおよび保存するためのものです。そのため、エンティティを保存すると、永続化レイヤーはその集約ルート エンティティ インスタンスのオブジェクト グラフのすべての変更を保存します。

コード内に 1 つのテーブル (またはエンティティ) ごとに約 1 つの "リポジトリ" がある場合、実際にはテーブル データ ゲートウェイまたはデータ転送オブジェクトを使用している可能性があります。その場合、おそらく、各 Save() メソッドでアクティブなトランザクション (または作業単位) への参照を渡す手段が必要です。

Evans の DDD の本で、彼はトランザクション制御をリポジトリのクライアントに任せることを推奨しています。実際にテーブル データ ゲートウェイ パターンを使用している場合、回避するのは難しいかもしれませんが、それは良い習慣ではないことに同意します。

于 2009-11-30T20:34:55.007 に答える
0

私は最終的にこれを見つけました:

http://www.goeleven.com/Blog/82

作成者は、更新/挿入/削除用の 3 つのリストを使用して問題を解決しますが、そこにエンティティを保存しません。代わりに、リポジトリ デリゲートとそのパラメーターが格納されます。そのため、作成者は登録されている各デリゲートを呼び出します。このアプローチでは、いくつかの複雑なリポジトリ メソッドでも登録できるため、個別の TableDataGateway の使用を避けることができます。

于 2009-12-01T15:58:34.350 に答える