2

我已经看到使用以下代码实现的工作单元模式:

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

然后有一些方法可以将实体添加到这些 HashSet 中的每一个。

在 Commit 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();

现在三 HashSet 方法失败了,因为我只能为插入、更新、删除操作注册 A 和 B 实体,但我现在需要这些自定义操作。

所以似乎我不能总是堆叠存储库操作,然后用UnitOfWork.Commit();

如何解决这个问题呢?第一个想法是 - 我可以存储方法的地址

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

在 UoW 实例中并执行它们,uow.Commit()但我还必须存储所有方法参数。这听起来很复杂。

另一个想法是让存储库完全感知 UoW:在DoSomeNonSimpleUpdateHere我可以检测到有一个 UoW 正在运行,所以我不执行DoSomeNonSimpleUpdateHere但将操作参数和“待定”状态保存在存储库实例的某个堆栈中(显然我无法保存UoW 中的所有内容,因为 UoW 不应依赖于具体的存储库实现)。然后我在 UoW 实例中注册相关的存储库。当 UoW 调用Commit时,它会打开一个事务,并为每个待处理的存储库调用诸如 Flush() 之类的东西。现在 Repository 的每个方法都需要一些东西来进行 UoW 检测和操作延迟以供以后使用Commit()

所以简短的问题是 - 在 UoW 的多个存储库中注册所有待处理的更改然后Commit()在单个事务中注册它们的最简单方法是什么?

4

3 回答 3

3

看起来即使是复杂的更新也可以分解为对一个或多个 DomainObjects 的一系列修改。调用 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 必须为所有非活动用户执行多个删除语句,而不是只执行一个语句。

于 2009-11-30T23:43:36.483 回答
1

您遇到此问题的事实表明您没有使用存储库模式,而是更像是多表数据网关。通常,存储库用于加载和保存聚合根。因此,当您保存实体时,您的持久层将所有更改保存在该聚合根实体实例的对象图中。

如果在您的代码中,每个表(或实体)大约有一个“存储库”,那么您实际上可能正在使用表数据网关或数据传输对象。在这种情况下,您可能需要在每个 Save() 方法中传递对活动事务(或工作单元)的引用。

在 Evans DDD 书中,他建议将事务控制权留给存储库的客户端,我同意这不是一个好的做法,尽管如果您实际使用表数据网关模式可能更难避免。

于 2009-11-30T20:34:55.007 回答
0

我终于找到了这个:

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

作者使用三个 List 进行更新/插入/删除解决了这个问题,但他并没有在其中存储实体。而是存储存储库委托及其参数。所以在提交时,作者调用了每个注册的代表。使用这种方法,我甚至可以注册一些复杂的存储库方法,从而避免使用单独的 TableDataGateway。

于 2009-12-01T15:58:34.350 回答