首页 > 解决方案 > 过滤一个实体的子集合

问题描述

我有以下实体:

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 ???

我需要Name与. Description_ProductTypeLocalizationLanguage == "en"

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

最好的方法是什么?

标签: linqlinq-to-entitiesentity-framework-core

解决方案


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();

推荐阅读