Entity framework et plusieurs à plusieurs requêtes inutilisables?

.net entity-framework many-to-many

Question

J'essaie EF et je filtre beaucoup en fonction de plusieurs relations. Par exemple, j'ai des personnes, des emplacements et un tableau de personnalisation pour relier les deux. J'ai aussi un rôle et une table de rôle.

EDIT: Tables:

Person (personid, name)

Personlocation (personid, locationid)

Location (locationid, description)

Personrole (personid, roleid)

Role (roleid, description)

EF me donnera des personnes, des rôles et des entités de localisation. EDIT: Puisque EF ne générera PAS les types d’entités personlocation et personrole, ils ne peuvent pas être utilisés dans la requête.

Comment créer une requête pour me donner toutes les personnes d'un emplacement donné ayant un rôle donné?

En SQL la requête serait

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'

J'ai regardé, mais je n'arrive pas à trouver une solution simple .

Réponse acceptée

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));

résultat de la requête:

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)

Utilisation de Contains ():

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")));

résultat de la requête:

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])
    ))

en utilisant 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});

Résultat de la requête:

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)

Vous voudrez peut-être un test plus rapide avec des données volumineuses.

Bonne chance.

Giuliano Lemes


Réponse populaire

Remarque:

Etant donné qu’il s’agit de EF v1, PersonLocation et PersonRole NE seront PAS générés en tant qu’entités comme le fait LINQ2SQL (La réponse ci-dessus illustre le scénario LINQ2SQL, qui ne s’applique pas à la question.)

Solution 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();  

Cela a l'air bien simple, mais cela génère un script SQL moche et ses performances sont correctes.

Solution 2:

Voici les pannes.

   // 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();

Combinez trois étapes ci-dessus en une seule:

 //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();

C'est un peu verbeux. Mais cela génère une requête SQL propre et fonctionne bien.



Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow