如何编写EF7(核心)/ SQL友好的多对多LINQ查询?
比如说我有很多会说多种语言的朋友,我想找到一组特定语言的朋友。
class Friend
{
Guid Id { get; set; }
ICollection<FriendLanguage> Languages { get; set; }
}
class Language
{
Guid { get; set; }
ICollection<FriendLanguage> Friends { get; set; }
}
class FriendLanguage
{
Friend { get; set; }
Language { get; set; }
}
鉴于我有一组语言ID IEnumerable<Guid>
,我希望找回所有使用这些语言的朋友。
我试过了......
friends
.Include(o => o.Languages).ThenInclude(o => o.Language)
.SelectMany(o => o.Languages).Select(o => o.Language.Id)
.Intersect(languages);
...但这只会返回一组简化的Guids ...不完全确定从哪里开始,或者即使我走在正确的道路上。
答案 0 :(得分:3)
如果我理解正确,您希望从列表中找到发言全部语言的朋友。
表达您要求的最自然的LINQ查询将是:
var friends = db.Friends
.Include(o => o.Languages).ThenInclude(o => o.Language)
.Where(o => languages.All(id => o.Languages.Any(fl => fl.Language.Id == id)));
不幸的是,它不是SQL友好的。事实上,EF Core目前无法将其转换为SQL,并将读取内存中的数据并在那里进行过滤。
所以你可以改用它:
var friends = db.Friends
.Include(o => o.Languages).ThenInclude(o => o.Language)
.Where(o => o.Languages.Count(fl => languages.Contains(fl.Language.Id)) == languages.Count);
翻译成这样的东西:
SELECT [o].[Id]
FROM [Friends] AS [o]
WHERE (
SELECT COUNT(*)
FROM [FriendLanguages] AS [fl]
WHERE [fl].[LanguageId] IN ('6e64302f-24db-4717-a5fe-2cc61985ca3a', '2c216a63-1f6a-4fad-9105-d5f8ece3fa3c') AND ([o].[Id] = [fl].[FriendId])
) = @__languages_Count_1
ORDER BY [o].[Id]
如果你确实想要从列表中说出任何语言的朋友,那么Where
会更简单:
.Where(o => o.Languages.Any(fl => languages.Contains(fl.Language.Id)))
,SQL是:
SELECT [o].[Id]
FROM [Friends] AS [o]
WHERE EXISTS (
SELECT 1
FROM [FriendLanguages] AS [fl]
WHERE [fl].[LanguageId] IN ('ed3f85a7-e122-45dd-b0af-2020052d55a7', '4819cb7d-ad43-41a0-a3a1-979b7abc6265') AND ([o].[Id] = [fl].[FriendId]))
ORDER BY [o].[Id]