Join LINQ'teki işlemler

join İki veri kaynağından biri, bir veri kaynağındaki nesnelerin başka bir veri kaynağındaki ortak özniteliği paylaşan nesnelerle ilişkisidir.

Önemli

Bu örnekler bir System.Collections.Generic.IEnumerable<T> veri kaynağı kullanır. Veri kaynaklarını ve ifade ağaçlarını System.Linq.IQueryProviderkullanan System.Linq.IQueryable<T> veri kaynakları. İfade ağaçlarının izin verilen C# söz diziminde sınırlamaları vardır. Ayrıca EF Core gibi her IQueryProvider veri kaynağı daha fazla kısıtlama uygulayabilir. Veri kaynağınızın belgelerine bakın.

Birleştirme, birbirleriyle ilişkileri doğrudan izlenemeyen veri kaynaklarını hedefleyen sorgularda önemli bir işlemdir. Nesne odaklı programlamada birleştirme, tek yönlü ilişkinin geriye dönük yönü gibi modellenmemiş nesneler arasında bağıntı anlamına gelebilir. Tek yönlü ilişki örneği, Student birincil öğeyi temsil eden türünde Department bir özelliği olan ancak sınıfın Department nesne koleksiyonu Student olan bir özelliği olmayan bir sınıftır. Nesnelerin bir listesi Department varsa ve her bölümdeki tüm öğrencileri bulmak istiyorsanız, bunları bulmak için bir join işlem kullanabilirsiniz.

join LINQ çerçevesinde sağlanan yöntemler ve GroupJoinşeklindedirJoin. Bu yöntemler, anahtarlarının eşitliğine bağlı olarak iki veri kaynağıyla eşleşen eş birleşimler veya birleşimler gerçekleştirir. (Karşılaştırma için Transact-SQL, işleç gibi dışındaki equalsişleçleri less than desteklerjoin.) İlişkisel veritabanı terimlerinde, Join yalnızca diğer veri kümesinde eşleşmesi olan nesnelerin döndürüldiği bir iç jointürü join uygular. yönteminin GroupJoin ilişkisel veritabanı terimlerinde doğrudan eşdeğeri yoktur, ancak iç birleşimlerin ve sol dış birleşimlerin üst kümesini uygular. Sol dış join , diğer veri kaynağında bağıntılı öğe olmasa bile ilk (sol) veri kaynağının her öğesini döndüren bir değerdir join .

Aşağıdaki çizimde, bir iç join veya sol dış joiniçine dahil edilen iki kümenin ve bu kümelerdeki öğelerin kavramsal görünümü gösterilmektedir.

İç/'nin gösterildiği iki örtüşen daire; Dış.

Yöntemler

Yöntem Adı Açıklama C# Sorgu İfadesi Söz Dizimi Daha Fazla Bilgi
Join Anahtar seçici işlevlerini temel alan iki diziyi birleştirir ve değer çiftlerini ayıklar. join … in … on … equals … Enumerable.Join

Queryable.Join
GroupJoin Anahtar seçici işlevlerine göre iki diziyi birleştirir ve her öğe için sonuçta elde edilen eşleşmeleri gruplar. join … in … on … equals … into … Enumerable.GroupJoin

Queryable.GroupJoin

Not

Bu makaledeki aşağıdaki örneklerde bu alan için ortak veri kaynakları kullanılır.
Her Student birinin bir not düzeyi, bir birincil bölüm ve bir dizi puanı vardır. Ayrıca, Teacher öğretmenin ders aldığı kampüsü tanımlayan bir City özelliği de vardır. A'nın Department bir adı ve bölüm başkanı olarak görev yapan bir Teacher kişi için bir referansı vardır.
Örnek veri kümesini kaynak depoda bulabilirsiniz.

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

Aşağıdaki örnek, belirli bir değere göre iki diziye join yan tümcesini join … in … on … equals … kullanır:

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

Yukarıdaki sorgu, aşağıdaki kodda gösterildiği gibi yöntem söz dizimi kullanılarak ifade edilebilir:

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

Aşağıdaki örnek, yan tümcesini join … in … on … equals … into … belirli bir değere göre iki diziye join kullanır ve sonuçta elde edilen eşleşmeleri her öğe için gruplandırmaktadır:

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

Yukarıdaki sorgu, aşağıdaki örnekte gösterildiği gibi yöntem söz dizimi kullanılarak ifade edilebilir:

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

İç birleşimler gerçekleştirme

