Join Operasi di LINQ
Salah satu join dari dua sumber data adalah asosiasi objek dalam satu sumber data dengan objek yang berbagi atribut umum di sumber data lain.
Penting
Sampel ini menggunakan System.Collections.Generic.IEnumerable<T> sumber data. Sumber data berdasarkan System.Linq.IQueryProvider penggunaan System.Linq.IQueryable<T> sumber data dan pohon ekspresi. Pohon ekspresi memiliki batasan pada sintaks C# yang diizinkan. Selain itu, setiap IQueryProvider
sumber data, seperti EF Core dapat memberlakukan lebih banyak batasan. Periksa dokumentasi untuk sumber data Anda.
Bergabung adalah operasi penting dalam kueri yang menargetkan sumber data yang hubungannya satu sama lain tidak dapat diikuti secara langsung. Dalam pemrograman berorientasi objek, bergabung dapat berarti korelasi antara objek yang tidak dimodelkan, seperti arah mundur dari hubungan satu arah. Contoh hubungan satu arah adalah Student
kelas yang memiliki properti jenis Department
yang mewakili utama, tetapi Department
kelas tidak memiliki properti yang merupakan kumpulan Student
objek. Jika Anda memiliki daftar Department
objek dan ingin menemukan semua siswa di setiap departemen, Anda dapat menggunakan join operasi untuk menemukannya.
Metode join yang disediakan dalam kerangka kerja LINQ adalah Join dan GroupJoin. Metode ini melakukan equijoins, atau gabungan yang cocok dengan dua sumber data berdasarkan kesetaraan kunci. (Sebagai perbandingan, Transact-SQL mendukung join operator selain equals
, misalnya less than
operator.) Dalam istilah database relasional, Join mengimplementasikan bagian dalam join, jenis join di mana hanya objek yang memiliki kecocokan di himpunan data lain yang dikembalikan. Metode GroupJoin tidak setara secara langsung dalam istilah database relasional, tetapi mengimplementasikan superset gabungan dalam dan gabungan kiri luar. Luar join kiri adalah join yang mengembalikan setiap elemen sumber data pertama (kiri), bahkan jika tidak memiliki elemen berkorelasi di sumber data lainnya.
Ilustrasi berikut menunjukkan tampilan konseptual dua set dan elemen dalam set yang disertakan dalam bagian dalam join atau kiri luar join.
Metode
Nama Metode | Deskripsi | Sintaksis Ekspresi Kueri C# | Informasi Selengkapnya |
---|---|---|---|
Join | Menggabungkan dua urutan berdasarkan fungsi pemilih utama dan mengekstrak pasangan nilai. | join … in … on … equals … |
Enumerable.Join Queryable.Join |
GroupJoin | Menggabungkan dua urutan berdasarkan fungsi pemilih utama dan mengelompokkan kecocokan yang dihasilkan untuk setiap elemen. | join … in … on … equals … into … |
Enumerable.GroupJoin Queryable.GroupJoin |
Catatan
Contoh berikut dalam artikel ini menggunakan sumber data umum untuk area ini.
Masing-masing Student
memiliki tingkat kelas, departemen utama, dan serangkaian skor. A Teacher
juga memiliki City
properti yang mengidentifikasi kampus tempat guru mengadakan kelas. A Department
memiliki nama, dan referensi untuk siapa Teacher
yang menjabat sebagai kepala departemen.
Anda dapat menemukan contoh himpunan data di repositori sumber.
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; }
}
Contoh berikut menggunakan klausul join … in … on … equals …
ke join dua urutan berdasarkan nilai tertentu:
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}");
}
Kueri sebelumnya dapat diekspresikan menggunakan sintaks metode seperti yang diperlihatkan dalam kode berikut:
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}");
}
Contoh berikut menggunakan klausul join … in … on … equals … into …
untuk join dua urutan berdasarkan nilai tertentu dan mengelompokkan kecocokan yang dihasilkan untuk setiap elemen:
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}");
}
}
Kueri sebelumnya dapat diekspresikan menggunakan sintaks metode seperti yang diperlihatkan dalam contoh berikut:
// 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}");
}
}
Lakukan gabungan dalam
Dalam istilah database relasional, bagian dalam join menghasilkan tataan hasil di mana setiap elemen koleksi pertama muncul satu kali untuk setiap elemen yang cocok di koleksi kedua. Jika elemen dalam koleksi pertama tidak memiliki elemen yang cocok, elemen tersebut tidak muncul dalam tataan hasil. Metode Join , yang disebut dengan join
klausul dalam C#, mengimplementasikan bagian dalam join. Contoh berikut menunjukkan kepada Anda cara melakukan empat variasi dalam join:
- Bagian join dalam sederhana yang menghubungkan elemen dari dua sumber data berdasarkan kunci sederhana.
- Bagian dalam join yang menghubungkan elemen dari dua sumber data berdasarkan kunci komposit . Kunci komposit, yang merupakan kunci yang terdiri dari lebih dari satu nilai, memungkinkan Anda untuk menghubungkan elemen berdasarkan lebih dari satu properti.
- Beberapa join di mana operasi berturut-turut join ditambahkan satu sama lain.
- Bagian dalam join yang diimplementasikan dengan menggunakan grup join.
Kunci tunggal join
Contoh berikut cocok Teacher
dengan objek dengan objek yang TeacherId
cocok dengan Deparment
.Teacher
Klausa select
dalam C# menentukan tampilan objek yang dihasilkan. Dalam contoh berikut, objek yang dihasilkan adalah jenis anonim yang terdiri dari nama departemen dan nama guru yang memimpin departemen.
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}");
}
Anda mencapai hasil yang sama menggunakan Join sintaks metode:
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}");
}
Guru yang bukan kepala departemen tidak muncul di hasil akhir.
Kunci komposit join
Alih-alih menghubungkan elemen hanya berdasarkan satu properti, Anda dapat menggunakan kunci komposit untuk membandingkan elemen berdasarkan beberapa properti. Tentukan fungsi pemilih kunci untuk setiap koleksi untuk mengembalikan jenis anonim yang terdiri dari properti yang ingin Anda bandingkan. Jika Anda memberi label properti, properti tersebut harus memiliki label yang sama di setiap jenis anonim kunci. Properti tersebut juga harus muncul dalam urutan yang sama.
Contoh berikut menggunakan daftar Teacher
objek dan daftar Student
objek untuk menentukan guru mana yang juga siswa. Kedua jenis ini memiliki properti yang mewakili nama depan dan keluarga setiap orang. Fungsi yang membuat join kunci dari setiap elemen daftar mengembalikan jenis anonim yang terdiri dari properti. Operasi membandingkan join kunci komposit ini untuk kesetaraan dan mengembalikan pasangan objek dari setiap daftar di mana nama depan dan nama keluarga cocok.
// 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);
Anda dapat menggunakan metode , seperti yang Join ditunjukkan dalam contoh berikut:
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);
}
Beberapa join
join Sejumlah operasi dapat ditambahkan satu sama lain untuk melakukan beberapa join. Setiap join
klausa dalam C# menghubungkan sumber data tertentu dengan hasil dari sebelumnya join.
Klausa pertama join
cocok dengan siswa dan departemen berdasarkan objek DepartmentID
yang Student
cocok dengan Department
objek ID
. Ini mengembalikan urutan jenis anonim yang berisi Student
objek dan Department
objek.
Klausa kedua join
berkorelasi dengan jenis anonim yang dikembalikan oleh yang pertama join dengan objek berdasarkan ID guru yang cocok dengan Teacher
ID kepala departemen. Ini mengembalikan urutan jenis anonim yang berisi nama siswa, nama departemen, dan nama pemimpin departemen. Karena operasi ini adalah bagian dalam join, hanya objek dari sumber data pertama yang memiliki kecocokan di sumber data kedua yang dikembalikan.
// 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}".""");
}
Yang setara menggunakan beberapa Join metode menggunakan pendekatan yang sama dengan jenis anonim:
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}".""");
}
join Dalam dengan menggunakan dikelompokkanjoin
Contoh berikut menunjukkan kepada Anda cara mengimplementasikan bagian dalam join dengan menggunakan grup join. Daftar Department
objek digabungkan dengan grup ke daftar Student
objek berdasarkan properti yang Department.ID
Student.DepartmentID
cocok. Grup join ini membuat kumpulan grup perantara, di mana setiap grup terdiri dari Department
objek dan urutan objek yang Student
cocok. Klausa kedua from
menggabungkan (atau meratakan) urutan urutan ini menjadi satu urutan yang lebih lama. Klausa select
menentukan jenis elemen dalam urutan akhir. Jenis itu adalah jenis anonim yang terdiri dari nama siswa dan nama departemen yang cocok.
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}");
}
Hasil yang sama dapat dicapai menggunakan GroupJoin metode, sebagai berikut:
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}");
}
Hasilnya setara dengan tataan hasil yang diperoleh dengan menggunakan join
klausa tanpa into
klausul untuk melakukan bagian dalam join. Kode berikut menunjukkan kueri yang setara ini:
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}");
}
Untuk menghindari penautan, metode tunggal Join dapat digunakan seperti yang disajikan di sini:
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}");
}
Melakukan gabungan yang dikelompokkan
Grup join ini berguna untuk menghasilkan struktur data hierarkis. Ini memasangkan setiap elemen dari kumpulan pertama dengan sekumpulan elemen berkorelasi dari kumpulan kedua.
Catatan
Setiap elemen koleksi pertama muncul dalam kumpulan hasil grup join terlepas dari apakah elemen berkorelasi ditemukan di koleksi kedua. Dalam kasus di mana tidak ada elemen berkorelasi yang ditemukan, urutan elemen berkorelasi untuk elemen tersebut kosong. Oleh karena itu, pemilih hasil memiliki akses ke setiap elemen dari kumpulan pertama. Ini berbeda dari pemilih hasil dalam non-grup join, yang tidak dapat mengakses elemen dari koleksi pertama yang tidak memiliki kecocokan di koleksi kedua.
Peringatan
Enumerable.GroupJoin tidak memiliki padanan langsung dalam istilah database hubungan tradisional. Namun, metode ini menerapkan superset gabungan dalam dan gabungan kiri luar. Kedua operasi ini dapat ditulis dalam hal yang dikelompokkan join. Untuk informasi selengkapnya, lihat Entity Framework Core, GroupJoin.
Contoh pertama dalam artikel ini menunjukkan kepada Anda cara melakukan grup join. Contoh kedua menunjukkan kepada Anda cara menggunakan grup join untuk membuat elemen XML.
Kelompok join
Contoh berikut melakukan sekelompok join objek jenis Department
dan Student
berdasarkan pencocokan Department.ID
Student.DepartmentID
properti. Tidak seperti non-grup join, yang menghasilkan sepasang elemen untuk setiap kecocokan, grup join hanya menghasilkan satu objek yang dihasilkan untuk setiap elemen koleksi pertama, yang dalam contoh ini adalah Department
objek. Elemen terkait dari kumpulan kedua, yang dalam contoh ini adalah objek Student
, dikelompokkan ke dalam kumpulan. Terakhir, fungsi pemilih hasil membuat jenis anonim untuk setiap kecocokan yang terdiri dari Department.Name
dan kumpulan objek Student
.
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}");
}
}
Dalam contoh di atas, query
variabel berisi kueri yang membuat daftar di mana setiap elemen adalah jenis anonim yang berisi nama departemen dan kumpulan siswa yang belajar di departemen tersebut.
Kueri yang setara menggunakan sintaks metode diperlihatkan dalam kode berikut:
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}");
}
}
Grup join untuk membuat XML
Gabungan grup ideal untuk membuat XML menggunakan LINQ ke XML. Contoh berikut ini mirip dengan contoh sebelumnya kecuali bahwa sebagai ganti dari membuat jenis anonim, fungsi pemilih hasil membuat elemen XML yang mewakili objek yang digabungkan.
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);
Kueri yang setara menggunakan sintaks metode diperlihatkan dalam kode berikut:
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);
Melakukan gabungan kiri luar
Luar join kiri adalah join tempat setiap elemen koleksi pertama dikembalikan, terlepas dari apakah ia memiliki elemen yang berkorelasi dalam koleksi kedua. Anda dapat menggunakan LINQ untuk melakukan luar join kiri dengan memanggil DefaultIfEmpty metode pada hasil grup join.
Contoh berikut menunjukkan cara menggunakan DefaultIfEmpty metode pada hasil grup join untuk melakukan luar joinkiri .
Langkah pertama dalam menghasilkan bagian luar join kiri dari dua koleksi adalah melakukan bagian dalam join dengan menggunakan grup join. (Lihat Lakukan gabungan dalam untuk penjelasan tentang proses ini.) Dalam contoh ini, daftar Department
objek digabungkan dalam ke daftar Student
objek berdasarkan Department
ID objek yang cocok dengan siswa DepartmentID
.
Langkah kedua adalah memasukkan setiap elemen dari koleksi pertama (kiri) dalam kumpulan hasil meskipun elemen tersebut tidak memiliki kecocokan dalam koleksi kanan. Ini dicapai dengan memanggil DefaultIfEmpty pada setiap urutan elemen pencocokan dari grup join. Dalam contoh ini, DefaultIfEmpty dipanggil pada setiap serangkai objek Student
yang cocok. Metode mengembalikan koleksi yang berisi nilai default tunggal jika urutan objek yang Student
cocok kosong untuk objek apa pun Department
, memastikan bahwa setiap Department
objek diwakili dalam koleksi hasil.
Catatan
Nilai default untuk jenis referensi adalah null
; oleh karena itu, contoh memeriksa referensi null sebelum mengakses setiap elemen dari setiap koleksi Student
.
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}");
}
Kueri yang setara menggunakan sintaks metode diperlihatkan dalam kode berikut:
var query = students.GroupJoin(departments, student => student.DepartmentID, department => department.ID,
(student, departmentList) => new { student, subgroup = departmentList.AsQueryable() })
.SelectMany(joinedSet => joinedSet.subgroup.DefaultIfEmpty(), (student, department) => new
{
student.student.FirstName,
student.student.LastName,
Department = department.Name
});
foreach (var v in query)
{
Console.WriteLine($"{v.FirstName:-15} {v.LastName:-15}: {v.Department}");
}