Join Műveletek a LINQ-ban
Két adatforrás összekapcsolása az egyik adatforrás objektumainak társítása olyan objektumokkal, amelyek közös attribútumot használnak egy másik adatforrásban.
JoinAz ing egy fontos művelet olyan lekérdezésekben, amelyek olyan adatforrásokat céloznak meg, amelyek egymáshoz való viszonya nem követhető közvetlenül. Az objektumorientált programozásban az összekapcsolás olyan korrelációt jelenthet a nem modellezett objektumok között, mint például egy egyirányú kapcsolat visszafelé irányuló iránya. Az egyirányú kapcsolatokra példa egy Student
osztály, amelynek típusa Department
a főt jelöli, de az Department
osztálynak nincs objektumgyűjteményt tartalmazó tulajdonsága Student
. Ha rendelkezik az objektumok listájával Department
, és meg szeretné keresni az egyes részlegek összes tanulóját, egy illesztési művelettel megkeresheti őket.
A LINQ-keretrendszerben megadott illesztési módszerek a következők Join : és GroupJoin. Ezek a metódusok a kulcsok egyenlősége alapján két adatforrásnak megfelelő egyenlő illesztéseket vagy illesztéseket hajtanak végre. (Összehasonlításképpen a Transact-SQL támogatja a nem az operátortól eltérő equals
illesztő operátorokat, például az operátort less than
.) A relációs adatbázis szempontjából Join egy belső illesztés implementálása, egy olyan illesztéstípus, amelyben csak azokat az objektumokat adja vissza a rendszer, amelyek megegyeznek a többi adatkészletben. A GroupJoin metódusnak nincs közvetlen megfelelője a relációs adatbázis szempontjából, de belső illesztések és bal oldali külső illesztések szuperhalmazát valósítja meg. A bal oldali külső illesztés olyan illesztés, amely az első (bal oldali) adatforrás minden elemét visszaadja, még akkor is, ha nincsenek korrelált elemei a másik adatforrásban.
Az alábbi ábrán két halmaz és a belső illesztésben vagy bal oldali külső illesztésben szereplő elemek fogalmi nézete látható.
Metódusok
Metódus neve | Leírás | C# lekérdezési kifejezés szintaxisa | További információ |
---|---|---|---|
Join | Joins két szekvenciát a kulcsválasztó függvények alapján, és kinyeri az értékpárokat. | join … in … on … equals … |
Enumerable.Join Queryable.Join |
GroupJoin | Joins két szekvenciát a kulcsválasztó függvények alapján, és csoportosítja az eredményként kapott egyezéseket az egyes elemekhez. | join … in … on … equals … into … |
Enumerable.GroupJoin Queryable.GroupJoin |
A cikkben szereplő alábbi példák a terület közös adatforrásait használják:
public enum GradeLevel
{
FirstYear = 1,
SecondYear,
ThirdYear,
FourthYear
};
public class Student
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
public required int ID { get; init; }
public required GradeLevel Year { get; init; }
public required List<int> Scores { get; init; }
public required int DepartmentID { get; init; }
}
public class Teacher
{
public required string First { get; init; }
public required string Last { get; init; }
public required int ID { get; init; }
public required string City { get; init; }
}
public class Department
{
public required string Name { get; init; }
public int ID { get; init; }
public required int TeacherID { get; init; }
}
Mindegyiknek Student
van egy osztályszintje, egy elsődleges osztálya és egy sor pontszáma. Az A-nek Teacher
is van egy City
tulajdonsága, amely azonosítja azt a campust, ahol a tanár órákat tart. Az A-nek Department
van egy neve, és egy olyan személyre Teacher
való hivatkozás, aki az osztályvezető.
Az alábbi példa a join … in … on … equals …
záradékot használja két sorozat adott értéken alapuló összekapcsolásához:
var query = from student in students
join department in departments on student.DepartmentID equals department.ID
select new { Name = $"{student.FirstName} {student.LastName}", DepartmentName = department.Name };
foreach (var item in query)
{
Console.WriteLine($"{item.Name} - {item.DepartmentName}");
}
Az előző lekérdezés metódusszintaxissal fejezhető ki az alábbi kódban látható módon:
var query = students.Join(departments,
student => student.DepartmentID, department => department.ID,
(student, department) => new { Name = $"{student.FirstName} {student.LastName}", DepartmentName = department.Name });
foreach (var item in query)
{
Console.WriteLine($"{item.Name} - {item.DepartmentName}");
}
Az alábbi példa a join … in … on … equals … into …
záradék használatával illeszt két sorozatot adott érték alapján, és csoportosítja az eredményként kapott egyezéseket az egyes elemekhez:
IEnumerable<IEnumerable<Student>> studentGroups = from department in departments
join student in students on department.ID equals student.DepartmentID into studentGroup
select studentGroup;
foreach (IEnumerable<Student> studentGroup in studentGroups)
{
Console.WriteLine("Group");
foreach (Student student in studentGroup)
{
Console.WriteLine($" - {student.FirstName}, {student.LastName}");
}
}
Az előző lekérdezés metódusszintaxissal fejezhető ki az alábbi példában látható módon:
// Join department and student based on DepartmentId and grouping result
IEnumerable<IEnumerable<Student>> studentGroups = departments.GroupJoin(students,
department => department.ID, student => student.DepartmentID,
(department, studentGroup) => studentGroup);
foreach (IEnumerable<Student> studentGroup in studentGroups)
{
Console.WriteLine("Group");
foreach (Student student in studentGroup)
{
Console.WriteLine($" - {student.FirstName}, {student.LastName}");
}
}
Belső illesztések végrehajtása
A relációs adatbázis szempontjából a belső illesztés olyan eredményhalmazt hoz létre, amelyben az első gyűjtemény minden eleme egyszer megjelenik a második gyűjtemény minden egyező eleméhez. Ha az első gyűjtemény egyik eleme nem rendelkezik egyező elemekkel, az nem jelenik meg az eredményhalmazban. A Join C#-ban a join
záradék által meghívott metódus belső illesztés implementál. Az alábbi példák bemutatják, hogyan hajthat végre négy változatot egy belső illesztésben:
- Egyszerű belső illesztés, amely két adatforrás elemeit egy egyszerű kulcs alapján korrelálja.
- Egy belső illesztés, amely két adatforrás elemeit egy összetett kulcs alapján korrelálja. Az összetett kulcs, amely egynél több értékből álló kulcs, lehetővé teszi az elemek egynél több tulajdonságon alapuló korrelációját.
- Több illesztés , amelyben az egymást követő illesztési műveletek egymáshoz vannak fűzve.
- Egy belső illesztés, amelyet csoportillesztés használatával valósítunk meg.
Egykulcsos illesztés
Az alábbi példa olyan objektumokkal egyezik Teacher
meg, amelyek TeacherId
megegyeznek a Teacher
következővelDeparment
. A select
C# záradéka határozza meg az eredményként kapott objektumok megjelenését. Az alábbi példában az eredményül kapott objektumok névtelen típusok, amelyek a részleg nevét és a részleget vezető tanár nevét tartalmazzák.
var query = from department in departments
join teacher in teachers on department.TeacherID equals teacher.ID
select new
{
DepartmentName = department.Name,
TeacherName = $"{teacher.First} {teacher.Last}"
};
foreach (var departmentAndTeacher in query)
{
Console.WriteLine($"{departmentAndTeacher.DepartmentName} is managed by {departmentAndTeacher.TeacherName}");
}
Ugyanezeket az eredményeket a metódus szintaxisával Join érheti el:
var query = teachers
.Join(departments, teacher => teacher.ID, department => department.TeacherID,
(teacher, department) =>
new { DepartmentName = department.Name, TeacherName = $"{teacher.First} {teacher.Last}" });
foreach (var departmentAndTeacher in query)
{
Console.WriteLine($"{departmentAndTeacher.DepartmentName} is managed by {departmentAndTeacher.TeacherName}");
}
Azok a tanárok, akik nem osztályvezetők, nem jelennek meg a végeredményben.
Összetett kulcs illesztése
Az elemek egyetlen tulajdonságon alapuló korrelációja helyett összetett kulccsal összehasonlíthatja az elemeket több tulajdonság alapján. Adja meg az egyes gyűjtemények kulcsválasztó függvényét egy névtelen típus visszaadásához, amely az összehasonlítandó tulajdonságokból áll. Ha megjelöli a tulajdonságokat, mindegyik kulcs névtelen típusában ugyanazzal a címkével kell rendelkeznie. A tulajdonságoknak ugyanabban a sorrendben is meg kell jelenniük.
Az alábbi példa egy objektumlistát Teacher
és egy objektumlistát Student
használ annak meghatározására, hogy mely tanárok is diákok. Mindkét típus rendelkezik olyan tulajdonságokkal, amelyek az egyes személyek vezeték- és családnevét jelölik. Az illesztőkulcsokat az egyes listaelemekből létrehozó függvények névtelen típust adnak vissza, amely a tulajdonságokból áll. Az illesztési művelet összehasonlítja ezeket az összetett kulcsokat az egyenlőség szempontjából, és minden listából visszaadja az objektumpárokat, ahol az utónév és a családnév is egyezik.
// Join the two data sources based on a composite key consisting of first and last name,
// to determine which employees are also students.
IEnumerable<string> query =
from teacher in teachers
join student in students on new
{
FirstName = teacher.First,
LastName = teacher.Last
} equals new
{
student.FirstName,
student.LastName
}
select teacher.First + " " + teacher.Last;
string result = "The following people are both teachers and students:\r\n";
foreach (string name in query)
{
result += $"{name}\r\n";
}
Console.Write(result);
A metódust az Join alábbi példában látható módon használhatja:
IEnumerable<string> query = teachers
.Join(students,
teacher => new { FirstName = teacher.First, LastName = teacher.Last },
student => new { student.FirstName, student.LastName },
(teacher, student) => $"{teacher.First} {teacher.Last}"
);
Console.WriteLine("The following people are both teachers and students:");
foreach (string name in query)
{
Console.WriteLine(name);
}
Több illesztés
Tetszőleges számú illesztési művelet hozzáfűzhető egymáshoz több illesztés végrehajtásához. A C# minden join
záradéka egy adott adatforrást korrelál az előző illesztés eredményeivel.
Az első join
záradék egy objektum objektumának megfelelő Department
objektum DepartmentID
alapján Student
egyezik a tanulókkal és a részlegekévalID
. Az objektumot és Department
objektumot Student
tartalmazó névtelen típusok sorozatát adja vissza.
A második join
záradék korrelálja az első illesztés által visszaadott névtelen típusokat az adott tanár azonosítójának megfelelő objektumokkal Teacher
. Névtelen típusokat ad vissza, amelyek tartalmazzák a tanuló nevét, a részleg nevét és a részlegvezető nevét. Mivel ez a művelet egy belső illesztés, a rendszer csak azokat az objektumokat adja vissza, amelyek az első adatforrásból származnak, és megegyeznek a második adatforrásban.
// The first join matches Department.ID and Student.DepartmentID from the list of students and
// departments, based on a common ID. The second join matches teachers who lead departments
// with the students studying in that department.
var query = from student in students
join department in departments on student.DepartmentID equals department.ID
join teacher in teachers on department.TeacherID equals teacher.ID
select new {
StudentName = $"{student.FirstName} {student.LastName}",
DepartmentName = department.Name,
TeacherName = $"{teacher.First} {teacher.Last}"
};
foreach (var obj in query)
{
Console.WriteLine($"""The student "{obj.StudentName}" studies in the department run by "{obj.TeacherName}".""");
}
A több Join metódust használó egyenértékű módszer ugyanazt a megközelítést használja a névtelen típussal:
var query = students
.Join(departments, student => student.DepartmentID, department => department.ID,
(student, department) => new { student, department })
.Join(teachers, commonDepartment => commonDepartment.department.TeacherID, teacher => teacher.ID,
(commonDepartment, teacher) => new
{
StudentName = $"{commonDepartment.student.FirstName} {commonDepartment.student.LastName}",
DepartmentName = commonDepartment.department.Name,
TeacherName = $"{teacher.First} {teacher.Last}"
});
foreach (var obj in query)
{
Console.WriteLine($"""The student "{obj.StudentName}" studies in the department run by "{obj.TeacherName}".""");
}
Belső illesztés csoportosított illesztés használatával
Az alábbi példa bemutatja, hogyan valósíthat meg belső illesztéseket csoportillesztés használatával. Az objektumok listája Department
a tulajdonságnak megfelelő Student.DepartmentID
objektumok listájához Student
Department.ID
van csoportosítva. A csoportillesztés köztes csoportok gyűjteményét hozza létre, ahol minden csoport egy objektumból és egy Department
egyező Student
objektumok sorozatából áll. A második from
záradék ezt a sorozatot egy hosszabb sorozatba egyesíti (vagy simítja). A select
záradék a végső sorrend elemeinek típusát határozza meg. Ez a típus egy névtelen típus, amely a tanuló nevéből és az egyező részleg nevéből áll.
var query1 =
from department in departments
join student in students on department.ID equals student.DepartmentID into gj
from subStudent in gj
select new
{
DepartmentName = department.Name,
StudentName = $"{subStudent.FirstName} {subStudent.LastName}"
};
Console.WriteLine("Inner join using GroupJoin():");
foreach (var v in query1)
{
Console.WriteLine($"{v.DepartmentName} - {v.StudentName}");
}
Ugyanezek az eredmények a következő módszerrel érhetők el GroupJoin :
var queryMethod1 = departments
.GroupJoin(students, department => department.ID, student => student.DepartmentID,
(department, gj) => new { department, gj })
.SelectMany(departmentAndStudent => departmentAndStudent.gj,
(departmentAndStudent, subStudent) => new
{
DepartmentName = departmentAndStudent.department.Name,
StudentName = $"{subStudent.FirstName} {subStudent.LastName}"
});
Console.WriteLine("Inner join using GroupJoin():");
foreach (var v in queryMethod1)
{
Console.WriteLine($"{v.DepartmentName} - {v.StudentName}");
}
Az eredmény megegyezik azzal az eredményhalmazsal, amelyet a join
záradék nélküli into
záradék használatával kapott egy belső illesztés végrehajtásához. Az alábbi kód ezt az egyenértékű lekérdezést mutatja be:
var query2 = from department in departments
join student in students on department.ID equals student.DepartmentID
select new
{
DepartmentName = department.Name,
StudentName = $"{student.FirstName} {student.LastName}"
};
Console.WriteLine("The equivalent operation using Join():");
foreach (var v in query2)
{
Console.WriteLine($"{v.DepartmentName} - {v.StudentName}");
}
A láncolás elkerülése érdekében az itt bemutatott egyetlen Join módszer használható:
var queryMethod2 = departments.Join(students, departments => departments.ID, student => student.DepartmentID,
(department, student) => new
{
DepartmentName = department.Name,
StudentName = $"{student.FirstName} {student.LastName}"
});
Console.WriteLine("The equivalent operation using Join():");
foreach (var v in queryMethod2)
{
Console.WriteLine($"{v.DepartmentName} - {v.StudentName}");
}
Csoportosított illesztések végrehajtása
A csoportillesztés hierarchikus adatstruktúrák létrehozásához hasznos. Párosítja az első gyűjtemény egyes elemeit a második gyűjtemény korrelált elemeivel.
Feljegyzés
Az első gyűjtemény minden eleme megjelenik egy csoportillesztés eredményhalmazában, függetlenül attól, hogy a korrelált elemek megtalálhatók-e a második gyűjteményben. Abban az esetben, ha nem találhatók korrelált elemek, az adott elemhez tartozó korrelált elemek sorrendje üres. Az eredményválasztó ezért hozzáfér az első gyűjtemény minden eleméhez. Ez eltér a nem csoporthoz tartozó illesztés eredményválasztójától, amely nem fér hozzá az első gyűjtemény azon elemeihez, amelyek nem egyeznek a második gyűjteményben.
Figyelmeztetés
Enumerable.GroupJoin nincs közvetlen egyenértékű a hagyományos relációs adatbázis szempontjából. Ez a módszer azonban a belső illesztések és a bal oldali külső illesztések szuperhalmazát implementálja. Mindkét művelet csoportosított illesztésként írható. További információ: Entity Framework Core, GroupJoin.
A cikk első példája bemutatja, hogyan végezhet csoportillesztéseket. A második példa bemutatja, hogyan hozhat létre XML-elemeket csoportillesztés használatával.
Csoporthoz való csatlakozás
Az alábbi példa a tulajdonságnak Department
Student
Department.ID
megfelelő Student.DepartmentID
típusú objektumok csoportillesztését hajtja végre. A nem csoportos illesztéssel ellentétben, amely minden egyes egyezéshez létrehoz egy-egy elemet, a csoportillesztés csak egy eredményt adó objektumot hoz létre az első gyűjtemény minden eleméhez, amely ebben a példában egy Department
objektum. A második gyűjtemény megfelelő elemei, amelyek ebben a példában Student
objektumok, gyűjteménybe vannak csoportosítva. Végül az eredményválasztó függvény létrehoz egy névtelen típust az egyes egyezésekhez, amelyekből és objektumok gyűjteményéből Department.Name
Student
áll.
var query = from department in departments
join student in students on department.ID equals student.DepartmentID into studentGroup
select new
{
DepartmentName = department.Name,
Students = studentGroup
};
foreach (var v in query)
{
// Output the department's name.
Console.WriteLine($"{v.DepartmentName}:");
// Output each of the students in that department.
foreach (Student? student in v.Students)
{
Console.WriteLine($" {student.FirstName} {student.LastName}");
}
}
A fenti példában a változó azt a lekérdezést tartalmazza, amely létrehoz egy listát, query
amelyben minden elem egy névtelen típus, amely tartalmazza a részleg nevét és a részlegen tanuló diákok gyűjteményét.
A metódusszintaxissal egyenértékű lekérdezés a következő kódban jelenik meg:
var query = departments.GroupJoin(students, department => department.ID, student => student.DepartmentID,
(department, Students) => new { DepartmentName = department.Name, Students });
foreach (var v in query)
{
// Output the department's name.
Console.WriteLine($"{v.DepartmentName}:");
// Output each of the students in that department.
foreach (Student? student in v.Students)
{
Console.WriteLine($" {student.FirstName} {student.LastName}");
}
}
Csoportillesztés XML létrehozásához
A csoportillesztések ideálisak az XML létrehozásához a LINQ és az XML használatával. Az alábbi példa az előző példához hasonló, azzal a kivételrel, hogy névtelen típusok létrehozása helyett az eredményválasztó függvény xml-elemeket hoz létre, amelyek az összekapcsolt objektumokat jelölik.
XElement departmentsAndStudents = new("DepartmentEnrollment",
from department in departments
join student in students on department.ID equals student.DepartmentID into studentGroup
select new XElement("Department",
new XAttribute("Name", department.Name),
from student in studentGroup
select new XElement("Student",
new XAttribute("FirstName", student.FirstName),
new XAttribute("LastName", student.LastName)
)
)
);
Console.WriteLine(departmentsAndStudents);
A metódusszintaxissal egyenértékű lekérdezés a következő kódban jelenik meg:
XElement departmentsAndStudents = new("DepartmentEnrollment",
departments.GroupJoin(students, department => department.ID, student => student.DepartmentID,
(department, Students) => new XElement("Department",
new XAttribute("Name", department.Name),
from student in Students
select new XElement("Student",
new XAttribute("FirstName", student.FirstName),
new XAttribute("LastName", student.LastName)
)
)
)
);
Console.WriteLine(departmentsAndStudents);
Bal oldali külső illesztések végrehajtása
A bal oldali külső illesztés olyan illesztés, amelyben az első gyűjtemény minden eleme vissza lesz adva, függetlenül attól, hogy a második gyűjteményben vannak-e korrelált elemei. A LINQ használatával bal oldali külső illesztést hajthat végre, ha meghívja a DefaultIfEmpty metódust a csoportillesztés eredményein.
Az alábbi példa bemutatja, hogyan használható a módszer a DefaultIfEmpty csoportillesztés eredményein a bal oldali külső illesztés végrehajtásához.
Két gyűjtemény bal oldali külső illesztésének első lépése egy belső illesztés csoportillesztés használatával történő végrehajtása. (Lásd:Belső illesztések végrehajtása a folyamat magyarázatához.) Ebben a példában az objektumok listája Department
egy olyan objektumazonosító alapján Department
csatlakozik az objektumok listájáhozStudent
, amely megfelel a tanulóénakDepartmentID
.
A második lépés az, hogy az első (bal oldali) gyűjtemény minden elemét belefoglalja az eredményhalmazba, még akkor is, ha az elemnek nincs egyezése a megfelelő gyűjteményben. Ez úgy érhető el, hogy meghívja DefaultIfEmpty a csoportillesztés egyező elemeinek mindegyik sorozatát. Ebben a példában DefaultIfEmpty a program meghívja az egyező Student
objektumok mindegyik sorozatát. A metódus egy olyan gyűjteményt ad vissza, amely egyetlen alapértelmezett értéket tartalmaz, ha az egyező Student
objektumok sorozata üres bármely Department
objektum esetében, biztosítva, hogy minden Department
objektum szerepeljen az eredménygyűjteményben.
Feljegyzés
A referenciatípus null
alapértelmezett értéke tehát a null értékű hivatkozás, mielőtt hozzáfér az egyes Student
gyűjtemények egyes elemeihez.
var query =
from student in students
join department in departments on student.DepartmentID equals department.ID into gj
from subgroup in gj.DefaultIfEmpty()
select new
{
student.FirstName,
student.LastName,
Department = subgroup?.Name ?? string.Empty
};
foreach (var v in query)
{
Console.WriteLine($"{v.FirstName:-15} {v.LastName:-15}: {v.Department}");
}
A metódusszintaxissal egyenértékű lekérdezés a következő kódban jelenik meg:
var query = students.GroupJoin(departments, student => student.DepartmentID, department => department.ID,
(student, department) => new { student, subgroup = department.DefaultIfEmpty() })
.Select(gj => new
{
gj.student.FirstName,
gj.student.LastName,
Department = gj.subgroup?.FirstOrDefault()?.Name ?? string.Empty
});
foreach (var v in query)
{
Console.WriteLine($"{v.FirstName:-15} {v.LastName:-15}: {v.Department}");
}
Lásd még
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: