多个存储库的非平凡CRUD操作的工作单元

时间:2009-11-30 20:20:36

标签: repository unit-of-work

我已经看到工作单元模式的实现类似于以下代码:

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

然后有一些方法可以为每个HashSets添加实体。

On 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实体,但我现在需要这些自定义操作。

所以似乎我总是堆叠Repository操作,然后用UnitOfWork.Commit();

执行所有操作

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

arep.DoSomeNonSimpleUpdateHere();
brep.DoSomeNonSimpleDeleteHere(); 
在UoW实例中

并在uow.Commit()上执行它们,但我还要存储所有方法参数。这听起来很复杂。

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

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

3 个答案:

答案 0 :(得分:3)

似乎即使是复杂的更新也可以分解为对一个或多个DomainObject的一系列修改。调用DoSomeNonSimpleUpdateHere()可能会修改几个不同的DomainObject,这会触发对每个对象的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必须执行多个delete语句,而不是只为所有非活动用户执行一个语句。

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

我终于找到了这个:

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

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