2012-07-24 16 views
7

Verwenden von Entity Framework/LINQ, ich brauche Hilfe mit den folgenden.Wie frage ich eine Junction-Tabelle

Die Datenbank enthält eine Tabelle mit Personen mit einer Identitätsspalte von PersonId. Es gibt auch eine Skills-Tabelle mit einer Identitätsspalte von SkillId. Diese beiden sind über eine dritte Tabelle PeopleSkills verbunden, die eine eigene Identitätsspalte PeopleSkillsId, eine fremde Spalte, die auf PersonId verweist, und eine fremde Spalte, die auf SkillId verweist, hat.

Die Methode, die ich versuche zu schreiben, ist ein Parameter des Typs List übergeben, der eine beliebige Anzahl von Fähigkeiten enthält, die wir suchen. Die Methode sollte eine Liste zurückgeben, die mit allen Fähigkeiten in der Eingabeparameterliste verknüpft ist. Wie erstelle ich eine Liste, die alle Personen ausschließt, die nicht über alle Fähigkeiten in der Skills-Liste verfügen?

Das Problem, das ich habe, ist, dass ich sehr wenig Erfahrung mit SQL habe. Ich habe viele andere Programmiererfahrung, aber SQL ist für mich immer etwas grob. Ich habe über Join nachgedacht, aber das wird nicht funktionieren. Wenn meine Person Fähigkeiten A & B hat und die Suchliste Elemente für B & C enthält, würde ein Join sie auf B abgleichen und die Person zurückgeben. Ich brauche diese Person ausgeschlossen werden, weil er nicht beide B & C.

Ich habe auch darüber nachgedacht, durch die Fähigkeiten Liste und einen Filter zu erstellen, aber das scheint hässlich. Dies scheint nur ein Problem zu sein, für das LINQ entwickelt wurde, indem eine Liste verwendet wird, um eine andere Liste abzufragen, und dass es eine elegante Lösung dafür geben sollte.

+1

Ich würde gerne darüber nachdenken; Ich werde innerhalb der nächsten 24 Stunden mit einer umfassenden Antwort auf Sie zurückkommen, falls jemand anderes dies noch nicht getan hat. –

Antwort

0

Das könnte funktionieren:

public List<Person> GetPeopleWithAllSkills(List<Skill> skills) 
{ 
    var skillIds = skills.Select(s => s.SkillId); 

    return context.People.Where(
     p => skillIds.All(id => p.PeopleSkills.Any(ps => ps.SkillId == id))) 
     .ToList(); 
} 

die Leute Geben Sie mir, die die Bedingung erfüllen, dass alle gegebenen Fähigkeiten existieren (Any) in der Liste der Fähigkeiten dieser Menschen. (Sie können mehr als die gegebenen Fähigkeiten haben, aber nicht weniger.)

+0

Beide Antworten zeigten mir ziemlich viel, aber das ist die elegant einfache Art, die ich mir vorgestellt habe. Brillant. –

1

Ich verwendete LinqPad, die Linq-to-SQL anstelle von Linq zu Entities verwendet, aber die Konzepte sollten die gleichen sein.

Zuerst die Daten, die ich zum Testen verwendet habe.

create table People (PersonID int, Name varchar(100)) 
create table Skills (SkillID int, Skill varchar(100)) 
create table PeopleSkills (PeopleSkillsID int, PersonID int, SkillID int) 

insert People values (1,'Bert'),(2,'Bob'),(3,'Phil'),(4,'Janet') 
insert Skills values (1,'C#'),(2,'Linq'),(3,'SQL') 
insert PeopleSkills values (1,1,1),(2,1,2),(3,1,3),(4,2,1),(5,2,3),(6,3,2),(7,3,3),(8,4,1),(9,4,2),(10,4,3) 

Und die Lösung.

//I don't know how you are specifying your list of skills; for explanatory purposes 
//I just generate a list. So, our test skill set is C#, Linq, and SQL. 
//int? is used because of LinqToSQL complains about converting int? to int 
var skills = new List<int?>(){1,2,3}; 
//This initial query is also a small bow to LinqToSQL; Really I just wanted a plain 
//List so that the Except and Any clauses can be used in the final query. 
//LinqToSQL can apparently only use Contains; that may or may not be an issue with 
//LinqToEntities. Also, its not a bad idea to filter the people we need to look at 
//in case there are a large number anyway. 
var peopleOfInterest = PeopleSkills.Where(p => skills.Contains(p.SkillID)).ToList(); 

//Final query is relatively simple, using the !x.Except(y).Any() method to 
//determine if one list is a subset of another or not. 
var peopleWithAllSkills = 
    //Get a distinct list of people 
    from person in peopleOfInterest.Select(p=>p.PersonID).Distinct() 
    //determine what skills they have 
    let personSkills = peopleOfInterest.Where(x=>x.PersonID == person).Select(x=>x.SkillID) 
    //check to see if any of the skills we are looking for are not skills they have 
    where !skills.Except(personSkills).Any() 
    select person; 
+0

Danke. Ich wäre nie auf die Idee gekommen! Außer Irgendwelche. –