【发布时间】:2022-02-20 16:45:12
【问题描述】:
我正在尝试使用 aspnetboilerplate 让 OData 处理 DTO 对象而不是实体。
我制作了一个控制器,灵感来自 AbpODataEntityController.cs,它继承自 AbpODataController。
我已经使用 AutoMapper.ExpressionMapping 的 UseAsDataSource().For<Dto>() 在 DTO 和实体之间进行映射
public abstract class DtoODataControllerBase<TEntity, TEntityDto, TPrimaryKey> : AbpODataController
where TEntity : class, IEntity<TPrimaryKey>
where TPrimaryKey : IEquatable<TPrimaryKey>
{
private readonly IRepository<TEntity, TPrimaryKey> _repository;
private readonly IMapper _mapper;
protected DtoODataControllerBase(IRepository<TEntity, TPrimaryKey> repository, IMapper mapper)
{
_repository = repository;
_mapper = mapper;
}
[EnableQuery]
public virtual IQueryable<TEntityDto> Get()
{
CheckGetAllPermission();
return _repository.GetAll().UseAsDataSource(_mapper).For<TEntityDto>();
}
// Permission checking code removed for brevity
}
它 - 有点 - 工作。但是,一旦我开始在我的 OData 请求中使用 $select,就会发生一些事情,即 UnitOfWork 尝试处置存储库,而 OData 在存储库的 DbContext 使用的底层连接上仍然有一个打开的 Datareader,我得到以下信息例外:
System.InvalidOperationException: There is already an open DataReader associated with this Connection which must be closed first.
at Microsoft.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command)
at Microsoft.Data.SqlClient.SqlInternalTransaction.Rollback()
at Microsoft.Data.SqlClient.SqlInternalTransaction.Dispose(Boolean disposing)
at Microsoft.Data.SqlClient.SqlInternalTransaction.Dispose()
at Microsoft.Data.SqlClient.SqlTransaction.Dispose(Boolean disposing)
at System.Data.Common.DbTransaction.Dispose()
at Microsoft.EntityFrameworkCore.Storage.RelationalTransaction.Dispose()
at Abp.EntityFrameworkCore.Uow.DbContextEfCoreTransactionStrategy.Dispose(IIocResolver iocResolver)
at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.DisposeUow()
at Abp.Domain.Uow.UnitOfWorkBase.Dispose()
at Abp.AspNetCore.Uow.AbpUnitOfWorkMiddleware.Invoke(HttpContext httpContext)
at Abp.AspNetCore.Security.AbpSecurityHeadersMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Watch.BrowserRefresh.BrowserRefreshMiddleware.InvokeAsync(HttpContext context)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
在 Postman 中,它仍然“看起来不错”,因为响应已经写入并且是“有效的”,但似乎响应通信会因异常而中断,任何不太可靠的东西都会抱怨。
我的实际控制器是这样的:
[AbpAuthorize]
public class myEntityController : DtoODataControllerBase<myEntity, myDto>, ITransientDependency
{
public myEntityController (IRepository<myEntity> repository, IMapper mapper) : base(repository, mapper)
{
}
}
有趣的是,当将 abp 的 AbpODataEntityController 与实际实体一起使用时,一切都很好,没有处理问题。
我试过在我的控制器上关闭 UoW 和其他一些东西,但它没有帮助,查看 UnitOfWork 中间件,我知道即使我禁用 UoW,当中间件完成时 UoW 仍然会被处理,因此触发问题。
唯一的区别似乎是UseAsDataSource 的使用,猜测它保持开放阅读器是出于……原因……
关于如何让 abp、automapper 的表达式映射和 odata 一起发挥作用的任何想法/线索?
编辑:
我能够使用带有 DbContext、没有存储库、没有 Abp 控制器的简单 ODataController 重现该问题。 UnitOfWorkMiddleware 被处理后,会处理 UnitOfWork 本身,它会在自己身后进行清理......但由于某种原因,使用 $select 会使映射器/表达式映射器/odatacontroller 保持 Datareader 处于打开状态...... 我会继续诊断,直到找到让读者保持开放的原因......我目前的猜测是 ODataController,它可能是枚举的......我会深入研究并报告......
【问题讨论】:
标签: c# asp.net-core odata automapper aspnetboilerplate