543 votes

Join/Where avec LINQ et Lambda

J'ai des problèmes avec une requête écrite en LINQ et Lambda. Jusqu'à présent, j'obtiens beaucoup d'erreurs voici mon code :

int id = 1;
var query = database.Posts.Join(database.Post_Metas,
                                post => database.Posts.Where(x => x.ID == id),
                                meta => database.Post_Metas.Where(x => x.Post_ID == id),
                                (post, meta) => new { Post = post, Meta = meta });

Je suis nouveau dans l'utilisation de LINQ, donc je ne suis pas sûr que cette requête soit correcte.

14 votes

Qu'essayez-vous d'accomplir ?

4 votes

Que voulez-vous que la requête fasse dans une phrase ?

6 votes

Vos principaux sélecteurs sont chemin trop compliqué. Si vous souhaitez effectuer une sélection par ID, x=>x.ID suffit.

1249voto

Daniel Schaffer Points 14707

Je trouve que si vous êtes familier avec la syntaxe SQL, l'utilisation de la syntaxe de requête LINQ est beaucoup plus claire, plus naturelle et permet de repérer plus facilement les erreurs :

var id = 1;
var query =
   from post in database.Posts
   join meta in database.Post_Metas on post.ID equals meta.Post_ID
   where post.ID == id
   select new { Post = post, Meta = meta };

Si vous êtes vraiment bloqué sur l'utilisation des lambdas, votre syntaxe est un peu fausse. Voici la même requête, en utilisant les méthodes d'extension LINQ :

var id = 1;
var query = database.Posts    // your starting point - table in the "from" statement
   .Join(database.Post_Metas, // the source table of the inner join
      post => post.ID,        // Select the primary key (the first part of the "on" clause in an sql "join" statement)
      meta => meta.Post_ID,   // Select the foreign key (the second part of the "on" clause)
      (post, meta) => new { Post = post, Meta = meta }) // selection
   .Where(postAndMeta => postAndMeta.Post.ID == id);    // where statement

11 votes

@Emanuele Greco, concernant votre édition, "L'égalité sur les champs ID est définie dans la condition JOIN ; vous n'avez pas besoin d'utiliser la clause WHERE !": la clause WHERE ne teste pas l'égalité entre les champs ID, elle teste l'égalité entre la colonne post ID et le paramètre id déclaré en dehors de la requête.

9 votes

Superbe pièce de lambda et la citation est facile à utiliser et à comprendre

1 votes

Parfois, les explications de lambda sont écrites en lambda. Bien expliqué.

91voto

Damian Powell Points 4156

On peut faire deux choses avec ça. En utilisant LINQPad (précieux si vous êtes novice en matière de LINQ) et une base de données factice, j'ai construit les requêtes suivantes :

Posts.Join(
    Post_metas,
    post => post.Post_id,
    meta => meta.Post_id,
    (post, meta) => new { Post = post, Meta = meta }
)

ou

from p in Posts
join pm in Post_metas on p.Post_id equals pm.Post_id
select new { Post = p, Meta = pm }

Dans ce cas particulier, je pense que la syntaxe LINQ est plus propre (je passe d'une syntaxe à l'autre en fonction de celle qui est la plus facile à lire).

Ce que je voudrais souligner, c'est que si vous avez des clés étrangères appropriées dans votre base de données (entre post et post_meta), vous n'avez probablement pas besoin d'une jointure explicite, sauf si vous essayez de charger un grand nombre d'enregistrements. Votre exemple semble indiquer que vous essayez de charger un seul article et ses métadonnées. En supposant qu'il y ait plusieurs enregistrements post_meta pour chaque article, vous pourriez faire ce qui suit :

var post = Posts.Single(p => p.ID == 1);
var metas = post.Post_metas.ToList();

Si vous voulez éviter le problème n+1, vous pouvez demander explicitement à LINQ to SQL de charger tous les éléments liés en une seule fois (bien que cela puisse être un sujet avancé pour quand vous serez plus familier avec L2S). L'exemple ci-dessous indique "lorsque vous chargez un article, chargez également tous les enregistrements qui lui sont associés via la clé étrangère représentée par la propriété 'Post_metas'" :

var dataLoadOptions = new DataLoadOptions();
dataLoadOptions.LoadWith<Post>(p => p.Post_metas);

var dataContext = new MyDataContext();
dataContext.LoadOptions = dataLoadOptions;

var post = Posts.Single(p => p.ID == 1); // Post_metas loaded automagically

Il est possible de faire de nombreux LoadWith sur un seul ensemble de DataLoadOptions pour le même type, ou plusieurs types différents. Si vous faites cela souvent, vous devriez peut-être envisager la mise en cache.

1 votes

LinqPad y CRM 2016 ?

70voto

Talspaugh27 Points 117

Daniel a une bonne explication des relations syntaxiques, mais j'ai rédigé ce document pour mon équipe afin de rendre la chose un peu plus simple à comprendre pour eux. J'espère que cela aidera quelqu'un enter image description here

0 votes

Cela ne fonctionne pas lorsque vous traitez simplement une liste de valeurs, comme c'est le cas ici. Il n'y a pas de propriété id sur l'objet.

0 votes

J'ai trouvé cela très utile, mais j'ai eu une erreur qui m'a obligé à ajouter la colonne de jonction. En regardant également la réponse postée par @Mark Byers, la colonne de jonction a la valeur Post_ID dans le deuxième alias meta => meta.Post_ID . Dans l'exemple de cette illustration, le g.id partie de la déclaration de sélection originale JOIN gStatus g on g.id n'est pas répliqué dans l'expression finale de Lambda.

3 votes

Je n'essayais pas de poster ceci comme une référence à la réponse linq requise par l'OP, c'était plus une référence pour savoir comment déplacer le SQL vers un format Linq, donc mes entrées étaient un peu différentes de la question originale. Si j'avais créé une classe pour les valeurs gStatus, j'aurais mis une propriété id sur elle et alors oui, elle aurait été jointe avec g => g.id J'ai utilisé une liste de valeurs pour essayer de garder le code aussi simple que possible.

41voto

Mark Byers Points 318575

Vos sélecteurs de clés sont incorrects. Ils devraient prendre un objet du type de la table en question et retourner la clé à utiliser dans la jointure. Je pense que vous voulez dire ceci :

var query = database.Posts.Join(database.Post_Metas,
                                post => post.ID,
                                meta => meta.Post_ID,
                                (post, meta) => new { Post = post, Meta = meta });

Vous pouvez appliquer la clause where après coup, mais pas dans le cadre du sélecteur de clés.

9voto

Visser Points 307

Posting parce que lorsque j'ai commencé LINQ + EntityFramework, j'ai regardé ces exemples pendant une journée.

Si vous utilisez EntityFramework, et que vous avez une propriété de navigation nommée Meta sur votre Post La mise en place d'un objet modèle est très simple. Si vous utilisez l'entité et que vous n'avez pas cette propriété de navigation, qu'attendez-vous ?

database
  .Posts
  .Where(post => post.ID == id)
  .Select(post => new { post, post.Meta });

Si vous faites le code en premier, vous devez configurer la propriété de cette façon :

class Post {
  [Key]
  public int ID {get; set}
  public int MetaID { get; set; }
  public virtual Meta Meta {get; set;}
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X