Verileri sorgulamak için C# LINQ sorguları yazma
Giriş niteliğindeki Dil Tümleşik Sorgu (LINQ) belgelerindeki sorguların çoğu LINQ bildirim temelli sorgu söz dizimi kullanılarak yazılır. Ancak, kod derlendiğinde sorgu söz dizimi .NET ortak dil çalışma zamanı (CLR) için yöntem çağrılarına çevrilmelidir. Bu yöntem çağrıları, , Select
, Max
GroupBy
Join
ve Average
gibi Where
adlara sahip standart sorgu işleçlerini çağırır. Sorgu söz dizimi yerine yöntem söz dizimini kullanarak bunları doğrudan çağırabilirsiniz.
Sorgu söz dizimi ve yöntem söz dizimi aynı olsa da, sorgu söz dizimi genellikle daha basit ve daha kolay okunur. Bazı sorgular yöntem çağrıları olarak ifade edilmelidir. Örneğin, belirtilen koşulla eşleşen öğe sayısını alan bir sorguyu ifade etmek için yöntem çağrısı kullanmanız gerekir. Ayrıca, kaynak dizisinde en yüksek değere sahip öğeyi alan bir sorgu için yöntem çağrısı kullanmanız gerekir. Ad alanında standart sorgu işleçleri için başvuru belgelerinde System.Linq genellikle yöntem söz dizimi kullanılır. Sorgularda ve sorgu ifadelerinde yöntem söz dizimlerinin nasıl kullanılacağını biliyor olmalısınız.
Standart sorgu işleci uzantısı yöntemleri
Aşağıdaki örnekte basit bir sorgu ifadesi ve yöntem tabanlı sorgu olarak yazılmış olan eş zamanlı eşdeğer sorgu gösterilmektedir.
int[] numbers = [ 5, 10, 8, 3, 6, 12 ];
//Query syntax:
IEnumerable<int> numQuery1 =
from num in numbers
where num % 2 == 0
orderby num
select num;
//Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
foreach (int i in numQuery1)
{
Console.Write(i + " ");
}
Console.WriteLine(System.Environment.NewLine);
foreach (int i in numQuery2)
{
Console.Write(i + " ");
}
İki örnekten alınan çıkış aynıdır. Sorgu değişkeninin türünün her iki biçimde de aynı olduğunu görebilirsiniz: IEnumerable<T>.
Yöntem tabanlı sorguyu anlamak için daha yakından inceleyelim. İfadenin sağ tarafında yan tümcesinin where
nesnesi üzerinde numbers
bir örnek yöntemi olarak ifade edildiğine ve türüne IEnumerable<int>
sahip olduğuna dikkat edin. Genel IEnumerable<T> arabirim hakkında bilgi sahibiyseniz, bir Where
yöntemi olmadığını biliyorsunuz. Ancak, Visual Studio IDE'de IntelliSense tamamlama listesini çağırırsanız, yalnızca bir Where
yöntemi değil, aynı zamanda , SelectMany
, Join
ve Orderby
gibi Select
diğer birçok yöntemi görürsünüz. Bu yöntemler standart sorgu işleçlerini uygular.
Daha fazla yöntem içeriyor gibi IEnumerable<T> görünse de, içermez. Standart sorgu işleçleri uzantı yöntemleri olarak uygulanır. Uzantı yöntemleri var olan bir türü "genişletir"; türünde örnek yöntemleriymiş gibi çağrılabilirler. Standart sorgu işleçleri genişletilir IEnumerable<T> ve bu nedenle yazabilirsiniz numbers.Where(...)
.
Uzantı yöntemlerini kullanmak için bunları yönergelerle using
kapsama alırsınız. Uygulamanızın bakış açısından bir uzantı yöntemi ve normal örnek yöntemi aynıdır.
Uzantı yöntemleri hakkında daha fazla bilgi için bkz . Uzantı Yöntemleri. Standart sorgu işleçleri hakkında daha fazla bilgi için bkz . Standart Sorgu İşleçlerine Genel Bakış (C#). Entity Framework ve LINQ to XML gibi bazı LINQ sağlayıcıları, dışındaki IEnumerable<T>diğer türler için kendi standart sorgu işleçlerini ve uzantı yöntemlerini uygular.
Lambda ifadeleri
Önceki örnekte, koşullu ifadenin (num % 2 == 0
) yöntemine Enumerable.Where satır içi bağımsız değişken olarak geçirildiğini göreceksiniz: Where(num => num % 2 == 0).
Bu satır içi ifade bir lambda ifadesidir. Aksi takdirde daha hantal biçimde yazılması gereken kod yazmanın kullanışlı bir yoludur. num
işlecinin sol tarafındaki giriş değişkenidir ve bu değişken sorgu ifadesindekine num
karşılık gelir. Derleyici, bunun genel IEnumerable<T> bir tür num
olduğunu numbers
bildiği için türünü çıkarsayabilir. Lambda gövdesi, sorgu söz dizimindeki veya başka bir C# ifadesi veya deyimindeki ifadeyle aynıdır. Yöntem çağrılarını ve diğer karmaşık mantığı içerebilir. Dönüş değeri yalnızca ifade sonucudur. Bazı sorgular yalnızca yöntem söz diziminde ifade edilebilir ve bunlardan bazıları lambda ifadeleri gerektirir. Lambda ifadeleri, LINQ araç kutunuzda güçlü ve esnek bir araçtır.
Sorguların birlenebilirliği
Önceki kod örneğinde Enumerable.OrderBy yöntemi çağrısındaki nokta işleci kullanılarak çağrılır Where
. Where
filtrelenmiş bir dizi oluşturur ve ardından Orderby
tarafından Where
üretilen diziyi sıralar. Sorgular bir IEnumerable
döndüreceği için, yöntem çağrılarını birbirine zincirleyerek bunları yöntem söz diziminde oluşturursunuz. Sorgu söz dizimini kullanarak sorgu yazdığınızda derleyici bu bileşimi yapar. Sorgu değişkeni sorgunun sonuçlarını depolamadığından, sorguyu yürütürken bile istediğiniz zaman değiştirebilir veya yeni bir sorgunun temeli olarak kullanabilirsiniz.
Aşağıdaki örneklerde, daha önce listelenen her yaklaşımı kullanarak bazı basit LINQ sorguları gösterilmektedir.
Not
Bu sorgular basit bellek içi koleksiyonlar üzerinde çalışır; ancak, temel söz dizimi LINQ to Entities ve LINQ to XML içinde kullanılan söz dizimi ile aynıdır.
Örnek - Sorgu söz dizimi
Sorgu ifadeleri oluşturmak için çoğu sorguyu sorgu söz dizimi ile yazarsınız. Aşağıdaki örnekte üç sorgu ifadesi gösterilmektedir. İlk sorgu ifadesi, yan where
tümcesi ile koşullar uygulayarak sonuçları filtrelemeyi veya kısıtlamayı gösterir. Kaynak dizideki değerleri 7'den büyük veya 3'ten küçük olan tüm öğeleri döndürür. İkinci ifade, döndürülen sonuçların nasıl sıralanması gerektiğini gösterir. Üçüncü ifadede sonuçların bir anahtara göre nasıl gruplandığı gösterilir. Bu sorgu, sözcüğün ilk harfine göre iki grup döndürür.
List<int> numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
// The query variables can also be implicitly typed by using var
// Query #1.
IEnumerable<int> filteringQuery =
from num in numbers
where num is < 3 or > 7
select num;
// Query #2.
IEnumerable<int> orderingQuery =
from num in numbers
where num is < 3 or > 7
orderby num ascending
select num;
// Query #3.
string[] groupingQuery = ["carrots", "cabbage", "broccoli", "beans", "barley"];
IEnumerable<IGrouping<char, string>> queryFoodGroups =
from item in groupingQuery
group item by item[0];
Sorguların türü şeklindedir IEnumerable<T>. Bu sorguların tümü aşağıdaki örnekte gösterildiği gibi kullanılarak var
yazılabilir:
var query = from num in numbers...
Önceki her örnekte sorgular, siz bir deyimdeki veya başka bir foreach
deyimdeki sorgu değişkeni üzerinde yinelene kadar yürütülmeyecektir.
Örnek - Yöntem söz dizimi
Bazı sorgu işlemleri yöntem çağrısı olarak ifade edilmelidir. Bu tür en yaygın yöntemler, , Max, , AverageMinve gibi Sumtek sayısal değerler döndüren yöntemlerdir. Bu yöntemler her zaman tek bir değer döndüreceği ve ek sorgu işlemi için kaynak işlevi göremeyecekleri için herhangi bir sorguda en son çağrılmalıdır. Aşağıdaki örnekte sorgu ifadesindeki bir yöntem çağrısı gösterilmektedir:
List<int> numbers1 = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
List<int> numbers2 = [15, 14, 11, 13, 19, 18, 16, 17, 12, 10];
// Query #4.
double average = numbers1.Average();
// Query #5.
IEnumerable<int> concatenationQuery = numbers1.Concat(numbers2);
Yöntemin System.Action veya System.Func<TResult> parametreleri varsa, bu bağımsız değişkenler aşağıdaki örnekte gösterildiği gibi lambda ifadesi biçiminde sağlanır:
// Query #6.
IEnumerable<int> largeNumbersQuery = numbers2.Where(c => c > 15);
Önceki sorgularda yalnızca Sorgu #4, genel IEnumerable<T> bir koleksiyon değil tek bir değer döndürdüğünden hemen yürütülür. Yönteminin kendisi değerini hesaplamak için veya benzer kod kullanır foreach
.
Önceki sorguların her biri, aşağıdaki örnekte gösterildiği gibi ile var
örtük yazma kullanılarak yazılabilir:
// var is used for convenience in these queries
double average = numbers1.Average();
var concatenationQuery = numbers1.Concat(numbers2);
var largeNumbersQuery = numbers2.Where(c => c > 15);
Örnek - Karma sorgu ve yöntem söz dizimi
Bu örnekte, sorgu yan tümcesinin sonuçlarında yöntem söz diziminin nasıl kullanılacağı gösterilmektedir. Sorgu ifadesini parantez içine almanız ve ardından nokta işlecini uygulayıp yöntemini çağırması gerekir. Aşağıdaki örnekte, #7 sorgusu değeri 3 ile 7 arasında olan sayıların sayısını döndürür. Ancak genel olarak yöntem çağrısının sonucunu depolamak için ikinci bir değişken kullanmak daha iyidir. Bu şekilde sorgunun sonuçlarıyla karıştırılma olasılığı daha düşüktür.
// Query #7.
// Using a query expression with method syntax
var numCount1 = (
from num in numbers1
where num is > 3 and < 7
select num
).Count();
// Better: Create a new variable to store
// the method call result
IEnumerable<int> numbersQuery =
from num in numbers1
where num is > 3 and < 7
select num;
var numCount2 = numbersQuery.Count();
Sorgu #7 bir koleksiyon değil tek bir değer döndürdüğünden sorgu hemen yürütülür.
Önceki sorgu aşağıdaki gibi ile var
örtük yazma kullanılarak yazılabilir:
var numCount = (from num in numbers...
Yöntem söz diziminde aşağıdaki gibi yazılabilir:
var numCount = numbers.Count(n => n is > 3 and < 7);
Aşağıdaki gibi açık yazma kullanılarak yazılabilir:
int numCount = numbers.Count(n => n is > 3 and < 7);
Çalışma zamanında koşul filtrelerini dinamik olarak belirtme
Bazı durumlarda, çalışma zamanına kadar yan tümcedeki where
kaynak öğelere uygulamanız gereken koşul sayısını bilmezsiniz. Birden çok koşul filtresini dinamik olarak belirtmenin Contains bir yolu, aşağıdaki örnekte gösterildiği gibi yöntemini kullanmaktır. Sorgu, sorgunun yürütüldüğü zaman değerine id
göre farklı sonuçlar döndürür.
int[] ids = [111, 114, 112];
var queryNames =
from student in students
where ids.Contains(student.ID)
select new
{
student.LastName,
student.ID
};
foreach (var name in queryNames)
{
Console.WriteLine($"{name.LastName}: {name.ID}");
}
/* Output:
Garcia: 114
O'Donnell: 112
Omelchenko: 111
*/
// Change the ids.
ids = [122, 117, 120, 115];
// The query will now return different results
foreach (var name in queryNames)
{
Console.WriteLine($"{name.LastName}: {name.ID}");
}
/* Output:
Adams: 120
Feng: 117
Garcia: 115
Tucker: 122
*/
Önceden belirlenmiş alternatif sorgular arasından seçim yapmak için veya switch
gibi if... else
denetim akışı deyimlerini kullanabilirsiniz. Aşağıdaki örnekte, studentQuery
çalışma zamanı değeri oddYear
veya false
ise true
farklı where
bir yan tümcesi kullanır.
void FilterByYearType(bool oddYear)
{
IEnumerable<Student> studentQuery = oddYear
? (from student in students
where student.Year is GradeLevel.FirstYear or GradeLevel.ThirdYear
select student)
: (from student in students
where student.Year is GradeLevel.SecondYear or GradeLevel.FourthYear
select student);
var descr = oddYear ? "odd" : "even";
Console.WriteLine($"The following students are at an {descr} year level:");
foreach (Student name in studentQuery)
{
Console.WriteLine($"{name.LastName}: {name.ID}");
}
}
FilterByYearType(true);
/* Output:
The following students are at an odd year level:
Fakhouri: 116
Feng: 117
Garcia: 115
Mortensen: 113
Tucker: 119
Tucker: 122
*/
FilterByYearType(false);
/* Output:
The following students are at an even year level:
Adams: 120
Garcia: 114
Garcia: 118
O'Donnell: 112
Omelchenko: 111
Zabokritski: 121
*/
Sorgu ifadelerinde boş değerler işleme
Bu örnek, kaynak koleksiyonlardaki olası null değerlerin nasıl işleneceğini gösterir. gibi bir IEnumerable<T> nesne koleksiyonu, değeri null olan öğeler içerebilir. Kaynak koleksiyon ise null
veya değeri null
olan bir öğe içeriyorsa ve sorgunuz değerleri işlemezse null
, sorguyu yürüttüğünüzde bir NullReferenceException oluşturulur.
Aşağıdaki örnekte gösterildiği gibi null başvuru özel durumunu önlemek için savunmayla kodlayabilirsiniz:
var query1 =
from c in categories
where c != null
join p in products on c.ID equals p?.CategoryID
select new
{
Category = c.Name,
Name = p.Name
};
Önceki örnekte yan tümcesi, where
kategoriler dizisindeki tüm null öğeleri filtreler. Bu teknik, join yan tümcesindeki null denetiminden bağımsızdır. Bu örnekte null olan koşullu ifade, için Nullable<int>
kısaltma olan türünde int?
olduğundan çalışırProducts.CategoryID
.
Birleştirme yan tümcesinde, karşılaştırma anahtarlarından yalnızca biri null değer türündeyse, diğerini sorgu ifadesinde null atanabilir bir değer türüne dönüştürebilirsiniz. Aşağıdaki örnekte, türündeki int?
değerleri içeren bir sütun olduğunu EmployeeID
varsayalım:
var query =
from o in db.Orders
join e in db.Employees
on o.EmployeeID equals (int?)e.EmployeeID
select new { o.OrderID, e.FirstName };
Örneklerin equals
her birinde sorgu anahtar sözcüğü kullanılır. ve is not null
için desenler içeren desen is null
eşleştirmeyi de kullanabilirsiniz. Sorgu sağlayıcıları yeni C# söz dizimini doğru yorumlayamadığından bu desenler LINQ sorgularında önerilmez. Sorgu sağlayıcısı, C# sorgu ifadelerini Entity Framework Core gibi yerel bir veri biçimine çeviren bir kitaplıktır. Sorgu sağlayıcıları arabirimi uygulayan System.Linq.IQueryProvider veri kaynakları oluşturmak için arabirimi uygular System.Linq.IQueryable<T> .
Sorgu ifadelerinde özel durumları işleme
Sorgu ifadesi bağlamında herhangi bir yöntemi çağırmak mümkündür. Veri kaynağının içeriğini değiştirme veya özel durum oluşturma gibi yan etki oluşturabilen bir sorgu ifadesinde hiçbir yöntemi çağırmayın. Bu örnekte, özel durum işlemeyle ilgili genel .NET yönergelerini ihlal etmeden sorgu ifadesindeki yöntemleri çağırdığınızda özel durumların nasıl yükseltilmesinin önlenmesi gösterilmektedir. Bu yönergeler, belirli bir bağlamda neden atıldığını anladığınızda belirli bir özel durumu yakalamanın kabul edilebilir olduğunu belirtir. Daha fazla bilgi için bkz . Özel Durumlar için En İyi Yöntemler.
Son örnek, bir sorgunun yürütülmesi sırasında özel durum oluşturmanız gerektiğinde bu durumların nasıl işleneceğini gösterir.
Aşağıdaki örnekte, özel durum işleme kodunun sorgu ifadesinin dışına nasıl taşınacakları gösterilmektedir. Bu yeniden düzenleme yalnızca yöntem sorgunun yerel değişkenlerine bağımlı olmadığında mümkündür. Sorgu ifadesinin dışındaki özel durumlarla başa çıkmak daha kolaydır.
// A data source that is very likely to throw an exception!
IEnumerable<int> GetData() => throw new InvalidOperationException();
// DO THIS with a datasource that might
// throw an exception.
IEnumerable<int>? dataSource = null;
try
{
dataSource = GetData();
}
catch (InvalidOperationException)
{
Console.WriteLine("Invalid operation");
}
if (dataSource is not null)
{
// If we get here, it is safe to proceed.
var query =
from i in dataSource
select i * i;
foreach (var i in query)
{
Console.WriteLine(i.ToString());
}
}
catch (InvalidOperationException)
Yukarıdaki örnekteki blokta, özel durumu uygulamanız için uygun şekilde işleyin (veya işlemeyin).
Bazı durumlarda, sorgunun içinden oluşan bir özel duruma en iyi yanıt, sorgu yürütmeyi hemen durdurmak olabilir. Aşağıdaki örnek, sorgu gövdesinden oluşturulabilecek özel durumların nasıl işleneceğini gösterir. Bunun SomeMethodThatMightThrow
, sorgu yürütmenin durdurulmasını gerektiren bir özel duruma neden olabileceğini varsayalım.
try
Blok, sorgunun foreach
kendisini değil döngünün içine alır. foreach
Döngü, sorgunun yürütüldiği noktadır. Sorgu yürütülürken çalışma zamanı özel durumları oluşturulur. Bu nedenle döngüde foreach
işlenmelidir.
// Not very useful as a general purpose method.
string SomeMethodThatMightThrow(string s) =>
s[4] == 'C' ?
throw new InvalidOperationException() :
@"C:\newFolder\" + s;
// Data source.
string[] files = ["fileA.txt", "fileB.txt", "fileC.txt"];
// Demonstration query that throws.
var exceptionDemoQuery =
from file in files
let n = SomeMethodThatMightThrow(file)
select n;
try
{
foreach (var item in exceptionDemoQuery)
{
Console.WriteLine($"Processing {item}");
}
}
catch (InvalidOperationException e)
{
Console.WriteLine(e.Message);
}
/* Output:
Processing C:\newFolder\fileA.txt
Processing C:\newFolder\fileB.txt
Operation is not valid due to the current state of the object.
*/
Bir blokta finally
yükseltmeyi beklediğiniz özel durumu yakalamayı ve/veya gerekli temizlemeyi yapmayı unutmayın.