使用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()
个异常。
我期待交易保持开放,因此如果服务需要,它可以被其他一些存储库使用。
答案 0 :(得分:0)
CommitChanges()
,当你再次使用这个方法时,它将是另一个事务。
同样在您的GetConnection()
方法中,您不应该开始新的交易。事务创建应该是明确的。在你的情况下没有人猜测,除了连接创建之外,方法运行事务的方法源代码。此外 - 您不需要为充当查询的方法创建事务(没有插入/更新/删除)答案 1 :(得分:0)
您的服务应该促进包含获取配置或连接服务的构造函数的存储库。在我的选择中,该存储库的实现应该以只需要持久存储端点(或连接)来进行调用的方式构建。
应使用定义持久性路径的配置和使用特定实现执行命令的实现来创建VeterinarianRepository
。除了VeterinarianRepository
之外,在其工作范围之外,没有管理或了解业务流程。另外,我的意见是,无论被调用的VeterinarianService
做了什么或不做了什么,你的VeterinarianRepository
都应该能够运作。我不知道这对于短小精悍是否有帮助,但是,带有自己强化实施的ORM对通用服务并不是非常友好的。