【发布时间】:2021-10-13 00:21:55
【问题描述】:
我正在尝试在 ASP.NET Core Web API 中使用 Dapper 实现工作单元存储库模式。
我已经创建了模型、存储库和 UOW。当我尝试执行 GET 请求时出现错误
System.InvalidOperationException: BeginExecuteReader 需要 命令在连接分配给 命令处于待处理的本地事务中。交易属性 的命令尚未初始化。
这是我的控制器;
public class CityController : ControllerBase
{
private readonly IUnitOfWork unitOfWork;
public CityController(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
[HttpGet]
public async Task<IEnumerable<City>> GetAll()
{
return await unitOfWork.Cities.All();
}
CityRepository.cs
internal class CityRepository : GenericRepository<City>, ICityRepository
{
public CityRepository(IDbTransaction transaction)
: base(transaction)
{
}
public async Task<IEnumerable<City>> All()
{
var model = await Connection.QueryAsync<City>("SELECT * FROM DT_Inspection.City");
return model.ToList();
}
}
public IConfiguration configuration;
private IDbTransaction _transaction;
private IDbConnection _connection;
ICityRepository _cityRepository;
private bool _disposed;
public UnitOfWork(IConfiguration _configuration)
{
configuration = _configuration;
_connection = new SqlConnection(_configuration.GetConnectionString("DefaultConnection"));
_connection.Open();
_transaction = _connection.BeginTransaction();
}
public ICityRepository Cities { get { return _cityRepository ?? (_cityRepository = new CityRepository(_transaction)); }
public void Commit()
{
try
{
_transaction.Commit();
}
catch
{
_transaction.Rollback();
throw;
}
finally
{
_transaction.Dispose();
_transaction = _connection.BeginTransaction();
resetRepositories();
}
}
【问题讨论】:
-
在尝试使用“存储库”之前尝试编写正确 ADO.NET 代码。正如错误所说,您需要将 DbCommand 与事务显式链接。或者您可以使用 TransactionScope。 Dapper 是 ADO.NET 之上的一个非常薄的映射层。您仍然需要知道如何使用 ADO.NET。顺便说一句,您尝试创建的实际上是一个数据访问对象,使用悲观(即不是非常可扩展)并发。不是一个真正的存储库,它使持久性看起来像内存中的操作。
-
您尝试做的事情显式地阻止了 ADO.NET 通过 DataTables 或 EF 的 DbContext 支持的更具可扩展性 (100-1000x) 的乐观并发。如果您使用显式事务,您将陷入死锁,因此您需要为它们做好准备。使用事务的简单方法(无论您是否将它们包装在 UoW 中)是在打开连接之前创建一个TransactionScope。新连接将自动加入事务
-
否则需要将数据库事务传递给每个命令。在 Dapper 的情况下,这意味着将其作为
Query或Execute的transaction参数传递。Dapper不会绕过 ADO.NET,它会为您传递给它的 SQL 字符串创建和缓存DbCommand实例。不用说,“存储库”实现开始泄漏,因为CityRepository类需要访问存储在UoW中的_transaction字段 -
顺便说一句,UoW 必须是一次性的,它必须处理它持有的连接和事务。两者都应该是短暂的。
_transaction = _connection.BeginTransaction();违反了工作单元模式。您所拥有的不是表示工作单元,而是使用隐式事务的泄露的持久连接,如果您使用特定的连接字符串设置,Oracle 或 SQL Server 就会这样做。但是很少有人使用它们,因为它们会使死锁和阻塞变得更糟
标签: c# asp.net-core asp.net-web-api unit-of-work