İlişkisel veritabanı terimlerinde iç, join ilk koleksiyonun her öğesinin ikinci koleksiyondaki eşleşen her öğe için bir kez göründüğü bir sonuç kümesi oluşturur. İlk koleksiyondaki bir öğenin eşleşen öğesi yoksa sonuç kümesinde görünmez. Join C# içindeki yan tümcesi join tarafından çağrılan yöntemi bir iç joinuygular. Aşağıdaki örneklerde, bir iç joindizinin dört varyasyonunu nasıl gerçekleştirebileceğiniz gösterilmektedir:

  • Basit bir anahtara dayalı olarak iki veri kaynağındaki öğeleri ilişkilendiren basit bir iç join .
  • join Bileşik anahtara dayalı olarak iki veri kaynağındaki öğeleri ilişkilendiren iç. Birden fazla değerden oluşan bir anahtar olan bileşik anahtar, birden fazla özelliğe göre öğeleri ilişkilendirmenizi sağlar.
  • Birbirini izleyen işlemlerin birbirine eklendiği katjoin.join
  • bir grup joinkullanılarak uygulanan bir içjoin.

Tek tuş join

Aşağıdaki örnek, nesneleri ile eşleşen nesnelerle eşleştirir Teacher Teacher.Deparment TeacherId select C# içindeki yan tümcesi, sonuçta elde edilen nesnelerin nasıl görüneceğini tanımlar. Aşağıdaki örnekte, elde edilen nesneler departman adından ve bölüme liderlik eden öğretmenin adından oluşan anonim türlerdir.

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

Yöntem söz dizimini Join kullanarak aynı sonuçları elde edebilirsiniz:

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

Bölüm başkanı olmayan öğretmenler nihai sonuçlarda görünmez.

Bileşik anahtar join

Öğeleri tek bir özelliğe göre ilişkilendirmek yerine, birden çok özelliğe göre öğeleri karşılaştırmak için bileşik anahtar kullanabilirsiniz. Karşılaştırmak istediğiniz özelliklerden oluşan anonim bir tür döndürmek için her koleksiyon için anahtar seçici işlevini belirtin. Özellikleri etiketlerseniz, her anahtarın anonim türünde aynı etikete sahip olmaları gerekir. Özelliklerin de aynı sırada görünmesi gerekir.

Aşağıdaki örnek, hangi öğretmenlerin Teacher de öğrenci olduğunu belirlemek için nesnelerin listesini ve nesnelerin listesini Student kullanır. Bu türlerin her ikisi de her bir kişinin adını ve aile adını temsil eden özelliklere sahiptir. Her listenin join öğelerinden anahtarları oluşturan işlevler, özelliklerden oluşan anonim bir tür döndürür. İşlem, join bu bileşik anahtarları eşitlik için karşılaştırır ve her listeden hem adın hem de aile adının eşleştiği nesne çiftlerini döndürür.

// 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şağıdaki örnekte gösterildiği gibi yöntemini kullanabilirsiniz Join :

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

Çoklu join

Birden çok joinişlemi gerçekleştirmek için herhangi bir sayıda join işlem birbirine eklenebilir. C# içindeki her join yan tümce, belirtilen bir veri kaynağını önceki joinsonucunun sonuçlarıyla ilişkilendirmektedir.

birinci join yan tümce, bir Student nesnenin bir nesnenin ile eşleşen Department öğesine DepartmentID göre öğrencilerle ve bölümlerle IDeşleşir. Nesneyi ve Department nesneyi içeren anonim türlerin bir dizisini Student döndürür.

İkinci join yan tümce, birinci join tarafından döndürülen anonim türleri, bölüm başı kimliğiyle eşleşen öğretmenin kimliğine göre nesnelerle Teacher ilişkilendirmektedir. Öğrencinin adını, bölüm adını ve bölüm liderinin adını içeren anonim türlerden oluşan bir dizi döndürür. Bu işlem bir iç joinolduğundan, yalnızca ikinci veri kaynağında eşleşmesi olan ilk veri kaynağındaki nesneler döndürülür.

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

Birden çok Join yöntem kullanan eşdeğer, anonim türle aynı yaklaşımı kullanır:

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

Gruplandırılmış kullanarak iç joinjoin

Aşağıdaki örnekte, bir grup joinkullanarak bir iç join öğesinin nasıl uygulandığı gösterilmektedir. Nesne listesiDepartment, eşleşen Student.DepartmentID özelliğe göre Department.ID nesne listesine Student grup olarak katılır. Grup join , her grubun bir Department nesneden ve eşleşen Student nesne dizilerinden oluştuğu bir ara grup koleksiyonu oluşturur. İkinci from yan tümce, bu dizi dizisini birleştirir (veya düzleştirir) daha uzun bir dizide. select yan tümcesi, son dizideki öğelerin türünü belirtir. Bu tür, öğrencinin adından ve eşleşen bölüm adından oluşan anonim bir türdür.

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

Yöntem kullanılarak GroupJoin aşağıdaki gibi aynı sonuçlar elde edilebilir:

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

Sonuç, iç joinöğesini gerçekleştirmek için yan tümcesi olmadan yan tümcesi join into kullanılarak elde edilen sonuç kümesine eşdeğerdir. Aşağıdaki kod bu eşdeğer sorguyu gösterir:

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

Zincirlemeden kaçınmak için tek Join yöntem burada gösterildiği gibi kullanılabilir:

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

Gruplanmış birleşimler gerçekleştirme

