我有以下实体(多对多关系):
public class Fragrance
{
public int FragranceId { get; set; }
public string Brand { get; set; } = null!;
public string Name { get; set; } = null!;
public string Gender { get; set; } = null!;
public int LaunchYear { get; set; }
public virtual List<Note>? Notes { get; set; }
}
public class Note
{
public int NoteId { get; set; }
public string Name { get; set; } = null!;
public string Description { get; set; } = null!;
public virtual List<Fragrance>? Fragrances { get; set; }
}
public class FragranceNote
{
public int FragranceId { get; set; }
public Fragrance Fragrance { get; set; } = null!;
public int NoteId { get; set; }
public Note Note { get; set; } = null!;
}
当用户发送包含香水 ID 的 http 请求时,我想要一个包含来自给定香水的 2 个随机音符的响应,每个音符包含 2 个其他随机香水(与用户发送的不同)。
[
{
"name": "Bergamot",
"fragrances": [
{
"fullName": "Maison Francis Kurkdjian Oud Silk Mood",
"gender": "Unisex"
},
{
"fullName": "Dolce & Gabbana Light Blue Forever",
"gender": "Male"
}
]
},
{
"name": "Pink Pepper",
"fragrances": [
{
"fullName": "Maison Margiela Replica Jazz Club",
"gender": "Male"
},
{
"fullName": "Dior Ambre Nuit",
"gender": "Unisex"
}
]
}
]
我的 EF 查询 看起来像这样:
var notes = _context.Fragrances
.Where(f => f.FragranceId == fragranceId)
.Include(
f => f.Notes!
.OrderBy(r => Guid.NewGuid())
.Take(2)
)
.ThenInclude(
n => n.Fragrances!
.Where(f => f.FragranceId != fragranceId)
.OrderBy(r => Guid.NewGuid())
.Take(2)
)
.First().Notes;
它有效,但我有这个警告:
编译一个查询,通过“包含”或通过投影加载多个集合导航的相关集合,但尚未配置“QuerySplittingBehavior”。默认情况下,实体框架将使用“QuerySplittingBehavior.SingleQuery”,这可能会导致查询性能降低。
我尝试使用
AsSplitQuery()
,但它弄乱了我的响应并且无法加载正确的数据。
罪魁祸首将是
OrderBy
/Include
中的 ThenInclude
这不适用于拆分查询,因为拆分将尝试加载相关注释的所有香水,然后随机订购完整的集合并取顶部2. 因此,它为每个香调挑选的最终香水是从所有选定香调的香水中提取的,从而导致不匹配。
警告是,您正在生成一个潜在的大型笛卡尔积,EF 警告该积可能会影响性能。拆分查询可以避免这种情况,但有局限性,特别是在排序方面,因为它试图拆分相关查询,而不会像延迟加载那样严重。
看起来您正在为某种特定的香水挑选两种随机香调,然后随机挑选两种与该香调相关的香水? (不排除您最初选择的香水)您应该能够做到这一点并避免警告:
var notes = _context.Notes
.Where(n => n.Fragrances.Any(f => f.FragranceId == fragranceId)
.Include(n => n.Fragrances
.OrderBy(f => Guid.NewGuid())
.Take(2))
.OrderBy(n => Guid.NewGuid())
.Take(2);
我相信在没有笛卡尔警告的情况下应该会给你相同的预期结果。如果您想排除所选的香水 ID,则:
.Include(n => n.Fragrances
,Where(f -> f.FragranceId != fragranceId)
.OrderBy(f => Guid.NewGuid())
.Take(2))