web-dev-qa-db-fra.com

EF Core comment sélectionner une entité avec une relation plusieurs à plusieurs

J'ave structure de table comme ça

  1. utilisateurs
  2. des profils d'utilisateurs
  3. les profils

la description:

utilisateur a de nombreux profils utilisateur, la table user_profile joint les tables utilisateur et profil (il existe une relation multiple à utilisateur entre l'utilisateur et la table de profil).

utilisateur> un-à-plusieurs> user_profiles> un-à-un> profils

utilisateur> plusieurs profils d'utilisateurs> un profil

Problème:

Comment puis-je sélectionner un utilisateur avec un profil en utilisant linq.

échantillon:

var user=cbContext.user.include("user_profiles").include("profiles").Where(predicate).FirstOrDefault();
3

Trouvé la réponse

dbContext.Users
  .Include(user => user.UserProfiles)
  .ThenInclude(userProfiles => userProfiles.Profile) 
  .Where(predicate)
  .FirstOrDefault();
5

Si vous avez une structure d'entité complète, le plusieurs-plusieurs est similaire à:

class User
{
     public int Id {get; set;}

     // every User has zero or more Profiles (many-to-many)
     public virtual ICollection<Profile> Profiles {get; set;}

     ...
}
class Profile
{
     public int Id {get; set;}

     // every Profile belongs to zero or more Users (many-to-many)
     public virtual ICollection<User> Userss {get; set;}

     ...
}

Si vos classes sont conçues comme cela et que vous voulez des "utilisateurs ... avec leurs profils", vous pouvez utiliser les collections et sélectionner les propriétés que vous prévoyez d'utiliser:

using (var dbContext = new MyDbContext(...))
{
    var requestedUsers = dbContext.Users
        .Where(user => ...)                      // only if you don't want all Users
        .Select(user => new
        {    // Select only the properties you plan to use:
             Id = user.Id,
             Name = user.Name,
             ...
             Profiles = user.Profiles
                 .Where(profile => ...)         // only if you don't want all profiles
                 .Select(profile => new
                 {
                      Name = profile.Name,
                      ...
                 })
                 .ToList(),
        })

L'une des parties les plus lentes d'une requête de base de données est le transport des données sélectionnées du système de gestion de base de données vers votre processus. Il est donc sage de limiter les données que vous transférez aux données que vous prévoyez d'utiliser.

Include sélectionnera toutes les propriétés de l'objet inclus, y compris les clés primaires et étrangères. Include a Collection sélectionnera la collection complète, même si vous prévoyez n'en utiliser que quelques-uns.

Conseil: utilisez uniquement Include si vous envisagez de modifier les données extraites. Utiliser Select est plus rapide. Select uniquement les propriétés que vous comptez réellement utiliser

Utiliser (Groupe) Rejoindre si vous ne pouvez pas utiliser ICollection

J'ai compris de certains que vous ne pouvez pas utiliser le virtual ICollections lorsque vous utilisez EF-core. Dans ce cas, vous devrez effectuer un groupeJoin vous-même

dbContext.Users
    .Where(user => ...)
    .GroupJoin(dbContext.UserProfiles,         // GroupJoin the users with the UserProfiles
        user => user.Id                        // from every user take the Id
        userProfile => userProfile.UserId,     // from every userProfile take the UserId
        (user, userProfiles) =>  new           // when thay match,
        {                                      // take the user and its matching UserProfiles
            UserId = user.Id,                  // again: select only properties you plan to use
            UserName = user.Name,
            ...

            // for the Profiles, do a new Join with the Profiles
            Profiles = userProfiles.Join(dbContext.Profiles, // join with Profiles
               userProfile => userProfile => profileId       // from the userProfile take profileId
               profile => profile.Id,                        // from the Profile take the Id
               (userProfile, profile) => new                 // when they match, make an object
               {   // again: use only properties you plan to use
                   ProfileId = profile.Id,
                   ProfileName = profile.Name,
                   ...
               })
               .ToList(),
        });

Attention: vous n'obtiendrez pas d'utilisateurs sans profils!
C'est une jointure interne.

Si vous souhaitez également des utilisateurs sans profils, utilisez Left-Outer-GroupJoin comme décrit ici dans Stackoverflow Faites défiler l'écran vers le bas pour obtenir la réponse la mieux classée, ce qui est bien meilleur que la réponse sélectionnée.

3
Harald Coppoolse

Outre votre propre réponse avec les lambdas et l'utilisation de ThenInclude, qui est ma version préférée pour les requêtes simples avec des relations n à n, vous pouvez également utiliser des chaînes pour spécifier vos inclusions. 

Il vous suffit d'écrire le "chemin" des propriétés séparées par des points . comme ceci:

dbContext.Users
  .Include("UserProfiles.Profile")
  .Where(predicate)
  .FirstOrDefault();

Cela fonctionne pour 1 à 1, 1 à plusieurs et plusieurs à plusieurs relations identiques.

C'est utile lorsque vous avez des inclusions profondes d'entités (mais vous perdez la vérification au moment de la compilation)

1
Pac0