2012-05-08 15 views
6

Uso la relación muchos a muchos para mis tablas.LINQ relación muchos a muchos, cómo escribir una cláusula WHERE correcta?

hay una consulta:

var query = from post in context.Posts 
     from tag in post.Tags where tag.TagId == 10 
     select post; 

Ok, trabaja muy bien. Recibo publicaciones con la etiqueta especificada por id.

Tengo una colección de identificadores de etiquetas. Y quiero obtener publicaciones con todas las etiquetas en mi colección.

Trato de la siguiente manera:

var tagIds = new int[]{1, 3, 7, 23, 56}; 

var query = from post in context.Posts 
     from tag in post.Tags where tagIds.Contains(tag.TagId) 
     select post; 

No funciona. La consulta devuelve todas las publicaciones que tienen CUALQUIERA de las etiquetas especificadas.

quiero conseguir una cláusula como este, pero dynamicaly para cualquier recuento de etiquetas en la colección:

post.Tags.Whare(x => x.TagId = 1 && x.TagId = 3 && x.TagId = 7 && ...) 
+1

posible duplicado de [¿Cómo recupero artículos que están etiquetadas con toda las etiquetas suministradas en linq?] (http://stackoverflow.com/questions/3478874/how-do-i-retrieve-items-that-are-tagged-with-all-the-supplied-tags-in-linq) –

Respuesta

24

No debe proyectar las etiquetas de cada puesto en la consulta externa; más bien, necesitas usar una consulta interna que realiza la verificación del filtro externo. (En SQL, se utilizó para llamar a un correlated subquery.)

var query = 
    from post in context.Posts 
    where post.Tags.All(tag => tagIds.Contains(tag.TagId)) 
    select post; 

sintaxis alternativa:

var query = 
    context.Posts.Where(post => 
     post.Tags.All(tag => 
      tagIds.Contains(tag.TagId))); 

Editar: Corrección por Slauma’s clarification. La siguiente versión devuelve publicaciones que contienen, al menos, todas las etiquetas en la colección tagIds.

var query = 
    from post in context.Posts 
    where tagIds.All(requiredId => post.Tags.Any(tag => tag.TagId == requiredId)) 
    select post; 

sintaxis alternativo:

var query = 
    context.Posts.Where(post => 
     tagIds.All(requiredId => 
      post.Tags.Any(tag => 
       tag.TagId == requiredId))); 

Editar: Se ha corregido anteriormente por Slauma. Incluyendo también otra alternativa haciendo pleno uso de la sintaxis de la consulta a continuación:

// Project posts from context for which 
// no Ids from tagIds are not matched 
// by any tags from post 
var query = 
    from post in context.Posts 
    where 
    ( 
     // Project Ids from tagIds that are 
     // not matched by any tags from post 
     from requiredId in tagIds 
     where 
     (
      // Project tags from post that match requiredId 
      from tag in post.Tags 
      where tag.TagId == requiredId 
      select tag 
     ).Any() == false 
     select requiredId 
    ).Any() == false 
    select post; 

He usado .Any() == false para simular el operador NOT EXISTS en Transact-SQL.

+2

+1 por ser el único que coincide solo con ** etiquetas **, no solo ** ANY ** – mattytommo

+0

@mattytommo ¿Está seguro de eso? Me parece que está recibiendo todas las publicaciones. Editar: Acaba de agregar la publicación. Etiquetas. Todo el bloque que debería hacer el truco, pero Tejs lo tuvo primero. – Trisped

+0

@Trisped, no está haciendo coincidir ** TODOS ** id en la lista de etiquetas, no solo ** CUALQUIER ** – mattytommo

4

Esto es en realidad bastante fácil de hacer:

var tags = context.Posts.Where(post => post.Tags.All(tag => tagIds.Contains(tag))); 
+0

Él quiere que coincida con ** TODOS **, no solo con el 1 – mattytommo

+0

actualizado simplemente con la extensión 'Todos'. – Tejs

+0

Esto devolvería las publicaciones donde las etiquetas * all * aparecen en la lista de etiquetas. Por ejemplo: tagIds = ("C#", "Java") y una publicación que solo tiene la etiqueta "C#". Su consulta devolverá esta publicación. Pero él quiere: "obtener mensajes con ** cada etiqueta ** en mi colección". Es decir: solo las publicaciones etiquetadas con "C#" ** y ** "Java". Tal vez, no es tan fácil de hacer? :) – Slauma

0

intentarlo con Any.

var query = from post in context.Posts 
    from tag in post.Tags where tagIds.Any(t => t == tag.TagId) 
    select post; 
+0

Quiere emparejar ** TODOS **, no solo el 1 – mattytommo

4

Otra opción es que corte las dos listas si desea que la colección de etiquetas que sólo contienen el conjunto se especifica y no otros:

var query = from post in context.Posts 
    let tags = post.Tags.Select(x => x.Id).ToList() 
    where tags.Intersect(tagIds).Count() == tags.Length 
    select post; 
+1

¿No le gustaría 'tags.Intersect (tagIds) .Count() == tagIds.Length'? – Trisped

+0

¡Esto se ve bien! (con el cambio en el comentario de @ Trisped) – Slauma

+0

@Trisped sí, edité la respuesta – scottm

Cuestiones relacionadas