2009-02-16 8 views
16

Ich versuche EF aus und ich mache viele Filterung basierend auf vielen zu vielen Beziehungen. Zum Beispiel habe ich Personen, Standorte und eine Personenstandort-Tabelle, um die beiden zu verbinden. Ich habe auch eine Rolle und einen Personrole-Tisch.Entity-Framework und viele bis viele Abfragen unbrauchbar?

EDIT: Tables: 

Person (personid, name) 

Personlocation (personid, locationid) 

Location (locationid, description) 

Personrole (personid, roleid) 

Role (roleid, description) 

EF gibt mir Personen, Rollen und Standorteinheiten. BEARBEITEN: Da EF NICHT die Entitäten personlocation und personrole generiert, können sie nicht in der Abfrage verwendet werden.

Wie erstelle ich eine Abfrage, um mir alle Personen eines bestimmten Ortes mit einer bestimmten Rolle zu geben?

In SQL würde die Abfrage

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' 

Ich habe aussah, aber ich kann keine einfache Lösung zu finden scheinen.

+1

Wenn Sie eine gute Antwort auf diese Frage wollen, sollten Sie die Entity-Typen enthalten. Nach allem, was die Entitäten SQL/LINQ to Entities betrifft, wird nicht auf die Tabellen Bezug genommen. Ansonsten muss ich auf Ihre Eigentumsnamen und Kardinalität raten. –

Antwort

11

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

Abfrageergebnis:

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) 

Verwendung Enthält():

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

Abfrageergebnis:

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

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

Abfrage Ergebnis:

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) 

Sie können Test wich man will, ist schneller mit großen Daten.

Viel Glück.

Giuliano Lemes

+2

.Join (Personlocations - Huh? EF wird das nicht für Viele-zu-Viele erzeugen –

0

Ok, LINQ hat keine Bestimmung, soweit ich das beurteilen kann. Lambda-Ausdrücke funktionieren mit jedem beliebigen().

29

Hinweis:

Da es in EF v1 ist, werden wir NICHT haben PersonLocation und PersonRole als Entitäten erzeugt wie das, was LINQ2SQL tut (Die Antwort oben domonstrating LINQ2SQL Szenario, das nicht . gilt für die Frage)

Lösung 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(); 

Das sieht nett und geradlinig aus, aber das erzeugt hässliches SQL-Skript und seine Leistung ist in Ordnung.

Lösung 2:

Hier sind Pannen.

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

Kombinieren Sie über drei Schritte in einem:

//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 Art ausführliche ist. Dies erzeugt jedoch eine saubere SQL-Abfrage und führt zu einer guten Leistung.

+0

Tolle Antwort, funktioniert auch mit EF wo die akzeptierte Antwort hier nicht funktioniert mit EF – Paul

+0

Wirklich tolle Antwort auf ein klebriges Problem. –

Verwandte Themen