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

标签 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,或将整个过程回滚。这将回滚任何存储库所做的任何更改。

两个问题:


这是实现这种事情的荒谬方法吗?
我的VeterinarianRepository实现在Create方法中,将由Transaction实例创建的UnitOfWork传递给ExecuteScalarAsync方法。但是,在执行ExecuteScalarAsync之后,事务似乎已完成。尝试使用"This SqlTransaction has completed; it is no longer usable."方法时出现UnitOfWork CommitChanges()异常。
我期望事务保持打开状态,以便在服务需要的情况下可以被其他一些存储库使用。

最佳答案

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

关于c# - 工作单元:Dapper和存储库模式-C#.Net,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39980459/

相关文章:

c# - ' Entity Framework 6.1。 3' already installed. Failed to add reference to ' System.ComponentModel.DataAnnotations'

c# - 使用关系更新数据表

.net - 如何定义公共(public)静态只读字段?

asp.net - 在任何操作之前执行代码

c# - 如何使用Automapper构造没有默认构造函数的对象

c# - 使用小巧玲珑的插入枚举处理

c# - 包含 TableValueParameter 的 Dapper 存储过程

c# - 如何在 ASP .NET Core 中初始化主机之前读取配置设置?

c# - 使用可变字段创建匿名类型?

c# - multi.Read 的输出参数始终为 null