İlkay İlknur

just a developer...

Entity Framework–ToList().Count vs Any() vs Count()

Merhaba Arkadaşlar, Bu yazımıza basit bir soru ile başlıyoruz :)
var email = from inc in entities.be_Settings                             
 where inc.SettingName == "email"                             
 select inc;
 

Yukarıdaki Linq-to-Entities sorgusunda kullandığımız kritere ait olan kaydın tabloda bulunup bulunmadığını nasıl anlarız  ve en sizin önerdiğiniz en doğru kullanım hangisidir ?

A) email.ToList().Count>0 B)email.Any() C) email.Count()>0 . . . . . . . . . . .

Şimdi 3 şıkkı da sırayla inceleyelim ve doğru cevabı bulmaya çalışalım. (Eğer ben doğru cevabı zaten biliyorum diyorsanız yazıyı okumadan doğru cevap için yazının son paragrafına bakabilirsiniz ;) )

email.ToList().Count>0

İlk olarak A şıkkındaki kullanımı deneyeceğiz ve Linq sorgumuzu aşağıdaki gibi değiştiriyor olacağız.
var IsExist = (from inc in entities.be_Settings                                
 where inc.SettingName == "email"                                
 select inc).ToList().Count > 0;

Şimdi Linq sorgusu  çalıştıralım ve SQL Profiler ile Linq sorgusu tarafından üretilen SQL sorgusunu inceleyelim.

SELECT [Extent1].[SettingName] AS [SettingName], [Extent1].[SettingValue] AS [SettingValue] FROM [dbo].[be_Settings] AS [Extent1] WHERE N'email' = [Extent1].[SettingName]

Yazdığımız Linq Sorgusunun ürettiği SQL sorgusu yukarıdaki gibi. Sorgunun çalışması sonucunda aldığımız sonuç ise bizim istediğimiz sonuç. Yani bu yöntem pratikte çalışıyor. Şimdi geçelim B şıkkına :)

email.Any()

B şıkkı için ise Linq sorgumuzu şu şekilde değiştiriyoruz.
var IsExist = (from inc in entities.be_Settings                                
 where inc.SettingName == "email"                                
 select inc).Any();
 

Linq sorgusunun ürettiği SQL sorgusu ile aşağıdaki gibi oluyor.

SELECT CASE WHEN ( EXISTS (SELECT 1 AS [C1] FROM [dbo].[be_Settings] AS [Extent1] WHERE N'email' = [Extent1].[SettingName] )) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[be_Settings] AS [Extent2] WHERE N'email' = [Extent2].[SettingName] )) THEN cast(0 as bit) END AS [C1] FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]

B şıkkında da sorgumuz pratikte amacına ulaşıyor ve sonucu başarılı bir şekilde elde ediyor.

Şimdi gelelim son olarak C şıkkına :)

email.Count()>0

Son şıkkımız olan C şıkkı için de Linq sorgumuzu şu şekilde değiştiriyoruz.
var IsExist = (from inc in entities.be_Settings                                
 where inc.SettingName == "email"                                
 select inc).Count()>0;
  Yukarıdaki Linq Sorgusunun ürettiği SQL Sorgusu ise aşağıdaki gibi oluyor. SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[be_Settings] AS [Extent1] WHERE N'email' = [Extent1].[SettingName] )  AS [GroupBy1]

Baktığımız zaman yine pratikte de bu sorgumuz doğru cevabı bize döndürdü ve başarıyla ilgili kaydın tabloda bulunup bulunmadığı öğrenebildik.

Peki ya Doğru Cevap :)

Yazının buraya kadar olan kısmını okuduğunuzda tüm cevapların doğru olduğunu düşünüyor olabilirsiniz. Pratikte doğrudur ama en doğru kullanım sadece tek bir şıkta mevcut. Neden mi ? Gelin şu şekilde başlayalım.

Sizden sorudaki işlemi bir SQL sorgusu ile yapmanızı istesem sizler nasıl yapardınız ? (Tabi yine en doğru şekilde)

IF EXISTS(SELECT * FROM be_Settings bs WHERE bs.SettingName='email') BEGIN --Gelecek işlemler END

Eminim çoğunuz yukarıdaki SQL sorgusunu yazacaksınızdır. Nitekim bu kullanım en doğru olan kullanımdır performans açısından. Bu konuyla ilgili kısa bir araştırma yaparsanız EXISTS kullanımının hız açısından ne kadar iyi olduğunu kısa sürede bulacaksınızdır. İlgili makalelerden birine şuradan erişebilirsiniz.

