2011-12-16 18 views
6

Tengo dos tablas de base de datos Contacto (Id, Nombre, ...) y ContactOperationalPlaces (ContactId, MunicipalityId), donde un contacto se puede conectar a varios ContactOperationalPlaces.Cómo agregar una cláusula where en una unión linq (lambda)?

Lo que intento hacer es crear una consulta (ASP .NET, C#) con IQueryable, que solo selecciona todos los contactos que existen en la tabla ContactOperationalPlaces, con un MunicipalityId dado.

la consulta SQL tiene este aspecto:

select * from Contacts c 
right join ContactOperationPlaces cop on c.Id = cop.ContactId 
where cop.MunicipalityId = 301; 

Con LINQ se vería algo como esto:

//_ctx is the context 
var tmp = (from c in _ctx.Contacts 
      join cop in _ctx.ContactOperationPlaces on c.Id equals cop.ContactId 
      where cop.MunicipalityId == 301 
      select c); 

lo tanto, sé cómo hacer esto si el punto era seleccionar todos esto de una vez, desafortunadamente no lo es. Estoy construyendo una consulta basada en la entrada del usuario, por lo que no sé toda la selección a la vez.

Así que esto es lo que se ve mi código como:

IQueryable<Contacts> query = (from c in _ctx.Contacts select c); 
//Some other logic.... 
/*Gets a partial name (string nameStr), and filters the contacts 
so that only those with a match on names are selected*/ 
query = query.Where(c => c.Name.Contains(nameStr); 
//Some more logic 
//Gets the municipalityId and wants to filter on it! :(how to? 
query = query.where(c => c.ContactOperationalPlaces ...........?); 

La diferencia con los dos cuando las declaraciones es que con la primera, cada contacto tiene un solo nombre, pero con este último un contacto puede contener varios lugares operativos ...

He logrado encontrar una solución, pero esta solución me da un objeto no identificado, que contiene las dos tablas. Y no sé cómo proceder con eso.

query.Join(_ctx.ContactOperationPlaces, c => c.Id, cop => cop.ContactId, 
     (c, cop) => new {c, cop}).Where(o => o.cop.municipalityId == 301); 

El objeto devuelto por esta expresión es System.Linq.Iqueryable < {c: Contacto, CP: ContactOperationalPlace}>, y no se puede convertir a Contactos ...

Entonces, eso es la cuestión. La respuesta es probablemente bastante simple, pero no puedo encontrarlo ...

Respuesta

11

Se crea un tipo anónimo con ambos objetos antes de su cláusula where y filtrarla en el valor ContactOperationPlaces. Solo tiene que seleccionar el Contacto después de eso.

query.Join(_ctx.ContactOperationPlaces, c => c.Id, cop => cop.ContactId, 
      (c, cop) => new {c, cop}).Where(o => o.cop.municipalityId == 301) 
            .Select(o => o.c) 
            .Distinct(); 
+2

y probablemente un signo profundo() para el contacto –

+0

Gracias por la corrección. –

+0

¡Muchas gracias! Sabía que la solución sería bastante simple una vez que la encontrara. – linnkb

0

¿Puedes simplemente echarlo a var e intentar usar intellisense en él?

var myCast = query.Join(_ctx.ContactOperationPlaces, c => c.Id, cop => cop.ContactId, 
    (c, cop) => new {c, cop}).Where(o => o.cop.municipalityId == 301); 

Es sólo una idea

1

No necesita devolver objetos nuevos en la función del selector de resultados. El delegado proporciona ambas variables para que pueda elegir una u otra, o alguna otra variación (que requeriría un nuevo objeto). Prueba esto:

query.Join(_ctx.ContactOperationPlaces, c => c.Id, cop => cop.ContactId, 
    (c, cop) => c).Where(o => o.cop.municipalityId == 301); 
+0

Lo he intentado antes, y como no hago esto: (c, cop) => nuevo {c, cop}, no tendré la oportunidad de seleccionar cop en la cláusula where ... – linnkb

0

Creo que sería mucho más fácil si usted comienza a esto como 2 consultas diferentes, luego combinarlas. Supongo que la relación es Contacto (1 < -> muchos) ¿Contactperationplaces? Y al final, ¿mostrará 1 artículo por cada contactoperationplaces, no 1 artículo por contacto?

hacerlo de esta manera:

IQueryable<Contacts> query = (from c in _ctx.Contacts select c); 

... 

query = query.Where(x=> x.Name.ToLower().Contains(nameStr.ToLower()); 

... 

IQueryable<ContactOperationPlaces> query_2 = 
    (from c in _ctx.ContactOperationPlaces 
     where query.Where(x=> x.Name == c.Contact.Name).Count() > 0 
     select c); 

//Now query_2 contains all contactoperationsplaces which have a contact that was found in var query 

Por el contrario, hay una manera mucho más fácil de hacer esto, y eso es por saltarse la primera parte del todo.

IQueryable<ContactOperationPlaces> query_2 = 
    (from c in _ctx.ContactOperationPlaces 
     where c.Contact.Name.ToLower().Contains(strName.ToLower()) 
     select c); 

Si está utilizando Entity Framework, no tiene que hacer ninguna unión siempre que defina las asociaciones entre las tablas.

Ahora que lo veo, mi segunda solución es mucho más eficiente y más fácil. Pero si es necesario hacer algún otro tipo de elaboración entre medio de estos comandos, la solución se trabaja también :)

Si necesita más explicación, no dude en preguntar :)

+0

Quiero mantener el contacto que tiene una conexión con un campo de municipalidad específico en lugares de contacto, por lo que quiero un elemento por contacto. Sé que la última solución es la mejor, pero como expliqué en mi pregunta, no puedo seleccionar todo de una vez. – linnkb

+0

son estas diferentes tablas conectadas a través de una asociación de uno a muchos? Si es así, esta es tu mejor solución. Si tiene otras combinaciones en las que se basa el contacto, debe reemplazarlas haciendo algo similar a lo anterior. Como EF hace todas las uniones por usted y solo tiene que decir lo que desea, creo que unir tablas manualmente con EF es ineficiente por defecto (excepto en algunas situaciones muy, muy inusuales, tal vez). No te culpo, pero te sugiero que evites hacer uniones manuales ya que tendrás que hacer más trabajo, con menos comentarios/depuración ... – Flater

Cuestiones relacionadas