工作单元:精巧和存储库模式 - C#.Net

时间:2016-10-11 15:05:09

标签: c# .net dapper unit-of-work

使用Dapper, Repositories, and Unit of Work提出一个好的解决方案对我来说是一个挑战。我已经做了很多研究,并且已经看到Unit of Work类具有dictionary of repositories的实现。这似乎不是正确的方法。这就是我想要做的。

UnitOfWork.cs

public interface IUnitOfWork
{
    SqlConnection GetConnection();

    SqlTransaction GetTransaction();

    void CommitChanges();
}

public class UnitOfWork : IUnitOfWork
{
    private SqlConnection connection;
    private SqlTransaction transaction;

    public SqlConnection GetConnection()
    {
        if (connection != null)
        {
            return connection;
        }

        connection = new SqlConnection(@"Data Source=");
        connection.Open();
        transaction = connection.BeginTransaction();
        return connection;
    }

    public SqlTransaction GetTransaction()
    {
        return this.transaction;
    }

    public void CommitChanges()
    {
        try
        {
            transaction.Commit();
        }
        catch
        {
            transaction.Rollback();
        }
        finally
        {
            transaction.Dispose();
            connection.Close();
        }
    }
}

我知道这可能是一个糟糕的实施,但我只是想开始建立一个基础。

以下是服务实施。 UnitOfWork实例已注入服务。

public class VeterinarianService : IVeterinarianService
{
    private readonly IClock clock;
    private readonly IUnitOfWork work;
    private readonly IVeterinarianRepository vetRepository;
    private readonly ITestingRepository testingRepository;


    public VeterinarianService(IClock clock, IUnitOfWork work, IVeterinarianRepository vetRepository, ITestingRepository testingRepository)
    {
        this.clock = clock;
        this.work = work;
        this.vetRepository = vetRepository;
        this.testingRepository = testingRepository;
    }

    /// <summary>
    /// Creates a veterinarian.
    /// </summary>
    /// <param name="newVetDTO">The newVetDTO containing all required parameters.</param>
    /// <returns>The newly created veterinarian.</returns>
    public async Task<VeterinarianDTO> CreateVeterinarian(NewVeterinarianDTO newVetDTO)
    {
        var now = clock.Now.ToDateTimeUtc();

        var vet = Mapper.Map<Veterinarian>(newVetDTO);
        var veterinarian = await vetRepository.Create(vet, now);
        // Call the second repository method here.

        // Commit the database changes from both repositories.
        this.work.CommitChanges();
        return Mapper.Map<VeterinarianDTO>(veterinarian);
    }
}

以下是存储库实现。请注意,相同的UnitOfWork对象将注入此存储库。它与注入服务类的UnitOfWork相同。

public class VeterinarianRepository : IVeterinarianRepository
{
    private readonly ICache cache;
    private readonly IUnitOfWork work;

    private readonly TimeSpan cacheTimeSpan = new TimeSpan(5, 0, 0);
    private const CommandType Type = CommandType.StoredProcedure;

    public VeterinarianRepository(ICache cache, IUnitOfWork work)
    {
        this.cache = cache;
        this.work = work;
    }

    public async Task<Veterinarian> Create(Veterinarian vet, DateTime date)
    {
            var connection = work.GetConnection();
            var parameters = new DynamicParameters();
            parameters.Add("@FirstName", vet.FirstName);
            parameters.Add("@LastName", vet.LastName);
            parameters.Add("@Date", date);
            parameters.Add("@User", 1);


            var identity = await connection.ExecuteScalarAsync<int?>("dbo.CreateVeterinarian", parameters, this.work.GetTransaction(), commandType: Type);

            if (identity.HasValue)
                vet.VeterinarianIdentity = identity.Value;
            else throw new Exception();
    }
}

我的思考过程是UnitOfWork类的相同实例将injected放入服务和底层存储库中。 服务可以使用相同的repositories来调用多个connection / transaction,如果一切都成功,它可以commit整个transaction,或者将整个内容回滚。这将回滚任何存储库所做的任何更改。

两个问题:

  1. 这是一种实施这种事情的荒谬方式吗?

  2. 我的VeterinarianRepository实现,在Create方法中,我将Transaction实例创建的UnitOfWork传递给ExecuteScalarAsync方法。然而,在ExecuteScalarAsync执行之后,事务似乎已完成。尝试使用"This SqlTransaction has completed; it is no longer usable."方法时,我收到UnitOfWork CommitChanges()个异常。 我期待交易保持开放,因此如果服务需要,它可以被其他一些存储库使用。

2 个答案:

答案 0 :(得分:0)

  1. 如果在您的存储库中工作,我不认为注入单元是个好主意。首先,您只需要工作单元中的连接对象,因此您不需要通过整个UoW。其次,通过将工作单元传递到存储库,您实际上允许使用它的整个功能 - 不是存储库的责任,通常应用程序服务对此负责。我的意思是你可以(例如,不小心)在一个存储库中调用CommitChanges(),当你再次使用这个方法时,它将是另一个事务。 同样在您的GetConnection()方法中,您不应该开始新的交易。事务创建应该是明确的。在你的情况下没有人猜测,除了连接创建之外,方法运行事务的方法源代码。此外 - 您不需要为充当查询的方法创建事务(没有插入/更新/删除)
  2. 在提交事务两次的情况下可能会出现这种错误(有关详细信息,请参阅"This SqlTransaction has completed; it is no longer usable."... configuration error?)。如上所述,这可以通过设计实现。你应该检查一下。

答案 1 :(得分:0)

您的服务应该促进包含获取配置或连接服务的构造函数的存储库。在我的选择中,该存储库的实现应该以只需要持久存储端点(或连接)来进行调用的方式构建。

应使用定义持久性路径的配置和使用特定实现执行命令的实现来创建VeterinarianRepository。除了VeterinarianRepository之外,在其工作范围之外,没有管理或了解业务流程。另外,我的意见是,无论被调用的VeterinarianService做了什么或不做了什么,你的VeterinarianRepository都应该能够运作。我不知道这对于短小精悍是否有帮助,但是,带有自己强化实施的ORM对通用服务并不是非常友好的