Come scrivere una query LINQ many-to-many EF7 (core) / SQL friendly?
Ad esempio, ho molti amici che parlano molte lingue e voglio trovare tutti gli amici per un determinato gruppo di lingue.
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; }
}
Dato che ho un set di ID di lingua IEnumerable<Guid>
, voglio recuperare tutti gli amici che parlano quelle lingue.
Ho provato questo ...
friends
.Include(o => o.Languages).ThenInclude(o => o.Language)
.SelectMany(o => o.Languages).Select(o => o.Language.Id)
.Intersect(languages);
... ma questo restituisce solo un set ridotto di Guids ... non del tutto sicuro di dove andare da qui, o anche se sono sulla strada giusta.
Se ho capito bene, vuoi ottenere gli amici che parlano tutte le lingue dalla lista.
La query LINQ più naturale che esprima il tuo requisito sarebbe:
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)));
Sfortunatamente non è SQL friendly. In effetti EF Core attualmente non può tradurlo in SQL e leggerà i dati in memoria e farà il filtraggio lì.
Quindi puoi usare questo invece:
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);
che si traduce in qualcosa di simile:
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]
Se davvero vuoi che gli amici che parlano una delle lingue dalla lista, allora il Where
è più semplice:
.Where(o => o.Languages.Any(fl => languages.Contains(fl.Language.Id)))
e l'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]