过滤一个实体的子集合

时间:2019-02-15 21:59:17

标签: linq linq-to-entities entity-framework-core

我有以下实体:

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,但是我认为这不是一种有效的方法。

什么是最好的方法?

1 个答案:

答案 0 :(得分: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();