Bagikan melalui


Join operasi di LINQ

Gabungan mengaitkan 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 Anda ikuti 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 objek Department dan ingin menemukan semua siswa di setiap departemen, Anda dapat menggunakan operasi gabungan untuk menemukannya.

Kerangka kerja LINQ menyediakan metode gabungan: Join dan GroupJoin. Metode ini melakukan equijoins, atau gabungan yang cocok dengan dua sumber data berdasarkan kesetaraan kunci. Sebagai perbandingan, Transact-SQL mendukung operator gabungan selain equals, seperti less than operator. Dalam istilah database relasional, Join mengimplementasikan gabungan dalam, jenis gabungan di mana hanya objek yang memiliki kecocokan dalam himpunan data lain yang dikembalikan. Metode GroupJoin tidak setara secara langsung dalam istilah database relasional, tetapi mengimplementasikan superset gabungan dalam dan gabungan kiri luar. Gabungan luar kiri adalah gabungan yang mengembalikan setiap elemen sumber data pertama (kiri), meskipun tidak memiliki elemen yang berkorelasi di sumber data lainnya.

Ilustrasi berikut menunjukkan pandangan konseptual dari dua set dan elemen-elemen di dalam set tersebut yang termasuk dalam gabungan dalam atau gabungan luar kiri.

Dua lingkaran yang tumpang tindih memperlihatkan bagian dalam/luar.

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
LeftJoin Mengkorelasikan elemen dari dua urutan berdasarkan kunci yang cocok. N/A Enumerable.LeftJoin

Queryable.LeftJoin
RightJoin Mengkorelasikan elemen dari dua urutan berdasarkan kunci yang cocok. N/A Enumerable.RightJoin

Queryable.RightJoin

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

Catatan

Anda dapat merujuk ke sumber data umum untuk area ini di artikel Gambaran Umum Operator Kueri Standar .

Contoh berikut menggunakan klausul join … in … on … equals … untuk menggabungkan 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}");
}

Anda dapat mengekspresikan kueri sebelumnya dengan 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 menggabungkan 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}");
    }
}

Anda dapat mengekspresikan kueri sebelumnya dengan 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, inner join menghasilkan hasil set di mana setiap elemen dalam koleksi pertama muncul satu kali untuk setiap elemen yang cocok dalam koleksi kedua. Jika elemen dalam koleksi pertama tidak memiliki elemen yang cocok, elemen tersebut tidak muncul dalam tataan hasil. Metode Join, yang dipanggil oleh klausul join dalam C#, mengimplementasikan join dalam. Contoh berikut menunjukkan kepada Anda cara melakukan empat variasi join dalam.

  • Gabungan dalam sederhana yang menghubungkan elemen dari dua sumber data berdasarkan kunci sederhana.
  • Inner 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.
  • Penggabungan berganda di mana Anda menambahkan operasi gabungan secara berturut-turut.
  • Gabungan dalam yang menggunakan gabungan grup.

Gabungan kunci tunggal

Contoh berikut cocok Teacher dengan objek dengan objek yang Department cocok dengan TeacherId .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 dapat mencapai hasil yang sama dengan menggunakan sintaks metode Join.

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 dalam hasil akhir.

Gabungan kunci komposit

Alih-alih menghubungkan elemen hanya berdasarkan satu properti, gunakan 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 kunci join dari setiap elemen daftar mengembalikan tipe anonim yang terdiri atas properti. Operasi gabungan membandingkan 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);
}

Gabungan ganda

Anda dapat menambahkan sejumlah operasi gabungan untuk melakukan beberapa gabungan. Setiap klausa join dalam C# menghubungkan sumber data tertentu dengan hasil gabungan sebelumnya.

Klausa pertama join cocok dengan siswa dan departemen berdasarkan objek Student yang DepartmentID cocok dengan Department objek ID. Ini mengembalikan urutan jenis anonim yang berisi Student objek dan Department objek.

