实际应如何处理UnitOfWork模式

时间:2018-10-29 14:33:13

标签: design-patterns unit-of-work

UoW应该实际处理什么?

按照我的看法,UoW不应处理提交和回滚。它仅应提供这样做的手段,并且仅应负责跟踪将要提交的对象的更改,这样,如果它们处于某种不一致的状态或发生了某些更改,则事务应该失败吗?

所以我看到UoW的方式是这样的。

public interface IUnitOfWork : IDisposable
{
    IAtomicTransaction BeginTransaction();

    Task<object> SaveAsync<TEntity>(TEntity entity);

    Task UpdateAsync<TEntity>(TEntity entity);

    Task RemoveAsync<TEntity>(TEntity entity);
}

上班应该是这样的

interface IAtomicTransaction : IDisposable 
{
    void Commit();

    void Rolleback();
}

例如,这篇文章(不仅是这篇文章,而且还有很多类似的文章),

http://jasonwatmore.com/post/2015/01/28/unit-of-work-repository-pattern-in-mvc5-and-web-api-2-with-fluent-nhibernate-and-ninject

如果您看到的话,您会发现它在存储库中使用了ISession,我发现这是一个错误,因为它将直接将存储库(您的业务)链接到NHibernate的ISession。 UoW不应该承担这项责任吗?是否应该因为更改框架而开始更改业务实现? UoW是否不应该充当适配器,例如反腐败层?

2 个答案:

答案 0 :(得分:1)

UoW不仅仅是交易,more。以下是来自Martin Fowler的引用:

  

工作单元会跟踪您在业务交易过程中可能影响数据库的所有操作。完成后,它会根据您的工作找出更改数据库所需要做的一切。

它应该:

  1. 为您的存储库提供上下文
    要么允许从此UoW实例创建存储库,要么允许将该UoW实例注入存储库中。我更喜欢第一种方法。所有使用internal构造函数的存储库以及UoW中的某些类似于工厂的方法都会创建存储库。
  2. 在此情况下跟踪更改
    这是一个复杂的主题,但是为了简单起见,让我们将其限制为数据库事务。许多ORM以更好的方式处理此问题。跟踪Entity对象的状态可以通过多种方式完成。维护更改列表,维护实体的状态(是否脏)或维护实体的原始副本,并将其与最终副本进行比较,等等。
  3. 刷新/不刷新在这种情况下所做的更改
    这与以上几点有关。基于跟踪,确定需要将哪些更改刷新到存储。一些UoW实施还支持自动刷新,其中UoW自动确定何时刷新更改。如果一切正常,将清除更改。如果有问题,请不要刷新。同样,为简单起见,让我们将其限制为数据库事务。对于详细的实现,使用一些好的ORM是更好的解决方案。
  4. 创建和清理资源
    UoW实例应创建(自动或手动)执行这些动作所需的资源,并在不再需要时清理它们。

interface IUnitOfWork : IDisposable
{
    IDbConnection Connection { get; }
    IDbTransaction Transaction { get; }
    void Begin();
    void Commit();
    void Rollback();

    IRepository CreateRepository(....);
}

方法CreateRepository在此UoW下创建一个存储库实例。这样,您可以在多个存储库中共享相同的UoW。这样,一个数据库事务可以分布在多个存储库中。另一种选择是将UoW注入到存储库中,如图here所示。

此方法的问题在于,它不会强制出现问题。呼叫者取决于何时(或是否)开始交易。


我可以想象的其他迷你版本的UoW(强制使用UoW)如下所示:

public sealed class UoWSession
{
    public UoWSession()
    {
        //Open connection here
        //Begin transaction here
    }

    IRepository CreateRepository(....)
    {
        //Create and return the requested repository instance here
    }

    void Commit()
    {
        transaction.Commit();
    }

    void Dispose()
    {
        //If transaction is not commited, rollback it here.
        //Cleanup resources here.
    }
}

在不使用ORM的情况下,您必须公开某些内容,该内容告诉您一切都很好。以上实现使用Commit方法。
可能有一个简单的属性,让我们说IsAllWell(默认情况下为false,并调用代码对其进行显式设置)。然后,您的Dispose方法基于属性来提交或回滚事务。在这种情况下,您不需要公开Commit方法,因为您正在标记内部对其进行处理。

答案 1 :(得分:1)

  

现在您已经编辑了问题,所以我必须更改回答方式;因此,第二个答案。我的first answer仍然很有价值(希望;))。

UoW应该自动找出需要清除的更改(如果有)。

IAtomicTransaction BeginTransaction();
void Commit();
void Rolleback();

上述方法可能是UoW的一部分,也可能不是。 UoW的许多实现都公开了这些内容。暴露那些缺点是,事务处理成为调用者的责任;不是你的班级。还有一点是,呼叫者可以更好地控制该过程。

如果您想绕过缺点,请参阅我的第一个答案中的替代方法。

Task<object> SaveAsync<TEntity>(TEntity entity);
Task UpdateAsync<TEntity>(TEntity entity);
Task RemoveAsync<TEntity>(TEntity entity);

以上方法是存储库的一部分。这些不能成为UoW的一部分。 UoW应该基于更改跟踪自动找出要做什么。如果我们仅将讨论限制在数据库事务中,那么DbTransaction可以正确处理。有关处理变更跟踪的更多详细方法,请参阅我的第一个答案。

以下是基于NHibernate的实现。请注意,这是not recommended 。如果您使用的是完整的ORM,则应避免这种类型的实现,因为这对您的设计几乎没有价值。 如果仅将ISession替换为IDbConnection,则也可以使用ADO.NET来实现。

public interface IUnitOfWork : IDisposable
{
    void Flush();
}

public sealed class UnitOfWork : IUnitOfWork
{
    public UnitOfWork()
    {
        session = SessionFactroyHelper.CreateSession();
        transaction = session.BeginTransaction();
    }

    ISession session = null;
    ITransaction transaction = null;

    void IUoWSession.Flush()
    {
        transaction.Commit();
    }

    void IDisposable.Dispose()
    {
        transaction.Dispose();
        transaction = null;
        session.Dispose();
        session.Dispose();
    }
}

顺便说一句,这个主题本身就是基于意见的。如何实现UoW和存储库是个人设计的决定。如果您真的很想实现 correct (?)UoW,请考虑在代码中绕过UoW包装器直接使用一些高级ORM。