网络核心:带有ThenInclude过滤器和Where的异步方法

时间:2019-08-12 00:15:39

标签: c# entity-framework asp.net-core .net-core

我正在尝试使用带有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();
        }

How to add where clause to ThenInclude

1 个答案:

答案 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关心的字段即可。