Evet bu nedenden dolayı SQL tarafında bu yöntem en performanslı olduğundan yukararıdaki soru için bizim Linq sorgumuzdan da bu şekilde bir SQL üretmesini beklememiz oldukça doğaldır. Sanırım bu noktadan sonra doğru cevabı tahmin edebiliyorsunuzdur. Ancak gelin sırayla 3 yöntemi de kısaca inceleyelim.

A şıkkındaki kullanıma baktığımızda en maliyetli kullanım olduğunu söyleyebiliriz. Çünkü select sorgusu ToList metoduyla çalıştırılmakta ve sorgu sonuçları belleğe alınarak buradan List’in eleman sayısına bakarak kaydın bulunup bulunmadığına bakılmakta. Örneğin eğer ilgili kriterlere sahipkayıt sizin tablonuzda 1000 tane var ise bu bin kayıt öncelikle database’den getirilip nesnelere çevrilmekte ve daha sonra bir List’in içerisine atılmakta ve siz sadece bu List’in eleman sayısını kontrol etmektesiniz. Bu nedenle en maliyetli ve en kaçınmamız gereken kullanım A şıkkında.  !!!

B şıkkı ise bize doğru sonucu en performanslı bir biçimde getiren kullanım. Çünkü Any metodu kullandığımızda sorgu “EXISTS” kullanımına çevrilmekte ve en doğru SQL oluşturulmaktadır.

Son şıkkımızda da aslında yanlış olan noktayı tahmin edebiliyorsunuzdur. Count ifadesi Exists ifadesine göre yukarıda paylaştığım makaleyi baz alarak konuşursak yaklaşık 250 kez daha yavaş olduğu için doğru kullanım olmamakta.

Sonuç olarak baktığımızda aslında tüm kullanımlar bizlere doğru sonucu getirmekte. Ancak performans açısından baktığımızda tek bir cevap bulunmakta. O da B şıkkı. Yazılım geliştirirken belki de çoğu kez yanlış kullanımlar yüzünden performans kayıpları yaşayabiliyoruz. Önemli olan bu şekilde ufak kullanım detaylarını bilerek hem doğru kullanım alışkanlıkları kazanmak hem de performans açısından sorun yaşamayan uygulamalar geliştirmek. ;)

Umarım bu yazım sizler için faydalı olmuştur.

Görüşmek Üzere,



Yorum Gönder


Yorumlar

  • profile

    İlkay İlknur

    • 16
    • 12
    • 2015

    @Ilker Aynısını alıp koymadığın sürece sorun değil ;)

  • profile

    ilker yanizca

    • 10
    • 11
    • 2015

    Faydalı yazı için teşekkürler, benzer bir yazım için kaynak olarak kullanabilirim izniniz olursa :)

  • profile

    AbbA

    • 6
    • 1
    • 2014

    Açıklayıcı temiz bir yazı. Bir bakış açısı eklersek; Bazı durumlarda elimizdeki listenin sayısı ve elemanlarıda lazım olabiliyor. Budurumda en maliyetli sorugu en avantajlı ve kaçınılmaz sorgu halini alabiliyor.

  • profile

    Kerem Kat

    • 3
    • 2
    • 2012

    Merhabalar, Eski bir yazınız fakat B şıkkı üzerinde bir iyileştirme yapılabilir :) var IsExist = ((from inc in entities.be_Settings where inc.SettingName == "email" select inc).Any() ? 1 : 0) == 1; Sorgusu, WHERE cümleciğimizi aşağıdaki gibi bir hale getiriyor: WHERE ( 1 = ( CASE WHEN ( EXISTS (SELECT 1 AS [C1] FROM ... WHERE ... ) ) THEN 1 ELSE 0 END ) )

  • profile

    Tarık

    • 25
    • 11
    • 2011

    Yazı güzeldi.Bu gibi makalelerin artması ise daha da bi güzel. Daha önce bir kaç makalenizide okudum (Örn: Tuple) ve okuduğum makaleler gerçekten çok yalın ve sade anlatılmış.Çok uzun örneklendirme yapılmamış.Bu şekilde giderseniz yeni başlayankar için çok güzel olacaktır.