UnitOfWork模式.md 4.9 KB

Unit Of Work

这个模式关注的是对象的变化。正如 Martin Fowler 所说,这个单元在事务上下文中保留了一个变化的对象列表。同时管理写操作,处理并发问题。我们可以把它看作是一个上下文、会话或对象,它在事务过程中跟踪所有数据模型上的变化。

架构概述

UOW

所以我们有接口、实现和两个服务与 UnitOfWork 的依赖。

给我看一些代码

下面的代码块显示了一个简单的接口来实现这个模式的主要目的:

public interface IUnitOfWork
{
    void BeginTransaction();
    void SaveChanges();
    bool Commit();
    void Rollback();
}

如你所想,这些名字的灵感来自于任何数据库的操作。 BeginTransaction 应该启动事务。如果你使用的是 SQL 数据库,你可以在这里定义隔离级别,例如。 此刻我们可以开始创建和更新一些对象,然后调用 SaveChanges 方法。这个方法的目标是在事务中保持这些变化在内存中的可用性。当我们在打开的事务中完成了所有的更改,如果一切都符合数据存储规则,我们可以 Commit,否则我们调用 Rollback 来丢弃所有这些更改,以防出现任何错误。

一个简单的使用实例

public void Update(Person person)
{
    UnitOfWork.BeginTransaction();
    try
    {
        PersonService.Update(person);
        PersonHistoryService.Create(person.Id, "Person updated!");
    }
    catch (Exception e)
    {
        UnitOfWork.Rollback();
        _logger.LogError(e.Message);
    }
    UnitOfWork.Commit();
}

步骤:

  1. 开启事务
  2. 更新 "人员",并在历史表中创建一个新条目。
  3. 每个服务都会调用 SaveChanges
  4. 如果捕捉到异常,使用回滚丢弃更改,并记录一条消息。
  5. 如果一切正常,提交事务并将这些更改写入存储系统中

这是一个基本案例,展示了如何使用 UnitOfWork 服务。重要的是要明白,当我们调用 SaveChanges 时,我们只是在事务范围内将这些对象保留在内存中。这些变化只是在我们提交事务时才被持久化在数据存储中。

这是一个我们做了多个更改的情况,我们必须保证所有的更改在同一时间被存储。然而,我们可能会有这样的情况:我们没有打开事务,我们只需要在最后调用 SaveChanges。

一个更简单的使用例子

在接下来的代码块中,我们有来自服务的方法来创建一个历史记录。在存储库调用后,我们只需要保存这些变化。

public void Create(int personId, string message)
{
    PersonHistory history = new PersonHistory(personId, message);
    NoteRepository.Insert(history);
    UnitOfWork.SaveChanges();
}

我们可以看到,这个方法调用了 SaveChanges,如果我们想在没有事务的情况下创建历史记录,那就足以存储数据。

仓储模式重要性

考虑到前面的例子,我们知道我们必须在类的构造函数中配置所有使用的仓储(PersonRepository 和 NoteRepository)的依赖注入。如果工作单元知道如何给我们一个特定的存储库的实例,以保持代码的简单性,那就太好了。

UnitOfWork.Repository<Note>().Insert(note);

正如我们所看到的,它很容易使用,而且在我们的构造函数中不需要那么多的依赖。我们只需要有 IUnitOfWork 依赖。

为了得到这个行为,我们应该改变接口:

public interface IUnitOfWork
{
    void BeginTransaction();
    void SaveChanges();
    bool Commit();
    void Rollback();

    IRepository<TEntity> Repository<TEntity>();
}

正如我们所看到的,实现 仓储模式 是非常重要的。这个新方法只适用于我们定义了这种模式的情况。

工作单元保存一个字典,其中键是 TEntity 类型的名称,值是一个动态对象,它将是 TEntity 的存储库的实例。如果字典中已经有该类型的值,它将返回它。如果没有,它将创建一个新的实例并存储它。我们可以在下面的代码块中看到这个算法。

private Dictionary<string, dynamic> _repositories;
public IRepository<TEntity> Repository<TEntity>()
{
    if (_repositories == null)
        _repositories = new Dictionary<string, dynamic>();
    var type = typeof(TEntity).Name;
    if (_repositories.ContainsKey(type))
        return (IRepository<TEntity>)_repositories[type];
    var repositoryType = typeof(Repository<>);
    _repositories.Add(type, Activator.CreateInstance(
        repositoryType.MakeGenericType(typeof(TEntity)), this)
    );
    return _repositories[type];
}

结论

这是对 Unit Of Work 模式的概述以及一些使用和架构设计的例子。我假设我们使用一个存储系统来持久化数据。它可以是任何类型的存储,你只需要改变如何实现 IUnitOfWork 接口的方式。