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 JOINDefaultIfEmpty 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 APPLYDefaultIfEmpty 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 IGroupingveritabanı 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.