使用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/