Join LINQ'teki işlemler
İki veri kaynağının birleşimi, 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.
Joining, 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 birleştirme işlemini kullanabilirsiniz.
LINQ çerçevesinde sağlanan birleştirme yöntemleri 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ışında equals
birleştirme işleçlerini less than
destekler.) İlişkisel veritabanı terimlerinde, Join yalnızca diğer veri kümesinde eşleşmesi olan nesnelerin döndürüldiği bir birleştirme türü olan bir iç birleşim 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ış birleşim, diğer veri kaynağında bağıntılı öğe olmasa bile ilk (sol) veri kaynağının her öğesini döndüren bir birleşimdir.
Aşağıdaki çizimde, iki kümenin kavramsal görünümü ve bu kümelerdeki iç birleşimde veya sol dış birleşimde yer alan öğeler gösterilmektedir.
Yöntemler
Yöntem Adı | Açıklama | C# Sorgu İfadesi Söz Dizimi | Daha Fazla Bilgi |
---|---|---|---|
Join | Joins anahtar seçici işlevlerini temel alan iki dizi ve değer çiftlerini ayıklar. | join … in … on … equals … |
Enumerable.Join Queryable.Join |
GroupJoin | Joins anahtar seçici işlevlerine dayalı iki dizi ve her öğe için sonuçta elde edilen eşleşmeleri gruplandırma. | join … in … on … equals … into … |
Enumerable.GroupJoin Queryable.GroupJoin |
Bu makaledeki aşağıdaki örneklerde bu alan için ortak veri kaynakları kullanılır:
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; }
}
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.
Aşağıdaki örnek, belirli bir değere göre iki diziyi birleştirmek için 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, belirli bir değere göre iki diziyi birleştirmek ve sonuçta elde edilen eşleşmeleri her öğe için gruplandırmak için yan tümcesini kullanır join … in … on … equals … into …
:
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ç birleşim, 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ç birleşim uygular. Aşağıdaki örneklerde, bir iç birleşimin 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ç birleşim.
- bileşik anahtara dayalı olarak iki veri kaynağındaki öğeleri ilişkilendiren iç birleşim. 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 birleştirme işlemlerinin birbirine eklendiği çoklu birleştirme.
- Grup birleştirme kullanılarak uygulanan iç birleşim.
Tek tuşla birleştirme
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 birleştirme
Öğ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 öğelerinden birleştirme anahtarlarını oluşturan işlevler, özelliklerden oluşan anonim bir tür döndürür. Birleştirme işlemi 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);
}
Birden çok birleşim
Birden çok birleştirme gerçekleştirmek için herhangi bir sayıda birleştirme işlemi birbirine eklenebilir. C# içindeki her join
yan tümce, belirtilen bir veri kaynağını önceki birleştirmenin 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, ilk birleşim 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ç birleşim olduğ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ış birleştirme kullanarak iç birleşim
Aşağıdaki örnekte, grup birleştirme kullanarak iç birleştirmenin nasıl uygulanacağınız gösterilmektedir. Nesne listesiDepartment
, eşleşen Student.DepartmentID
özelliğe göre Department.ID
nesne listesine Student
grup olarak katılır. Grup birleştirme, 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ç birleşim 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 birleştirme, 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 grup birleştirmenin 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 dışı birleştirmedeki sonuç seçiciden 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ış birleştirme açısından yazılabilir. Daha fazla bilgi için bkz . Entity Framework Core, GroupJoin.
Bu makaledeki ilk örnekte grup birleştirme işleminin nasıl gerçekleştirebileceğiniz gösterilmektedir. İkinci örnekte, XML öğeleri oluşturmak için grup birleştirmenin nasıl kullanılacağı gösterilmektedir.
Grup birleştirme
Aşağıdaki örnek, türündeki Department
nesnelerin grup birleştirmesini ve Student
eşleşen Student.DepartmentID
özelliğine göre Department.ID
gerçekleştirir. Her eşleşme için bir öğe çifti oluşturan grup dışı bir birleşimden farklı olarak, grup birleşimi ilk koleksiyonun her öğesi için yalnızca bir sonuç nesnesi oluşturur 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 grup birleştirme
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ış birleşim, ikinci koleksiyonda herhangi bir bağıntılı öğe olup olmadığına bakılmaksızın, ilk koleksiyonun her öğesinin döndürüldiği bir birleşimdir. LinQ kullanarak grup birleştirmenin sonuçları üzerinde yöntemini çağırarak DefaultIfEmpty sol dış birleşim gerçekleştirebilirsiniz.
Aşağıdaki örnekte, sol dış birleşim gerçekleştirmek için grup birleştirmenin sonuçlarında yönteminin nasıl kullanılacağı DefaultIfEmpty gösterilmektedir.
İki koleksiyonun sol dış birleşimini oluşturmanın ilk adımı, grup birleştirmesi kullanarak iç birleşim 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, grup birleşiminden eş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.
Geri Bildirim
https://aka.ms/ContentUserFeedback.
Çok yakında: 2024 boyunca, içerik için geri bildirim mekanizması olarak GitHub Sorunları’nı kullanımdan kaldıracak ve yeni bir geri bildirim sistemiyle değiştireceğiz. Daha fazla bilgi için bkz.Gönderin ve geri bildirimi görüntüleyin