Grup join , hiyerarşik veri yapıları oluşturmak için kullanışlıdır. İlk koleksiyondaki her öğeyi ikinci koleksiyondaki bağıntılı öğeler kümesiyle eşleştiriyor.

Not

İlk koleksiyonun her öğesi, bağıntılı öğelerin ikinci koleksiyonda bulunup bulunmadığına bakılmaksızın grubun join sonuç kümesinde görünür. Bağıntılı öğe bulunamazsa, bu öğe için bağıntılı öğelerin dizisi boş olur. Bu nedenle sonuç seçici, ilk koleksiyonun her öğesine erişebilir. Bu, ikinci koleksiyonda eşleşmeyen ilk koleksiyondaki öğelere erişemeyen, grup joindışı bir içindeki sonuç seçicisinden farklıdır.

Uyarı

Enumerable.GroupJoin geleneksel ilişkisel veritabanı terimlerinde doğrudan eşdeğeri yoktur. Ancak bu yöntem iç birleşimlerin ve sol dış birleşimlerin üst kümesini uygular. Bu işlemlerin her ikisi de gruplandırılmış joinolarak yazılabilir. Daha fazla bilgi için bkz . Entity Framework Core, GroupJoin.

Bu makaledeki ilk örnek, bir grubu joinnasıl gerçekleştirebileceğinizi gösterir. İkinci örnekte, XML öğeleri oluşturmak için bir grubun join nasıl kullanılacağı gösterilmektedir.

Grup join

Aşağıdaki örnek, türündeki Student Department ve eşleşen Student.DepartmentID özelliğine göre Department.ID bir nesne grubu join gerçekleştirir. Her eşleşme için bir öğe çifti oluşturan grup joindışı bir öğesinden farklı olarak, grup join ilk koleksiyonun her öğesi için yalnızca bir sonuç nesnesi üretir ve bu örnekte bir Department nesnedir. Bu örnekte Student nesneler olan ikinci koleksiyona karşılık gelen öğeler bir koleksiyonda gruplandırılır. Son olarak sonuç seçici işlevi, her eşleşme için ve bir nesne koleksiyonundan Student oluşan Department.Name anonim bir tür oluşturur.

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

Yukarıdaki örnekte değişken, query her öğenin bölümün adını ve o bölümde okuyan öğrenci koleksiyonunu içeren anonim bir tür olduğu bir liste oluşturan sorguyu içerir.

Yöntem söz dizimini kullanan eşdeğer sorgu aşağıdaki kodda gösterilir:

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

XML oluşturmak için gruplandırma join

Grup birleştirmeleri, LINQ to XML kullanarak XML oluşturmak için idealdir. Aşağıdaki örnek önceki örneğe benzer, ancak sonuç seçici işlevi anonim türler oluşturmak yerine birleştirilen nesneleri temsil eden XML öğeleri oluşturur.

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

Yöntem söz dizimini kullanan eşdeğer sorgu aşağıdaki kodda gösterilir:

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

Sol dış birleşimler gerçekleştirme

Sol dış join , ikinci koleksiyonda herhangi bir join bağıntılı öğe olup olmadığına bakılmaksızın, ilk koleksiyonun her öğesinin döndürülür. LINQ kullanarak bir grubun joinsonuçlarında yöntemini çağırarak DefaultIfEmpty sol dış join işlemi gerçekleştirebilirsiniz.

Aşağıdaki örnekte, sol dış joinöğesini gerçekleştirmek için bir grubun join sonuçlarında yönteminin nasıl kullanılacağı DefaultIfEmpty gösterilmektedir.

İki koleksiyonun sol dış kısmını join oluşturmanın ilk adımı, bir grup joinkullanarak bir iç join gerçekleştirmektir. (Bkz.Bu işlemin açıklaması için iç birleşimler gerçekleştirin.) Bu örnekte, nesne listesiDepartment, öğrencinin DepartmentIDkimliğiyle eşleşen bir Department nesnenin Student kimliğine göre nesne listesiyle içsel olarak birleştirilir.

İkinci adım, ilk (sol) koleksiyonun her öğesini, bu öğenin doğru koleksiyonda eşleşmesi olmasa bile sonuç kümesine eklemektir. Bu, grubundan joineşleşen öğelerin her dizisinde çağrılarak DefaultIfEmpty gerçekleştirilir. Bu örnekte, DefaultIfEmpty eşleşen Student nesnelerin her dizisinde çağrılır. yöntemi, eşleşen Student nesnelerin dizisi herhangi Department bir nesne için boşsa tek bir varsayılan değer içeren bir koleksiyon döndürür ve her Department nesnenin sonuç koleksiyonunda temsil edilmesini sağlar.

Not

Bir başvuru türü için varsayılan değerdir null; bu nedenle örnek, her Student koleksiyonun her öğesine erişmeden önce null başvuru olup olmadığını denetler.

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

Yöntem söz dizimini kullanan eşdeğer sorgu aşağıdaki kodda gösterilir:

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

Ayrıca bkz.