2009-02-16 13 views
16

Estoy probando EF y hago un montón de filtrado basado en muchas relaciones. Por ejemplo, tengo personas, ubicaciones y una tabla de ubicación de personas para vincular los dos. También tengo un rol y una lista de roles.Marco de entidad y muchas consultas muchas inutilizables?

EDIT: Tables: 

Person (personid, name) 

Personlocation (personid, locationid) 

Location (locationid, description) 

Personrole (personid, roleid) 

Role (roleid, description) 

EF me dará personas, roles y entidades de ubicación. EDITAR: Dado que EF va a NO generar los tipos de entidades personlocation y personrole, no se pueden usar en la consulta.

¿Cómo puedo crear una consulta para darme todas las personas de una ubicación determinada con un rol determinado?

En SQL de la consulta sería

select p.* 
from persons as p 
join personlocations as pl on p.personid=pl.personid 
join locations  as l on pl.locationid=l.locationid 
join personroles  as pr on p.personid=pr.personid 
join roles   as r on pr.roleid=r.roleid 
where r.description='Student' and l.description='Amsterdam' 

He mirado, pero me parece que no puede encontrar una solución simple.

+1

Si quieres una buena respuesta a esta pregunta, que debe incluir los tipos de entidad. Después de todo, eso, no las tablas, es a lo que se referirá la Entidad SQL/LINQ a las Entidades. De lo contrario, tengo que adivinar los nombres de sus propiedades y la cardinalidad. –

Respuesta

11

En Lambda:

var persons = Persons.Where(p=>(p.PersonLocations.Select(ps=>ps.Location) 
    .Where(l=>l.Description == "Amsterdam").Count() > 0) 
    && (p.PersonRoles.Select(pr=>pr.Role) 
    .Where(r=>r.Description == "Student").Count() > 0)); 

resultado de la consulta:

SELECT [t0].[personId] AS [PersonId], [t0].[description] AS [Description] 
FROM [Persons] AS [t0] 
WHERE (((
    SELECT COUNT(*) 
    FROM [personlocations] AS [t1] 
    INNER JOIN [Locations] AS [t2] ON [t2].[locationid] = [t1].[locationid] 
    WHERE ([t2].[description] = @p0) AND ([t1].[personid] = [t0].[personId]) 
    )) > @p1) AND (((
    SELECT COUNT(*) 
    FROM [PersonRoles] AS [t3] 
    INNER JOIN [Roles] AS [t4] ON [t4].[roleid] = [t3].[roleid] 
    WHERE ([t4].[description] = @p2) AND ([t3].[personid] = [t0].[personId]) 
    )) > @p3) 

usando contiene(): resultado

var persons = Persons 
      .Where(p=>(p.Personlocations.Select(ps=>ps.Location) 
      .Select(l=>l.Description).Contains("Amsterdam")) && 
      (p.PersonRoles.Select(pr=>pr.Role) 
      .Select(r=>r.Description).Contains("Student"))); 

consulta:

SELECT [t0].[personId] AS [PersonId], [t0].[description] AS [Description] 
FROM [Persons] AS [t0] 
WHERE (EXISTS(
    SELECT NULL AS [EMPTY] 
    FROM [personlocations] AS [t1] 
    INNER JOIN [Locations] AS [t2] ON [t2].[locationid] = [t1].[locationid] 
    WHERE ([t2].[description] = @p0) AND ([t1].[personid] = [t0].[personId]) 
    )) AND (EXISTS(
    SELECT NULL AS [EMPTY] 
    FROM [PersonRoles] AS [t3] 
    INNER JOIN [Roles] AS [t4] ON [t4].[roleid] = [t3].[roleid] 
    WHERE ([t4].[description] = @p1) AND ([t3].[personid] = [t0].[personId]) 
    )) 

usando join():

var persons = Persons 
     .Join(Personlocations, p=>p.PersonId, ps=>ps.Personid, 
(p,ps) => new {p,ps}) 
.Where(a => a.ps.Location.Description =="Amsterdam") 
     .Join(PersonRoles, 
pr=> pr.p.PersonId, r=>r.Personid,(pr,r) => new {pr.p,r}) 
.Where(a=>a.r.Role.Description=="Student") 
     .Select(p=> new {p.p}); 

Resultado de la consulta:

SELECT [t0].[personId] AS [PersonId], [t0].[description] AS [Description] 
FROM [Persons] AS [t0] 
INNER JOIN [personlocations] AS [t1] ON [t0].[personId] = [t1].[personid] 
INNER JOIN [Locations] AS [t2] ON [t2].[locationid] = [t1].[locationid] 
INNER JOIN [PersonRoles] AS [t3] ON [t0].[personId] = [t3].[personid] 
INNER JOIN [Roles] AS [t4] ON [t4].[roleid] = [t3].[roleid] 
WHERE ([t4].[description] = @p0) AND ([t2].[description] = @p1) 

es posible que desee wich prueba es más rápido con datos de gran tamaño.

Buena suerte.

Giuliano Lemes

+2

.Join (Ubicaciones de persona - ¿Huh? EF no generará esto para muchos a muchos –

0

Ok, LINQ tiene ninguna disposición en la medida de lo que puedo decir. Las expresiones Lambda funcionan con any().

29

Nota:

Ya que está en EF v1, vamos a NO tener PersonLocation y PersonRole generado como entidades como lo hace LINQ2SQL (La respuesta anterior domonstrating escenario LINQ2SQL, que no lo hace . aplicará a la pregunta)

Solución 1:

Persons.Include("Role").Include("Location") // Include to load Role and Location 
     .Where(p => p.Role.Any(r => r.description == "Student") 
     && p.Location.Any(l => l.description == "Amsterdam")).ToList(); 

Esto se ve bien y sencillo, pero esto genera un feo script SQL y su rendimiento es correcto.

Solución 2:

Aquí hay averías.

// Find out all persons in the role 
    // Return IQuerable<Person> 
    var students = Roles.Where(r => r.description == "Student") 
         .SelectMany(r => r.Person); 

    // Find out all persons in the location 
    // Return IQuerable<Person> 
    var personsInAmsterdam = Locations.Where(l=> l.description == "Amsterdam") 
            .SelectMany(l=>l.Person); 

    // Find out the intersection that gives us students in Admsterdam. 
    // Return List<Person> 
    var AdmsterdamStudents = students.Intersect(personsInAmsterdam).ToList(); 

combinar tres pasos anteriores en una sola:

//Return List<Person> 
var AdmsterdamStudents = Roles.Where(r => r.description == "Student") 
           .SelectMany(r => r.Person) 
           .Intersect 
           ( 
           Locations 
           .Where(l=> l.description == "Amsterdam") 
           .SelectMany(l=>l.Person) 
           ).ToList(); 

Es una especie de prolijo. Pero esto genera una consulta SQL limpia y funciona bien.

+0

Gran respuesta, funciona también con EF donde la respuesta aceptada aquí no funciona con EF – Paul

+0

Respuesta realmente increíble a un problema pegajoso. –

Cuestiones relacionadas