过滤一个实体的子集合

问题描述 投票:1回答:1

我有以下实体:

public class Product {
  public Int32 ProductId { get; set; }
  public Double Price { get; set; }
  public virtual ProductType ProductType { get; set; }
}

public class ProductType {
  public Int32 ProductTypeId { get; set; }
  public virtual ICollection<ProductTypeLocalization> ProductTypeLocalizations { get; set; }
}

public class ProductTypeLocalization {
  public Int32 ProductTypeId { get; set; }
  public String Language { get; set; }
  public String Name { get; set; }
  public String Description { get; set; }
  public virtual ProductType { get; set; }
}

然后我有一个查询如下:

var models = await products.Select(product => new {
  Id = product.Id,
  Price = product.Price,
  ProductType = new {
    Id = product.ProductType.ProductTypeId,
    Name = ???,
    Description = ???
  }
}).ToListAsync()

在我的查询中显示

Name = ???,
Description ???

我需要从NameDescription获得ProductTypeLocalizationLanguage == "en"

我可以在每个上使用FirstOrDefault,但我认为这不是一种有效的方法。

最好的方法是什么?

linq linq-to-entities entity-framework-core
1个回答
3
投票

LEFT OUTER JOIN翻译似乎是这种情况的最佳选择。

理论上,EF Core查询翻译器应该能够将常见的FirstOrDefault()表达式合并为单个LEFT OUTER JOIN,就像它对可选参考导航属性一样。

在实践中(截至目前最新的EF Core 2.2),它不会这样做,并为每个选定的字段生成单独的相关子查询。

假设每种产品类型对于特定语言都有0或1个本地化,可以使用SelectMany实现所需的转换,如下所示:

var models = await products.SelectMany(
    product => product.ProductType.ProductTypeLocalizations
        .DefaultIfEmpty()
        .Where(ptl => ptl == null || ptl.Language == "en"),
    (product, ptl) => new
    {
        Id = product.ProductId,
        Price = product.Price,
        ProductType = new
        {
            Id = product.ProductType.ProductTypeId,
            Name = ptl.Name,
            Description = ptl.Description
        }
    })
    .ToListAsync();

或使用LINQ查询语法的等效且更易读的版本:

var models = await (
    from product in products
    let pt = product.ProductType
    from ptl in pt.ProductTypeLocalizations.DefaultIfEmpty()
    where ptl == null || ptl.Language == "en"
    select new
    {
        Id = product.ProductId,
        Price = product.Price,
        ProductType = new
        {
            Id = pt.ProductTypeId,
            Name = ptl.Name,
            Description = ptl.Description
        }
    }).ToListAsync();
© www.soinside.com 2019 - 2024. All rights reserved.