【问题标题】:Filter child collection of one entity过滤一个实体的子集合
【发布时间】:2019-07-10 02:03:59
【问题描述】:

我有以下实体:

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

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

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

最好的方法是什么?

【问题讨论】:

  • 你是对的。我错过了人际关系。刚刚更新了我的代码。对不起。
  • 我猜最好的方法是做一个子查询
  • 是的,但是是哪一个? Select 只能用于集合。
  • 您的ProductType 将拥有ProductTypeLocalization 的列表,对吗?您在查询中想要什么,那么它应该 ProductType 应该有单个 ProductTypeLocalization
  • 是的,基本上我需要选择 ProductType 和一个 ProductTypeLocazition ,其中 Language 匹配“en”......但我一直在尝试 Select 和 SelectMany,但现在很幸运。您能否提供代码来阐明您的意思?

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


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 2016-12-27
    • 2013-03-23
    • 2014-10-26
    • 1970-01-01
    • 1970-01-01
    • 2017-07-14
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    相关资源
    最近更新 更多