我正在尝试使用带有ThenInclude过滤器和Where的Async方法。
引发此错误。认为这与以下行有关:
LkDocumentTypeProduct = new Collection<LkDocumentTypeProduct>(dept.LkDocumentTypeProduct.Where(c => c.LkDocumentTypeId == lkDocumentTypeId).ToList())
,并在我的App Service中也跳过了以下几行:我将如何使它们然后包括“ Filter Async”(异步过滤器)并解决跳过行的问题?
//ITS SKIPPING THESE THREE LINES BELOW
IEnumerable<Product> ProductModel = mapper.Map<IEnumerable<Product>>(Products);
var ProductDto = mapper.Map<IEnumerable<Product>, IEnumerable<ProductDto>>(ProductModel);
return ProductDto;
错误:
ArgumentException: Expression of type 'System.Collections.Generic.IAsyncEnumerable`1[Data.Entities.LkDocumentTypeProduct]' cannot be used for parameter of type
'System.Collections.Generic.IEnumerable`1[Data.Entities.LkDocumentTypeProduct]' of method 'System.Collections.Generic.List`1[Data.Entities.LkDocumentTypeProduct] ToList[LkDocumentTypeProduct]
(System.Collections.Generic.IEnumerable`1[Data.Entities.LkDocumentTypeProduct])'
Parameter name: arg0
存储库:
public async Task<IEnumerable<Product>> GetProductsByDocumentType(int lkDocumentTypeId)
{
var ProductsByDocumentType = All
.Include(dept => dept.LkDocumentTypeProduct)
.Select(dept => new Product
{
ProductId = dept.ProductId,
ProductName = dept.ProductName,
ProductCode = dept.ProductCode,
LkDocumentTypeProduct = new Collection<LkDocumentTypeProduct>(dept.LkDocumentTypeProduct.Where(c => c.LkDocumentTypeId == lkDocumentTypeId).ToList())
}).Where(dept=>dept.LkDocumentTypeProduct.Any());
return await ProductsByDocumentType.ToListAsync();
}
AppService:
public async Task<IEnumerable<ProductDto>> GetProductsByDocumentTypeId(int id)
{
var Products = await ProductRepository.GetProductsByDocumentType(id);
//ITS SKIPPING THESE THREE LINES BELOW !
IEnumerable<Product> ProductModel = mapper.Map<IEnumerable<Product>>(Products);
var ProductDto = mapper.Map<IEnumerable<Product>, IEnumerable<ProductDto>>(ProductModel);
return ProductDto;
}
控制器:
[HttpGet("[Action]/{id}")]
[ProducesResponseType(typeof(ProductDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<IEnumerable<ProductDto>>> GetByDocumentType(int id)
{
IEnumerable<ProductDto> Product = await ProductAppService.GetProductsByDocumentTypeId(id);
if (Product == null)
{
return NotFound();
}
答案 0 :(得分:1)
这不是“跳过”行,而是此行的错误: IEnumerable ProductModel = mapper.Map>(产品);
Automapper是一个助手,而不是魔术师。 :)
根据我的猜测,您有一种方法将返回告诉您要转换为DTO的Product实体。 Automapper可以通过异步方式为您提供帮助:
首先,请忽略您正在使用异步集合的事实。 Automapper确实可以很好地将对象映射到彼此,而不是集合,而是在 in 集合中工作。
在您的情况下,鉴于您的存储库正在返回IEnumerable<Product>
,要将其映射到IEnumerable<ProductDto>
,请在您的服务中使用以下方法:
public async Task<IEnumerable<ProductDto>> GetProductsByDocumentTypeId(int id)
{
var products = await ProductRepository.GetProductsByDocumentType(id);
var dtos = await products.Select(x => Mapper.Map<ProductDto>(x)).ToListAsync();
return dtos;
}
这应该可以使您工作,但并不理想。原因是存储库正在返回实体的集合,这意味着我们将在每个返回的实体中实现所有字段,无论是否需要它们。如果Automapper“接触”尚未急切加载的任何相关实体,这也将打开大门,因为这将触发对数据库的延迟加载调用。您可以使用Include
来满足此要求,但是随着代码的成熟,这些惰性加载命中事件可能会潜入,或者您渴望不再需要的字段的加载成本。
Automapper提供了一种出色的方法来解决此问题,ProjectTo
,但是它需要代码来利用EF的IQueryable
实现而不是返回IEnumerable
。
例如,如果我们将存储库方法更改为:
public IQueryable<Product> GetProductsByDocumentType(int lkDocumentTypeId)
{
var query = _dbContext.Products
.Where(p => p.LkDocumentTypeProduct.Any(c => c.LkDocumentTypeId == lkDocumentTypeId));
return query;
}
然后在服务中
public async Task<IEnumerable<ProductDto>> GetProductsByDocumentTypeId(int id)
{
var dtos = await ProductRepository.GetProductsByDocumentType(id)
.ProjectTo<ProductDto>().ToListAsync();
return dtos;
}
这是将存储库更改为返回一个IQueryable,基本上是返回一个承诺,以获取我们的使用者可以查询的一组已知实体。对于该属性返回的内容,我会有些警惕。我见过一些案例,其中类似的方法会执行return _context.Products.ToList();
之类的事情,这是真正的性能/资源陷阱。
我们将Queryable输入到Automapper提供的扩展方法ProjectTo中,然后该方法仅查询满足ProductDto所需的列。这种方法的优点是可观的。我们不需要显式地Include
相关表,也不必担心会触发延迟加载的引用,而构建的查询只需拉回DTO关心的字段即可。