Partage
  • Partager sur Facebook
  • Partager sur Twitter

LinqToEntities: Différence entre .Include(), .Join(), .Load(

Sujet résolu
    26 juillet 2011 à 16:06:35

    Bonjour,

    J'ai une question certainement bête, mais je me lance quand même.
    Quelle est la différence entre:
    - .Include()
    - .Join()
    - .Load()

    J'utilise .Include() car je suis tombé sur un tuto présentant cela mais je ne connais pas la différence avec .Join().

    Exemple:
    using(var db = new DatabaseContext())
    {
    	var query = Db.client.Include("commande");
    	var result = query.ToList(); //déclenche 1 requête
    }
    


    Peut on utiliser .Join() dans ce cas où est ce que ça n'a rien à voir.

    Si je ne dis pas de bétises, je crois que l'on peut aussi faire.
    using(var db = new DatabaseContext())
    {
    	var query = Db.client;
    	var result = query.ToList(); //déclenche 1 requête
    	foreach(var oneClient in result){
    		oneClient.commande.Load();//déclenche 1 requête à chaque itération
    	}
    }
    
    • Partager sur Facebook
    • Partager sur Twitter
      26 juillet 2011 à 17:11:09

      La doc complète à ce sujet est consultable en ligne (ici), mais voici quelques explications:

      Supposons que ton modèle de données contienne une table Order (qui représente une commande) et une table Customer (qui représente un client). Chaque commande est passée par un et un seul client, donc la table Order contient une clé étrangère CustomerId qui référence la clé primaire Id de la table Customer.

      Ton objectif est d'extraire une liste de commandes, et tu veux récupérer en même temps les informations sur les clients qui ont passé les commandes. Il y a différentes manières de procéder selon la manière dont tu as configuré ton modèle de données.

      1) Le plus classique est de donner à ton entité Order une propriété Customer (de type Customer), et à l'entité Customer une propriété Orders (de type EntityCollection<Order>). Tu peux alors écrire une requête portant sur la table Orders, en utilisant Include() pour indiquer que tu veux récupérer les Customers concernés au passage (référence ici):

      using(var db = new DatabaseContext())
      {
          var allOrdersWithCustomers = Db.Orders.Include("Customer");
          foreach(Order order in allOrdersWithCustomers)
          {
              // order.Customer représente le client qui a passé la commande
          }
      }
      

      De la même manière tu peux avoir envie de charger des clients et la liste des commandes qu'ils ont effectuées:

      using(var db = new DatabaseContext())
      {
          var allCustomersWithOrders = Db.Customers.Include("Orders");
          foreach(Customer customer in allCustomersWithOrders)
          {
              // customer.Orders représente la liste des commandes passées par le client
          }
      }
      


      Cependant, ça ne t'intéresse peut-être pas de charger la liste des commandes de tous les clients chargés: il est possible que le chargement ne soit nécessaire que pour certains d'entre eux. A ce moment-là, tu peux utiliser Load() au lieu d'Include() pour demander le chargement de leur collection de commandes:

      using(var db = new DatabaseContext())
      {
          var allCustomers = Db.Customers;
          foreach(Customer customer in allCustomers)
          {
              // faire qq chose avec le customer
              if( /* on a besoin de sa liste de commandes */)
              {
                  customer.Orders.Load(); // charge la liste
                  // faire qq chose avec customer.Orders
              }
          }
      }
      


      Le Load() s'utilise toujours sur une collection d'entités ou sur une référence d'entité. Il faut donc faire attention:

      using(var db = new DatabaseContext())
      {
          var allOrders = Db.Orders
          foreach(Order order in allOrders)
          {
              // faire quelque chose avec la commande
              if( /* on a besoin de son client */ )
              { 
                  order.Customer.Load() // ERREUR: order.Customer est null !
                  order.CustomerReference.Load() //ok, order.Customer est maintenant chargé.
              }
          }
      }
      


      De manière générale il vaut mieux diminuer le nombre de requêtes pour améliorer les performances. Donc en fonction des cas il peut être préférable d'utiliser Include() (pour charger systématiquement) ou Load() (pour charger à la demande).

      2) Supposons à présent que tu n'as pas donné à ton entité Order une propriété Customer (tu n'as que le CustomerId), mais que tu veux quand même charger les clients ayant passé les commandes. Dans ce cas, tu vas devoir faire un Join() (réference ici):

      using(var db = new DatabaseContext())
      {
          var query =
              from order in db.Orders
              join customer in db.Customers
              on order.CustomerId equals customer.Id
              select new
              {
                  Order = order,
                  Customer = customer
              };
      
          foreach(var orderAndCustomer in query)
          {
              // la commande est donnée par orderAndCustomer.Order
              // le client est donné par orderAndCustomer.Customer
          }
      }
      


      Ici j'ai écrit la requête en LINQ parce que c'est un poil plus simple pour les Join, mais la même requête peut s'écrire

      var query = db.Orders.Join(db.Customers, 
          order => order.CustomerId, 
          customer => customer.Id, 
          (order, customer) => new { Order = order, Customer = customer };
      


      Voilà, en espérant que tu y verras plus clair avec ça ^^
      • Partager sur Facebook
      • Partager sur Twitter
        26 juillet 2011 à 17:33:11

        Je te remercie énormement Orwell.
        Tes explications sont claires et je pense avoir donc compris la nuance entre les 3.

        On choisit entre .Include et .Load en fonction des objets à charger.
        .Include() : l'objet en question sera chargé systématiquement (automatique).
        .Load() : on indique que l'on désire charger l'objet (manuel).

        .Join() intervient lui seulement si nos objets ne pointent pas sur d'autres objets mais sur des "ID".

        1) Je ne dis pas de bétises, c'est bien ça?

        Autre question concernant ce point.


        2) Est il possible d'avoir la liste de tous les clients dont au moins une commande est en cours mais SANS charger les commandes?


        using(var db = new DatabaseContext())
        {
            var result = Db.Customers
                .Include("Orders")
                .Where(c=> c.Orders.Any(o=> o.isFinis ==0));
        }
        
        • Partager sur Facebook
        • Partager sur Twitter
          26 juillet 2011 à 17:39:02

          Citation : Takinelinfo

          1) Je ne dis pas de bétises, c'est bien ça?

          Oui, bien que Join() puisse être aussi utilisé même si tes entités possèdent des propriétés vers les entités auxquelles elles sont liées (comme c'est ton cas). Par exemple pour ton autre topic je pense qu'un Join pourrait mieux convenir.

          Citation : Takinelinfo

          2) Est il possible d'avoir la liste de tous les clients dont au moins une commande est en cours mais SANS charger les commandes?

          Oui bien sûr, il suffit de retirer le Include() de ta requête.
          • Partager sur Facebook
          • Partager sur Twitter
            26 juillet 2011 à 17:47:11

            Citation : Orwell


            Citation : Takinelinfo

            2) Est il possible d'avoir la liste de tous les clients dont au moins une commande est en cours mais SANS charger les commandes?


            Oui bien sûr, il suffit de retirer le Include() de ta requête.


            </citation>

            Ok je pensais qu'à partir du moment où l'on voulait faire une comparaison sur un sous objet il fallait forcément que celui ci soit "include" pour ne pas avoir l'objet à NULL.


            Citation

            Oui, bien que Join() puisse être aussi utilisé même si tes entités possèdent des propriétés vers les entités auxquelles elles sont liées (comme c'est ton cas). Par exemple pour ton autre topic je pense qu'un Join pourrait mieux convenir.



            C'est là que je ne vois pas la subtilité alors, parce que je ne vois pas la différence qu'il y a à utiliser l'un ou l'autre si on pointe sur des objets.
            • Partager sur Facebook
            • Partager sur Twitter
              26 juillet 2011 à 18:00:17

              Citation : Takinelinfo

              Ok je pensais qu'à partir du moment où l'on voulait faire une comparaison sur un sous objet il fallait forcément que celui ci soit "include" pour ne pas avoir l'objet à NULL.


              Non parce que les objets que tu manipules dans tes requêtes ne sont pas instanciés : le code de tes expressions est converti en SQL et pas exécuté :)

              Citation

              C'est là que je ne vois pas la subtilité alors, parce que je ne vois pas la différence qu'il y a à utiliser l'un ou l'autre si on pointe sur des objets.


              Pour être honnête avec toi, je n'utilise jamais de Join(). :-°
              Cependant on peut imaginer des scénarios un peu tordus où ça pourrait s'avérer utile... Mais Include() reste souvent une solution préférable.
              • Partager sur Facebook
              • Partager sur Twitter

              LinqToEntities: Différence entre .Include(), .Join(), .Load(

              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
              • Editeur
              • Markdown