我有以下实体:
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
获得ProductTypeLocalization
和Language == "en"
。
我可以在每个上使用FirstOrDefault,但是我认为这不是一种有效的方法。
什么是最好的方法?
答案 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();