Karmaşık Sorgu İşleçleri
Dil Tümleşik Sorgusu (LINQ), birden çok veri kaynağını birleştiren veya karmaşık işleme yapan birçok karmaşık işleç içerir. Tüm LINQ işleçlerinin sunucu tarafında uygun çevirileri yoktur. Bazen, bir formdaki sorgu sunucuya çevrilir, ancak farklı bir biçimde yazılmışsa sonuç aynı olsa bile çevrilemez. Bu sayfada bazı karmaşık işleçler ve bunların desteklenen varyasyonları açıklanmaktadır. Gelecek sürümlerde daha fazla desen tanıyabilir ve karşılık gelen çevirilerini ekleyebiliriz. Çeviri desteğinin sağlayıcılar arasında farklılık gösterdiğini aklınızda bulundurmak da önemlidir. SqlServer'da çevrilen belirli bir sorgu SQLite veritabanları için çalışmayabilir.
Bahşiş
Bu makalenin örneğini GitHub'da görüntüleyebilirsiniz.
Katılın
LINQ Join işleci, her kaynağın anahtar seçicisini temel alarak iki veri kaynağını bağlamanıza olanak tanır ve anahtar eşleştiğinde bir değer tanımlama grubu oluşturur. Doğal olarak ilişkisel veritabanlarına çevrilir INNER JOIN
. LINQ Join'in dış ve iç anahtar seçicileri olsa da, veritabanı tek bir birleştirme koşulu gerektirir. Bu nedenle EF Core, dış anahtar seçicisini eşitlik için iç anahtar seçiciyle karşılaştırarak bir birleştirme koşulu oluşturur.
var query = from photo in context.Set<PersonPhoto>()
join person in context.Set<Person>()
on photo.PersonPhotoId equals person.PhotoId
select new { person, photo };
SELECT [p].[PersonId], [p].[Name], [p].[PhotoId], [p0].[PersonPhotoId], [p0].[Caption], [p0].[Photo]
FROM [PersonPhoto] AS [p0]
INNER JOIN [Person] AS [p] ON [p0].[PersonPhotoId] = [p].[PhotoId]
Ayrıca, anahtar seçiciler anonim türlerse EF Core eşitlik bileşeni açısından karşılaştırmak için bir birleştirme koşulu oluşturur.
var query = from photo in context.Set<PersonPhoto>()
join person in context.Set<Person>()
on new { Id = (int?)photo.PersonPhotoId, photo.Caption }
equals new { Id = person.PhotoId, Caption = "SN" }
select new { person, photo };
SELECT [p].[PersonId], [p].[Name], [p].[PhotoId], [p0].[PersonPhotoId], [p0].[Caption], [p0].[Photo]
FROM [PersonPhoto] AS [p0]
INNER JOIN [Person] AS [p] ON ([p0].[PersonPhotoId] = [p].[PhotoId] AND ([p0].[Caption] = N'SN'))
Groupjoin
LINQ GroupJoin işleci Join'e benzer iki veri kaynağını bağlamanıza olanak tanır, ancak dış öğeleri eşleştirmek için bir iç değerler grubu oluşturur. Aşağıdaki örnekte olduğu gibi bir sorgunun yürütülmesi , & IEnumerable<Post>
sonucunu Blog
oluşturur. Veritabanları (özellikle ilişkisel veritabanları) istemci tarafı nesne koleksiyonunu temsil etmek için bir yönteme sahip olmadığından GroupJoin çoğu durumda sunucuya çevirmez. Özel seçici olmadan GroupJoin yapmak için sunucudan tüm verileri almanız gerekir (aşağıdaki ilk sorgu). Ancak seçici seçilen verileri sınırlandırıyorsa, tüm verilerin sunucudan getirilmesi performans sorunlarına neden olabilir (aşağıdaki ikinci sorgu). Bu nedenle EF Core GroupJoin'i çevirmez.
var query = from b in context.Set<Blog>()
join p in context.Set<Post>()
on b.BlogId equals p.BlogId into grouping
select new { b, grouping };
var query = from b in context.Set<Blog>()
join p in context.Set<Post>()
on b.BlogId equals p.BlogId into grouping
select new { b, Posts = grouping.Where(p => p.Content.Contains("EF")).ToList() };
Selectmany
LINQ SelectMany işleci, her dış öğe için bir koleksiyon seçici üzerinde numaralandırıp her veri kaynağından değer tanımlama grupları oluşturmanıza olanak tanır. Bir şekilde, birleştirmedir ancak herhangi bir koşul içermez, bu nedenle her dış öğe koleksiyon kaynağından bir öğeyle bağlanır. Koleksiyon seçicinin dış veri kaynağıyla ilişkisine bağlı olarak SelectMany, sunucu tarafında çeşitli farklı sorgulara çevrilebilir.
Koleksiyon seçici dışa başvurmuyor
Koleksiyon seçici dış kaynaktan hiçbir şeye başvurmuyorsa, sonuç her iki veri kaynağının kartezyen ürünüdür. İlişkisel veritabanlarında olarak çevrilir CROSS JOIN
.
var query = from b in context.Set<Blog>()
from p in context.Set<Post>()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
CROSS JOIN [Posts] AS [p]
Koleksiyon seçici where yan tümcesinde dış başvurular
Koleksiyon seçicinin dış öğeye başvuran where yan tümcesi olduğunda EF Core bunu bir veritabanı birleşimine çevirir ve koşulu birleştirme koşulu olarak kullanır. Normalde bu durum, koleksiyon seçici olarak dış öğede koleksiyon gezintisi kullanılırken ortaya çıkar. Koleksiyon bir dış öğe için boşsa, bu dış öğe için hiçbir sonuç oluşturulmaz. Ancak koleksiyon seçiciye uygulanırsa DefaultIfEmpty
, dış öğe iç öğenin varsayılan değeriyle bağlanır. Bu ayrım nedeniyle, bu tür sorgular INNER JOIN
ve ne zaman DefaultIfEmpty
uygulandığının yokluğunda LEFT JOIN
DefaultIfEmpty
olarak çevrilir.
var query = from b in context.Set<Blog>()
from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId)
select new { b, p };
var query2 = from b in context.Set<Blog>()
from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId).DefaultIfEmpty()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
INNER JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]
Koleksiyon seçicisi, durum olmayan bir durumda dışa başvurur
Koleksiyon seçicisi where yan tümcesinde olmayan dış öğeye başvurduğunda (yukarıdaki örnekte olduğu gibi), veritabanı birleşimine çevrilmiyor. Bu nedenle her dış öğe için koleksiyon seçiciyi değerlendirmemiz gerekir. Birçok ilişkisel veritabanındaki işlemlere APPLY
çevrilir. Koleksiyon bir dış öğe için boşsa, bu dış öğe için hiçbir sonuç oluşturulmaz. Ancak koleksiyon seçiciye uygulanırsa DefaultIfEmpty
, dış öğe iç öğenin varsayılan değeriyle bağlanır. Bu ayrım nedeniyle, bu tür sorgular CROSS APPLY
ve ne zaman DefaultIfEmpty
uygulandığının yokluğunda OUTER APPLY
DefaultIfEmpty
olarak çevrilir. SQLite gibi bazı veritabanları işleçleri desteklemediğinden APPLY
bu tür sorgular çevrilmeyebilir.
var query = from b in context.Set<Blog>()
from p in context.Set<Post>().Select(p => b.Url + "=>" + p.Title)
select new { b, p };
var query2 = from b in context.Set<Blog>()
from p in context.Set<Post>().Select(p => b.Url + "=>" + p.Title).DefaultIfEmpty()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], ([b].[Url] + N'=>') + [p].[Title] AS [p]
FROM [Blogs] AS [b]
CROSS APPLY [Posts] AS [p]
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], ([b].[Url] + N'=>') + [p].[Title] AS [p]
FROM [Blogs] AS [b]
OUTER APPLY [Posts] AS [p]
GroupBy
LINQ GroupBy işleçleri, herhangi bir rastgele türün IGrouping<TKey, TElement>
olabileceği TKey
bir TElement
tür sonucu oluşturur. Ayrıca, uygularIEnumerable<TElement>
, IGrouping
bu da gruplandırma sonrasında herhangi bir LINQ işlecini kullanarak bunun üzerinde oluşturabileceğiniz anlamına gelir. Hiçbir veritabanı yapısı bir IGrouping
öğesini temsil edebileceğinden GroupBy işleçlerinin çoğu durumda çevirisi yoktur. Skaler döndüren her gruba bir toplama işleci uygulandığında, ilişkisel veritabanlarında SQL'e GROUP BY
çevrilebilir. SQL GROUP BY
de kısıtlayıcıdır. Yalnızca skaler değerlere göre gruplandırmanızı gerektirir. Projeksiyon yalnızca gruplandırma anahtar sütunlarını veya bir sütuna uygulanan herhangi bir toplamayı içerebilir. EF Core bu deseni tanımlar ve aşağıdaki örnekte olduğu gibi sunucuya çevirir:
var query = from p in context.Set<Post>()
group p by p.AuthorId
into g
select new { g.Key, Count = g.Count() };
SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]
FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]
EF Core, gruplandırmadaki bir toplama işlecinin Where veya OrderBy (veya başka bir sıralama) LINQ işlecinde göründüğü sorguları da çevirir. where yan tümcesi için SQL'deki yan tümcesini kullanır HAVING
. GroupBy işlecini uygulamadan önce sorgunun parçası, sunucuya çevrilebildiği sürece herhangi bir karmaşık sorgu olabilir. Ayrıca, sonuçta elde edilen kaynaktan gruplandırmaları kaldırmak için bir gruplandırma sorgusuna toplama işleçleri uyguladıktan sonra, bunun üzerine diğer sorgular gibi oluşturabilirsiniz.
var query = from p in context.Set<Post>()
group p by p.AuthorId
into g
where g.Count() > 0
orderby g.Key
select new { g.Key, Count = g.Count() };
SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]
FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]
HAVING COUNT(*) > 0
ORDER BY [p].[AuthorId]
EF Core'un desteklediği toplama işleçleri aşağıdaki gibidir
.NET | SQL |
---|---|
Average(x => x.Property) | AVG(Özellik) |
Count() | COUNT(*) |
LongCount() | COUNT(*) |
Max(x => x.Property) | MAX(Özellik) |
Min(x => x.Property) | MIN(Özellik) |
Sum(x => x.Property) | SUM(Özellik) |
Ek toplama işleçleri desteklenebilir. Daha fazla işlev eşlemesi için sağlayıcı belgelerinize bakın.
bir öğesini temsil eden bir IGrouping
veritabanı yapısı olmasa da, bazı durumlarda EF Core 7.0 ve daha yeni sürümler, sonuçlar veritabanından döndürüldükten sonra gruplandırmalar oluşturabilir. Bu, işlecin ilgili koleksiyonları eklerken nasıl çalıştığına Include
benzer. Aşağıdaki LINQ sorgusu, sonuçları Price özelliğinin değerine göre gruplandırmak için GroupBy işlecini kullanır.
var query = context.Books.GroupBy(s => s.Price);
SELECT [b].[Price], [b].[Id], [b].[AuthorId]
FROM [Books] AS [b]
ORDER BY [b].[Price]
Bu durumda GroupBy işleci doğrudan SQL'deki bir GROUP BY
yan tümceye çevrilmiyor, bunun yerine EF Core, sonuçlar sunucudan döndürüldükten sonra gruplandırmaları oluşturur.
Sol Birleştirme
Left Join bir LINQ işleci olmasa da ilişkisel veritabanlarında sorgularda sık kullanılan Bir Sol Birleştirme kavramı vardır. LINQ sorgularındaki belirli bir desen, sunucudaki ile LEFT JOIN
aynı sonucu verir. EF Core bu tür desenleri tanımlar ve sunucu tarafında eşdeğerini LEFT JOIN
oluşturur. Desen, her iki veri kaynağı arasında bir GroupJoin oluşturmayı ve sonra selectMany işlecini gruplandırma kaynağında DefaultIfEmpty ile kullanarak gruplandırmayı düzleştirmeyi, iç öğeyle ilgili bir öğe olmadığında null ile eşleştirmeyi içerir. Aşağıdaki örnekte bu desenin nasıl göründüğü ve ne oluşturduğu gösterilmektedir.
var query = from b in context.Set<Blog>()
join p in context.Set<Post>()
on b.BlogId equals p.BlogId into grouping
from p in grouping.DefaultIfEmpty()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]
Yukarıdaki desen, ifade ağacında karmaşık bir yapı oluşturur. Bu nedenle EF Core, işleci hemen izleyen bir adımda GroupJoin işlecinin gruplandırma sonuçlarını düzleştirmenizi gerektirir. GroupJoin-DefaultIfEmpty-SelectMany farklı bir desende kullanılsa bile, bunu Sol Birleştirme olarak tanımlamayabiliriz.