2012-01-03 10 views
6

estoy tratando de consultar Posts basado en una lista de Tags:LINQ varios a varios intersección

public class Post 
{ 
    public int? Id {get;set;} 
    public string Name {get;set;} 
    public virtual ICollection<Tag> Tags {get;set;} 
} 
public class Tag 
{ 
    public int? Id {get;set;} 
    public string Name {get;set;} 
    public vritual ICollection<Post> Posts {get;set;} 
} 

Ahora quiero devolver mensajes basados ​​en una lista de etiquetas: IList<Tag> searchTags = ParseTagsFromSearchString("tag1,tag2,tag3"); // this function checks the tags in the database, so all the primary keys are available in the list

Cuando una publicación contiene una o más etiquetas que también existe en searchTags, debe incluirse en el resultado. He intentado el siguiente:

var q = from s in Context.Registrations 
        where s.Tags.Intersect(tagList) 
        select s; 

error: Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<Models.Tag>' to 'bool'

var q = from s in Context.Registrations 
        where s.Tags.Any(t => tagList.Any(t2 => t.Id.Value == t2.Id.Value)) 
        select s; 

Tiempo de ejecución de error: NotSupportedException: Unable to create a constant value of type 'Models.Tag'. Only primitive types ('such as Int32, String, and Guid') are supported in this context. ¿Alguna idea?

- update Jan. 4: Las respuestas apuntan a la solución correcta, pero en mi código todavía tengo la excepción NotSupportedException. ¿Es posible que el entero nullable cause esto ya que no es un tipo primitivo?

Respuesta

1

Hoy me encontré con este problema de nuevo, y decidieron resolverlo. El problema de mi pregunta es que el IList<Tag> searchTags = ParseTagsFromSearchString("tag1,tag2,tag3"); produce una lista, que causará una excepción cuando se use en una expresión de intersección de otra consulta en el marco de la entidad. Lo que hay que hacer es lo siguiente:

var q = ParseTagsFromSearchString("tag1,tag2,tag3"); // this function will now build a query instead of a list 
IList<Post> posts = (from s in Context.Posts where s.Tags.Intersect(q.AsEnumerable()).Any() select s).ToList(); 
0

Quizás esto podría funcionar?

var q = from s in Context.Registrations 
        where s.Tags.Intersect(tagList).Count() != 0 
        select s; 

o tal vez

var q = from s in Context.Registrations 
        where s.Tags.Any(t => tagList.Contains(t)) 
        select s; 

No he probado ninguno de esto, sin embargo, así que no hay garantías :)

+0

He tratado de la segunda solución en el código LINQPad del puesto de Mark Lindel y funciona.Desafortunadamente, en mi código recibí la misma UnsupportedException. – Marthijn

1

intenta convertir su taglist a una lista de identificadores de número entero. Úselo en su consulta para corregir la excepción NotSupportedException.

var tagIds = tagList.Select(x=>x.Id); 

A continuación, utilice tagIds en su consulta ...

var q = from s in Context.Registrations 
        where s.Tags.Any(t => tagIds.Any(t2 => t.Id.Value == t2.Id)) 
        select s; 

No estoy seguro de si la anidación Cualquier declaración como que funcione de verdad. Solo explicando por qué estabas recibiendo la excepción.

3

Ya casi ha terminado, apenas cambia el Intersect(taglist)-Intersect(taglist).Any()

Aquí está un ejemplo de trabajo (que va a lo largo de sus definiciones para Post y Tag):

Tag tag1 = new Tag() { Id = 1, Name = "tag1" }; 
Tag tag2 = new Tag() { Id = 2, Name = "tag2" }; 
Tag tag3 = new Tag() { Id = 3, Name = "tag3" }; 

List<Post> posts = new List<Post>() { 
    new Post() { Id = 1, Name = "post1", Tags = new Tag[] {tag1} }, 
    new Post() { Id = 2, Name = "post2", Tags = new Tag[] {tag2} }, 
    new Post() { Id = 3, Name = "post3", Tags = new Tag[] {tag3} }, 
    new Post() { Id = 4, Name = "post13", Tags = new Tag[] {tag1, tag3} }, 
}; 

List<Tag> searchTags = new List<Tag>() { tag1, tag2 }; 

IEnumerable<Post> matching = posts.Where(p => p.Tags.Intersect(searchTags).Any()); 
//matching now contains post1, post2 and post13 

ahora, en código real es probable que ganaron No use las mismas instancias para las etiquetas en la lista de búsqueda y las publicaciones, por lo que tendrá que anular Equals y GetHashCode para etiqueta o proporcionar un IEqualityComparer en la llamada para intersecar

+0

Gracias por su respuesta, ahora recibí este error: 'No se puede crear un valor constante de tipo 'Models.Tag'. En este contexto solo se admiten tipos primitivos ('como Int32, String y Guid'). – Marthijn

+0

He agregado un ejemplo de código de trabajo. Espero eso ayude. No estoy seguro de lo que intenta lograr con la llamada anidada Any() en su actualización. – voidengine

+0

Implementé un IEqualityComparer, pero obtuve este error: 'LINQ to Entities no reconoce el método ...'. Como dije en mi pregunta, creo que el error de tipo primitivo es causado por 'int?'. Lamentablemente, no puedo probarlo en mi aplicación porque parte del código ya está en producción. – Marthijn

0

arrojó esto juntos en LinqPad:

void Main() 
{ 
    var tag1 = new Tag { Name = "tag1" }; 
    var tag2 = new Tag { Name = "tag2" }; 
    var tag3 = new Tag { Name = "tag3" }; 

    var posts = new List<Post> 
    { 
     new Post 
     { 
      Name = "Post1", 
      Tags = new List<Tag> { tag1, tag3 } 
     }, 
     new Post 
     { 
      Name = "Post2", 
      Tags = new List<Tag> { tag2, tag3 } 
     } 
    }; 

    var tagList = new List<Tag> { tag1 }; 

    var q = posts.Where(x => x.Tags.Intersect(tagList).Any()); 

    q.Dump(); 
} 

public class Post 
{ 
    public int? Id {get;set;} 
    public string Name {get;set;} 
    public virtual ICollection<Tag> Tags {get;set;} 
} 

public class Tag 
{ 
    public int? Id {get;set;} 
    public string Name {get;set;} 
    public virtual ICollection<Post> Posts {get;set;} 
} 
+0

Gracias, en LinqPad esto funciona de hecho (buena herramienta por cierto), pero en mi código obtengo 'No se puede crear un valor constante de tipo 'Modelos.Tag'. En este contexto solo se admiten tipos primitivos ('como Int32, String y Guid'). Muy extraño, ya que este código y mi código son más o menos lo mismo. – Marthijn

Cuestiones relacionadas