过滤一个实体的子集合
Filter child collection of one entity
我有以下实体:
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 ???
我需要用 Language == "en"
从 ProductTypeLocalization
得到 Name
和 Description
。
我可以对每个都使用 FirstOrDefault,但我认为这不是一种有效的方法。
最好的方法是什么?
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();
我有以下实体:
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 ???
我需要用 Language == "en"
从 ProductTypeLocalization
得到 Name
和 Description
。
我可以对每个都使用 FirstOrDefault,但我认为这不是一种有效的方法。
最好的方法是什么?
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();