Klausa join kedua mengaitkan tipe anonim yang dikembalikan oleh penggabungan pertama dengan objek Teacher berdasarkan kecocokan ID guru dengan ID kepala departemen. Ini mengembalikan urutan jenis anonim yang berisi nama siswa, nama departemen, dan nama pemimpin departemen. Karena operasi ini adalah gabungan dalam, kueri hanya mengembalikan objek tersebut dari sumber data pertama yang memiliki kecocokan di sumber data kedua.

// 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}".""");
}

Kueri yang setara, yang memanfaatkan beberapa Join metode, menggunakan pendekatan yang sama dengan tipe 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}".""");
}

Gabungan dalam dengan menggunakan gabungan yang dikelompokkan

Contoh berikut menunjukkan cara menerapkan gabungan dalam dengan menggunakan gabungan grup. Daftar Department objek digabungkan dengan grup ke daftar Student objek berdasarkan properti yang Department.IDStudent.DepartmentID cocok. Gabungan grup membuat kumpulan grup perantara, di mana setiap grup terdiri dari objek Department dan urutan objek Student yang 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}");
}

Anda dapat mencapai hasil yang sama dengan menggunakan metode GroupJoin, seperti yang ditunjukkan dalam contoh 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 kumpulan hasil yang diperoleh dengan menggunakan klausa join tanpa klausa into untuk melakukan gabungan dalam. 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, gunakan metode tunggal Join 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

Gabungan grup 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 gabungan grup terlepas dari apakah elemen berkorelasi ditemukan di koleksi kedua. Jika 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. Perilaku ini berbeda dari pemilih hasil dalam gabungan non-grup, 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 bentuk gabungan yang dikelompokkan. Untuk informasi selengkapnya, lihat Entity Framework Core, GroupJoin.

Contoh pertama dalam artikel ini memperlihatkan cara melakukan join antar-grup. Contoh kedua menunjukkan cara menggunakan gabungan grup untuk membuat elemen XML.

Bergabung dengan grup

Contoh berikut melakukan gabungan grup objek jenis Department dan Student berdasarkan Department.ID yang cocok dengan properti Student.DepartmentID. Tidak seperti gabungan non-grup, yang menghasilkan sepasang elemen untuk setiap kecocokan, gabungan grup hanya menghasilkan satu objek untuk setiap elemen dalam koleksi pertama. Dalam contoh ini, objek pertama adalah koleksi Department. 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 sebelumnya, 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}");
    }
}

Bergabung dengan grup 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 luar

.NET 10 mencakup metode LeftJoin dan RightJoin dalam kelas System.Linq.Enumerable dan System.Linq.Queryable. Metode ini masing-masing melakukan equijoin kiri luar dan equijoin kanan luar. Equijoin kiri luar adalah penggabungan di mana setiap anggota dari urutan pertama disertakan dalam urutan keluaran, meskipun urutan kedua tidak menyertakan kecocokan. Equijoin kanan luar adalah gabungan di mana setiap anggota urutan kedua disertakan dalam urutan output, bahkan jika urutan pertama tidak menyertakan kecocokan.

Mensimulasikan join luar kiri

Sebelum .NET 10, gunakan LINQ untuk melakukan join luar kiri dengan memanggil metode DefaultIfEmpty pada hasil join grup.

Contoh berikut menunjukkan cara menggunakan metode DefaultIfEmpty pada hasil dari penggabungan grup untuk melakukan join kiri luar.

Langkah pertama dalam menghasilkan gabungan luar kiri dari dua koleksi adalah melakukan gabungan dalam dengan menggunakan gabungan grup. (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. Anda menyelesaikan langkah ini dengan memanggil DefaultIfEmpty pada setiap urutan elemen yang cocok dari hasil gabungan grup. Dalam contoh ini, Anda memanggil DefaultIfEmpty pada setiap urutan 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 })
    .SelectMany(
        joinedSet => joinedSet.subgroup.DefaultIfEmpty(),
        (student, department) => new
        {
            student.student.FirstName,
            student.student.LastName,
            Department = department?.Name ?? string.Empty
        });

foreach (var v in query)
{
    Console.WriteLine($"{v.FirstName:-15} {v.LastName:-15}: {v.Department}");
}

Lihat juga