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 equals
iş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.
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 ID
eş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 DepartmentID
kimliğ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}");
}