Aracılığıyla paylaş


EF 4, 5 ve 6 için performansla ilgili dikkat edilmesi gerekenler

David Obando, Eric Dettinger ve diğerleri tarafından

Yayımlanma Tarihi: Nisan 2012

Son güncelleştirme: Mayıs 2014


1. Giriş

Nesne-İlişkisel Eşleme çerçeveleri, nesne odaklı bir uygulamada veri erişimi için soyutlama sağlamanın kullanışlı bir yoludur. .NET uygulamaları için Microsoft'un önerdiği O/RM, Entity Framework'dür. Ancak herhangi bir soyutlama ile performans bir sorun haline gelebilir.

Bu teknik inceleme, Entity Framework kullanarak uygulama geliştirirken performansla ilgili dikkat edilmesi gereken noktaları göstermek, geliştiricilere performansı etkileyebilecek Entity Framework iç algoritmaları hakkında fikir vermek ve Entity Framework kullanan uygulamalarında araştırma ve performansı geliştirmeye yönelik ipuçları sağlamak amacıyla yazılmıştır. Web'de performansla ilgili bir dizi iyi konu vardır ve mümkün olduğunda bu kaynaklara işaret etmeye çalıştık.

Performans karmaşık bir konudur. Bu teknik inceleme, Entity Framework kullanan uygulamalarınız için performansla ilgili kararlar almak üzere bir kaynak olarak tasarlanmıştır. Performansı göstermek için bazı test ölçümleri dahil ettik, ancak bu ölçümler uygulamanızda göreceğiniz performansın mutlak göstergeleri olarak tasarlanmamıştır.

Pratik amaçlar doğrultusunda, bu belgede Entity Framework 4'ün .NET 4.0 altında, Entity Framework 5 ve 6'nın ise .NET 4.5 altında çalıştığı varsayılır. Entity Framework 5 için yapılan performans geliştirmelerinin çoğu .NET 4.5 ile birlikte gelen temel bileşenlerde yer alır.

Entity Framework 6 bant dışı bir sürümdür ve .NET ile birlikte gelen Entity Framework bileşenlerine bağımlı değildir. Entity Framework 6 hem .NET 4.0 hem de .NET 4.5 üzerinde çalışır ve .NET 4.0'dan yükseltme yapmamış ancak uygulamalarında en son Entity Framework bitlerini isteyenlere büyük bir performans avantajı sunabilir. Bu belgede Entity Framework 6'dan bahsedildiğinde, bu yazma sırasında kullanılabilen en son sürüme başvurur: sürüm 6.1.0.

2. Soğuk ve Sıcak Sorgu Yürütme karşılaştırması

Belirli bir modelde ilk kez sorgu yapıldığında Entity Framework arka planda modeli yüklemek ve doğrulamak için çok fazla çalışma yapar. Bu ilk sorguyu sık sık "soğuk" sorgu olarak adlandırıyoruz.  Zaten yüklenmiş bir modele yönelik diğer sorgular "sıcak" sorgular olarak bilinir ve çok daha hızlıdır.

Entity Framework'ü kullanarak sorgu yürütürken zamanın nereye harcandığı hakkında üst düzey bir görünüme sahip olalım ve Entity Framework 6'da işlerin nerede geliştiğini görelim.

İlk Sorgu Yürütme – soğuk sorgu

Kod Kullanıcısı Yazma İşlemleri Eylem EF4 Performans Etkisi EF5 Performans Etkisi EF6 Performans Etkisi
using(var db = new MyContext())
{
Bağlam oluşturma Orta Orta Düşük
var q1 =
from c in db.Customers
where c.Id == id1
select c;
Sorgu ifadesi oluşturma Düşük Düşük Düşük
var c1 = q1.First(); LINQ sorgu yürütme - Meta veri yükleme: Yüksek ancak önbelleğe alınmış
- Görünüm oluşturma: Potansiyel olarak çok yüksek ancak önbelleğe alınmış
- Parametre değerlendirmesi: Orta
- Sorgu çevirisi: Orta
- Malzemeleştirici oluşturma: Orta ama önbelleğe alınmış
- Veritabanı sorgu yürütmesi: Büyük olasılıkla yüksek
+ Bağlan ion. Açık
+ Command.ExecuteReader
+ DataReader.Read
Nesne gerçekleştirme: Orta
- Kimlik araması: Orta
- Meta veri yükleme: Yüksek ancak önbelleğe alınmış
- Görünüm oluşturma: Potansiyel olarak çok yüksek ancak önbelleğe alınmış
- Parametre değerlendirmesi: Düşük
- Sorgu çevirisi: Orta ancak önbelleğe alınmış
- Malzemeleştirici oluşturma: Orta ama önbelleğe alınmış
- Veritabanı sorgu yürütmesi: Potansiyel olarak yüksek (Bazı durumlarda daha iyi sorgular)
+ Bağlan ion. Açık
+ Command.ExecuteReader
+ DataReader.Read
Nesne gerçekleştirme: Orta
- Kimlik araması: Orta
- Meta veri yükleme: Yüksek ancak önbelleğe alınmış
- Görünüm oluşturma: Orta ama önbelleğe alınmış
- Parametre değerlendirmesi: Düşük
- Sorgu çevirisi: Orta ancak önbelleğe alınmış
- Malzemeleştirici oluşturma: Orta ama önbelleğe alınmış
- Veritabanı sorgu yürütmesi: Potansiyel olarak yüksek (Bazı durumlarda daha iyi sorgular)
+ Bağlan ion. Açık
+ Command.ExecuteReader
+ DataReader.Read
Nesne gerçekleştirme: Orta (EF5'ten daha hızlı)
- Kimlik araması: Orta
} Bağlan. Yakın Düşük Düşük Düşük

İkinci Sorgu Yürütme – sıcak sorgu

Kod Kullanıcısı Yazma İşlemleri Eylem EF4 Performans Etkisi EF5 Performans Etkisi EF6 Performans Etkisi
using(var db = new MyContext())
{
Bağlam oluşturma Orta Orta Düşük
var q1 =
from c in db.Customers
where c.Id == id1
select c;
Sorgu ifadesi oluşturma Düşük Düşük Düşük
var c1 = q1.First(); LINQ sorgu yürütme - Meta veri yükleme araması: Yüksek ancak önbelleğe alınmış Düşük
- Görünüm oluşturma araması: Potansiyel olarak çok yüksek ancak önbelleğe alınmış Düşük
- Parametre değerlendirmesi: Orta
- Sorgu çevirisi araması: Orta
- Malzeme oluşturucu oluşturma araması: Orta ancak önbelleğe alınmış Düşük
- Veritabanı sorgu yürütmesi: Büyük olasılıkla yüksek
+ Bağlan ion. Açık
+ Command.ExecuteReader
+ DataReader.Read
Nesne gerçekleştirme: Orta
- Kimlik araması: Orta
- Meta veri yükleme araması: Yüksek ancak önbelleğe alınmış Düşük
- Görünüm oluşturma araması: Potansiyel olarak çok yüksek ancak önbelleğe alınmış Düşük
- Parametre değerlendirmesi: Düşük
- Sorgu çevirisi araması: Orta ancak önbelleğe alınmış Düşük
- Malzeme oluşturucu oluşturma araması: Orta ancak önbelleğe alınmış Düşük
- Veritabanı sorgu yürütmesi: Potansiyel olarak yüksek (Bazı durumlarda daha iyi sorgular)
+ Bağlan ion. Açık
+ Command.ExecuteReader
+ DataReader.Read
Nesne gerçekleştirme: Orta
- Kimlik araması: Orta
- Meta veri yükleme araması: Yüksek ancak önbelleğe alınmış Düşük
- Görünüm oluşturma araması: Orta ancak önbelleğe alınmış Düşük
- Parametre değerlendirmesi: Düşük
- Sorgu çevirisi araması: Orta ancak önbelleğe alınmış Düşük
- Malzeme oluşturucu oluşturma araması: Orta ancak önbelleğe alınmış Düşük
- Veritabanı sorgu yürütmesi: Potansiyel olarak yüksek (Bazı durumlarda daha iyi sorgular)
+ Bağlan ion. Açık
+ Command.ExecuteReader
+ DataReader.Read
Nesne gerçekleştirme: Orta (EF5'ten daha hızlı)
- Kimlik araması: Orta
} Bağlan. Yakın Düşük Düşük Düşük

Hem soğuk hem de sıcak sorguların performans maliyetini azaltmanın çeşitli yolları vardır ve aşağıdaki bölümde bunlara göz atacağız. Özellikle, önceden oluşturulmuş görünümleri kullanarak soğuk sorgularda model yükleme maliyetini azaltmayı ele alacağız. Bu, görünüm oluşturma sırasında karşılaşılan performans sorunlarını azaltmaya yardımcı olacaktır. Sıcak sorgular için sorgu planı önbelleğe alma, izleme sorgusu yok ve farklı sorgu yürütme seçeneklerini ele alacağız.

2.1 Görünüm Oluşturma nedir?

Görünüm oluşturmanın ne olduğunu anlamak için öncelikle "Eşleme Görünümleri"nin ne olduğunu anlamamız gerekir. Eşleme Görünümleri, her varlık kümesi ve ilişkilendirme için eşlemede belirtilen dönüştürmelerin yürütülebilir gösterimleridir. Dahili olarak, bu eşleme görünümleri CQT'ler (kurallı sorgu ağaçları) şeklini alır. İki tür eşleme görünümü vardır:

  • Sorgu görünümleri: Bunlar, veritabanı şemasından kavramsal modele gitmek için gereken dönüşümü temsil edebilir.
  • Güncelleştirme görünümleri: Bunlar kavramsal modelden veritabanı şemasına gitmek için gereken dönüştürmeyi temsil edebilir.

Kavramsal modelin veritabanı şemasından farklı şekillerde farklılık gösterebileceğini unutmayın. Örneğin, iki farklı varlık türünün verilerini depolamak için tek bir tablo kullanılabilir. Devralma ve önemsiz olmayan eşlemeler, eşleme görünümlerinin karmaşıklığında rol oynar.

Bu görünümleri eşleme belirtimine göre hesaplama işlemi, görünüm oluşturma olarak adlandırdığımız işlemdir. Görünüm oluşturma, bir model yüklendiğinde veya derleme zamanında "önceden oluşturulmuş görünümler" kullanılarak dinamik olarak gerçekleşebilir; ikincisi, C# veya VB dosyasına Entity SQL deyimleri biçiminde serileştirilir.

Görünümler oluşturulduğunda, bunlar da doğrulanır. Performans açısından bakıldığında, görünüm oluşturma maliyetinin büyük çoğunluğu aslında varlıklar arasındaki bağlantıların anlamlı olmasını ve desteklenen tüm işlemler için doğru kardinaliteye sahip olmasını sağlayan görünümlerin doğrulanmasıdır.

Bir varlık kümesi üzerindeki bir sorgu yürütülürse, sorgu ilgili sorgu görünümüyle birleştirilir ve bu oluşturmanın sonucu plan derleyicisi aracılığıyla çalıştırılarak arka plan deposunun anlayabileceği sorgunun gösterimi oluşturulur. SQL Server için bu derlemenin nihai sonucu bir T-SQL SELECT deyimi olacaktır. Bir varlık kümesi üzerinde ilk kez bir güncelleştirme gerçekleştirildiğinde, güncelleştirme görünümü benzer bir işlemle çalıştırılır ve hedef veritabanı için DML deyimlerine dönüştürülür.

2.2 Görünüm Oluşturma performansını etkileyen faktörler

Görünüm oluşturma adımının performansı yalnızca modelinizin boyutuna değil, aynı zamanda modelin ne kadar birbirine bağlı olduğuna da bağlıdır. İki Varlık bir devralma zinciri veya İlişkilendirme aracılığıyla bağlıysa, bunların bağlı olduğu söylenir. Benzer şekilde, iki tablo bir yabancı anahtar aracılığıyla bağlanırsa, bunlar bağlanır. Şemalarınızdaki bağlı Varlıkların ve tabloların sayısı arttıkça görünüm oluşturma maliyeti artar.

Görünümleri oluşturmak ve doğrulamak için kullandığımız algoritma en kötü durumda üsteldir, ancak bunu geliştirmek için bazı iyileştirmeler kullanırız. Performansı olumsuz etkilediği görünen en büyük faktörler şunlardır:

  • Model boyutu, varlık sayısına ve bu varlıklar arasındaki ilişkilendirme miktarına başvurur.
  • Model karmaşıklığı, özellikle çok sayıda türü içeren devralma.
  • Yabancı Anahtar İlişkilendirmeleri yerine Bağımsız İlişkilendirmeler kullanma.

Küçük ve basit modeller için maliyet, önceden oluşturulmuş görünümleri kullanma zahmetine neden olmayacak kadar küçük olabilir. Model boyutu ve karmaşıklığı arttıkça, görünüm oluşturma ve doğrulama maliyetini azaltmak için çeşitli seçenekler mevcuttur.

2.3 Model yükleme süresini azaltmak için Önceden Oluşturulmuş Görünümleri Kullanma

Entity Framework 6'da önceden oluşturulmuş görünümleri kullanma hakkında ayrıntılı bilgi için Önceden Oluşturulmuş Eşleme Görünümleri'ni ziyaret edin

2.3.1 Entity Framework Power Tools Community Edition kullanılarak Önceden Oluşturulmuş görünümler

Entity Framework 6 Power Tools Community Edition'ı kullanarak model sınıfı dosyasına sağ tıklayıp Entity Framework menüsünü kullanarak "Görünüm Oluştur" seçeneğini belirleyerek EDMX ve Code First modellerinin görünümlerini oluşturabilirsiniz. Entity Framework Power Tools Community Edition yalnızca DbContext türetilmiş bağlamlarda çalışır.

2.3.2 Önceden oluşturulmuş görünümleri EDMGen tarafından oluşturulan bir modelle kullanma

EDMGen, .NET ile birlikte gelen ve Entity Framework 4 ve 5 ile çalışan ancak Entity Framework 6 ile çalışmayan bir yardımcı programdır. EDMGen, komut satırından bir model dosyası, nesne katmanı ve görünümler oluşturmanıza olanak tanır. Çıkışlardan biri, tercihiniz olan VB veya C# dilinde bir Görünümler dosyası olacaktır. Bu, her varlık kümesi için Entity SQL kod parçacıklarını içeren bir kod dosyasıdır. Önceden oluşturulmuş görünümleri etkinleştirmek için dosyayı projenize eklemeniz yeterlidir.

Modelin şema dosyalarında el ile düzenlemeler yaparsanız görünüm dosyasını yeniden oluşturmanız gerekir. Bunu yapmak için EDMGen'i /mode:ViewGeneration bayrağıyla çalıştırabilirsiniz.

2.3.3 Önceden Oluşturulmuş Görünümleri EDMX dosyasıyla kullanma

Bir EDMX dosyası için görünümler oluşturmak için de EDMGen kullanabilirsiniz. Daha önce başvurulan MSDN konusu, bunu yapmak için derleme öncesi olay eklemeyi açıklar; ancak bu karmaşıktır ve mümkün olmayan bazı durumlar vardır. Modeliniz bir edmx dosyasındayken görünümleri oluşturmak için T4 şablonu kullanmak genellikle daha kolaydır.

ADO.NET ekip blogu, görüntüleme oluşturma ( <https://learn.microsoft.com/archive/blogs/adonet/how-to-use-a-t4-template-for-view-generation>) için T4 şablonunun nasıl kullanılacağını açıklayan bir gönderiye sahiptir. Bu gönderi, indirilebilen ve projenize eklenebilen bir şablon içerir. Şablon, Entity Framework'ün ilk sürümü için yazılmıştır, bu nedenle Entity Framework'ün en son sürümleriyle çalışmaları garanti değildir. Ancak, Visual Studio Galerisi'nden Entity Framework 4 ve 5 için daha güncel bir görünüm oluşturma şablonları kümesi indirebilirsiniz:

  • VB.NET: <http://visualstudiogallery.msdn.microsoft.com/118b44f2-1b91-4de2-a584-7a680418941d>
  • C#: <http://visualstudiogallery.msdn.microsoft.com/ae7730ce-ddab-470f-8456-1b313cd2c44d>

Entity Framework 6 kullanıyorsanız, konumundaki Visual Studio Galerisi'nden <http://visualstudiogallery.msdn.microsoft.com/18a7db90-6705-4d19-9dd1-0a6c23d0751f>görünüm oluşturma T4 şablonlarını alabilirsiniz.

2.4 Görünüm oluşturma maliyetini azaltma

Önceden oluşturulmuş görünümlerin kullanılması, görünüm oluşturma maliyetini model yüklemesinden (çalışma süresi) tasarım süresine taşır. Bu, çalışma zamanında başlatma performansını geliştirse de, geliştirirken görüntüleme oluşturmanın acısını yaşamaya devam edebilirsiniz. Hem derleme zamanında hem de çalışma zamanında görünüm oluşturma maliyetini azaltmaya yardımcı olabilecek birkaç ek püf noktası vardır.

2.4.1 Görünüm oluşturma maliyetini azaltmak için Yabancı Anahtar İlişkilendirmelerini kullanma

Modeldeki ilişkilendirmelerin Bağımsız İlişkilendirmelerden Yabancı Anahtar İlişkilendirmelerine geçirilmesinin görünüm oluşturmada harcanan süreyi önemli ölçüde artırdığı bir dizi durum gördük.

Bu geliştirmeyi göstermek için EDMGen kullanarak Navision modelinin iki sürümünü oluşturduk. Not: Navision modelinin açıklaması için ek C'ye bakın. Navision modeli, çok büyük miktarda varlığı ve aralarındaki ilişkiler nedeniyle bu alıştırma için ilgi çekicidir.

Bu çok büyük modelin bir sürümü Yabancı Anahtar İlişkilendirmeleri ve diğeri Bağımsız İlişkilendirmeler ile oluşturulmuştur. Ardından her model için görünümlerin oluşturulmasının ne kadar sürdüğünü zamanladık. Entity Framework 5 testi görünümleri oluşturmak için EntityViewGenerator sınıfından GenerateViews() yöntemini kullanırken, Entity Framework 6 testi Depolama MappingItemCollection sınıfından GenerateViews() yöntemini kullandı. Bunun nedeni Entity Framework 6 kod tabanında oluşan kod yeniden yapılandırmasıdır.

Entity Framework 5'i kullanarak, yabancı anahtarlara sahip model için görüntüleme oluşturma işlemi laboratuvar makinesinde 65 dakika sürdü. Bağımsız ilişkilendirmeler kullanan modelin görünümlerini oluşturmanın ne kadar süreceği bilinmiyor. Aylık güncelleştirmeleri yüklemek için makine laboratuvarımızda yeniden başlatılmadan önce testi bir aydan uzun süre çalışır durumda bıraktık.

Entity Framework 6'yı kullanarak yabancı anahtarlara sahip model için görüntüleme oluşturma işlemi aynı laboratuvar makinesinde 28 saniye sürdü. Bağımsız İlişkilendirmeler kullanan model için görüntüleme oluşturma işlemi 58 saniye sürdü. Entity Framework 6'da görünüm oluşturma kodu üzerinde yapılan iyileştirmeler, birçok projenin daha hızlı başlangıç süreleri elde etmek için önceden oluşturulmuş görünümlere gerek duymayacağından kaynaklanır.

Entity Framework 4 ve 5'te önceden oluşturulan görünümlerin EDMGen veya Entity Framework Power Tools ile yapılabilmesini sağlamak önemlidir. Entity Framework 6 için görünüm oluşturma, Entity Framework Power Tools aracılığıyla veya Önceden Oluşturulmuş Eşleme Görünümlerinde açıklandığı gibi program aracılığıyla yapılabilir.

2.4.1.1 Bağımsız İlişkilendirmeler Yerine Yabancı Anahtarları Kullanma

Visual Studio'da EDMGen veya Entity Tasarım Aracı kullanırken varsayılan olarak FK'leri alırsınız ve FK'ler ile IA'lar arasında geçiş yapmak için yalnızca tek bir onay kutusu veya komut satırı bayrağı alır.

Büyük bir İlk Kod modeliniz varsa, Bağımsız İlişkilendirmeler'i kullanmak görünüm oluşturma üzerinde aynı etkiye sahip olur. Bağımlı nesnelerinizin sınıflarına Yabancı Anahtar özelliklerini ekleyerek bu etkiyi önleyebilirsiniz, ancak bazı geliştiriciler bunu nesne modellerini kirletiyor olarak değerlendirir. Bu konu hakkında daha fazla bilgiyi içinde <http://blog.oneunicorn.com/2011/12/11/whats-the-deal-with-mapping-foreign-keys-using-the-entity-framework/>bulabilirsiniz.

Kullanırken Bunu yapın
Varlık Tasarım Aracı İki varlık arasına bir ilişkilendirme ekledikten sonra, bilgi sınırlamanız olduğundan emin olun. Bilgi kısıtlamaları Entity Framework'e Bağımsız İlişkilendirmeler yerine Yabancı Anahtarlar kullanmasını söyler. Ek ayrıntılar için adresini ziyaret edin <https://learn.microsoft.com/archive/blogs/efdesign/foreign-keys-in-the-entity-framework>.
EDMGen Dosyalarınızı veritabanından oluşturmak için EDMGen kullanırken, Yabancı Anahtarlarınıza saygı gösterilir ve modele bu şekilde eklenir. EDMGen tarafından sunulan farklı seçenekler hakkında daha fazla bilgi için adresini ziyaret edin http://msdn.microsoft.com/library/bb387165.aspx.
Önce KodLa Code First kullanılırken bağımlı nesnelere yabancı anahtar özelliklerini ekleme hakkında bilgi için Code First Conventions konusunun "İlişki Kuralı" bölümüne bakın.

2.4.2 Modelinizi ayrı bir derlemeye taşıma

Modeliniz doğrudan uygulamanızın projesine dahil edildiğinde ve bir derleme öncesi olay veya T4 şablonu aracılığıyla görünümler oluşturduğunuzda, model değiştirilmemiş olsa bile proje yeniden oluşturulduğunda görüntüleme oluşturma ve doğrulama gerçekleştirilir. Modeli ayrı bir derlemeye taşır ve uygulamanızın projesinden başvurursanız, modeli içeren projeyi yeniden oluşturmanıza gerek kalmadan uygulamanızda başka değişiklikler yapabilirsiniz.

Not: Modelinizi ayrı derlemelere taşırken modelin bağlantı dizesi istemci projesinin uygulama yapılandırma dosyasına kopyalamayı unutmayın.

2.4.3 edmx tabanlı bir modelin doğrulamayı devre dışı bırakma

EDMX modelleri, model değişmemiş olsa bile derleme zamanında doğrulanır. Modeliniz zaten doğrulanmışsa, özellikler penceresinde "Derlemede Doğrula" özelliğini false olarak ayarlayarak derleme zamanında doğrulamayı gizleyebilirsiniz. Eşlemenizi veya modelinizi değiştirdiğinizde, değişikliklerinizi doğrulamak için doğrulamayı geçici olarak yeniden etkinleştirebilirsiniz.

Entity Framework 6 için Entity Framework Tasarım Aracı performans iyileştirmeleri yapıldığını ve "Derlemede Doğrula" maliyetinin tasarımcının önceki sürümlerinden çok daha düşük olduğunu unutmayın.

Entity Framework'te 3 Önbelleğe Alma

Entity Framework aşağıdaki yerleşik önbelleğe alma biçimlerine sahiptir:

  1. Nesne önbelleğe alma – Bir ObjectContext örneğinde yerleşik olarak bulunan ObjectStateManager, bu örnek kullanılarak alınan nesnelerin belleğinde izler. Bu, birinci düzey önbellek olarak da bilinir.
  2. Sorgu Planı Önbelleğe Alma: Bir sorgu birden çok kez yürütülürken oluşturulan depo komutunu yeniden kullanma.
  3. Meta verileri önbelleğe alma - modelin meta verilerini aynı modele yapılan farklı bağlantılarda paylaşma.

EF'in kullanıma hazır olarak sağladığı önbelleklerin yanı sıra, Entity Framework'ün ikinci düzey önbelleğe alma olarak da bilinen veritabanından alınan sonuçlar için önbellekle genişletilmesi için sarmalama sağlayıcısı olarak bilinen özel bir ADO.NET veri sağlayıcısı da kullanılabilir.

3.1 Nesne Önbelleğe Alma

Varsayılan olarak, ef sorgunun sonuçlarında bir varlık döndürülürse, EF bunu gerçekleştirmeden hemen önce ObjectContext aynı anahtara sahip bir varlığın ObjectStateManager'ına zaten yüklenip yüklenmediğini denetler. Aynı anahtarlara sahip bir varlık zaten varsa, EF bunu sorgunun sonuçlarına ekler. EF sorguyu veritabanına karşı yine de verir, ancak bu davranış varlığı birden çok kez gerçekleştirme maliyetinin çoğunu atlayabilir.

3.1.1 DbContext Bul kullanarak nesne önbelleğinden varlık alma

Normal sorgudan farklı olarak, DbSet'teki Find yöntemi (EF 4.1'de ilk kez dahil edilen API'ler), sorguyu veritabanına karşı vermeden önce bellekte bir arama gerçekleştirir. İki farklı ObjectContext örneğinin iki farklı ObjectStateManager örneğine sahip olacağını, yani ayrı nesne önbelleklerine sahip olduklarını unutmayın.

Bul, bağlama göre izlenen bir varlığı bulmaya çalışmak için birincil anahtar değerini kullanır. Varlık bağlamda değilse, sorgu yürütülür ve veritabanında değerlendirilir ve varlık bağlamda veya veritabanında bulunmazsa null döndürülür. Bul'un bağlama eklenmiş ancak henüz veritabanına kaydedilmemiş varlıkları da döndürdüğünü unutmayın.

Bul kullanılırken performans konusunda dikkat edilmesi gereken bir nokta vardır. Bu yönteme yapılan çağrılar, veritabanında işlemeyi bekleyen değişiklikleri algılamak için varsayılan olarak nesne önbelleğinin doğrulanmasına neden olur. Nesne önbelleğinde veya nesne önbelleğine büyük bir nesne grafında çok fazla sayıda nesne ekleniyorsa bu işlem çok pahalı olabilir, ancak devre dışı bırakılabilir. Bazı durumlarda, değişiklikleri otomatik olarak algılamayı devre dışı bırakırken Find yöntemini çağırmada büyük bir fark sırası algılayabilirsiniz. Ancak ikinci bir büyüklük sırası, nesne aslında önbellekte olduğunda algılanır ve nesnenin veritabanından alınması gerektiğinde algılanır. Burada, 5000 varlık yüküyle milisaniye cinsinden ifade edilen bazı mikrobenchmark'larımız kullanılarak alınan ölçümleri içeren örnek bir grafik verilmiştir:

.NET 4.5 logarithmic scale

Değişiklikleri otomatik algılama devre dışı bırakılmış bul örneği:

    context.Configuration.AutoDetectChangesEnabled = false;
    var product = context.Products.Find(productId);
    context.Configuration.AutoDetectChangesEnabled = true;
    ...

Find yöntemini kullanırken dikkate almanız gerekenler:

  1. Nesne önbellekte değilse Bul'un avantajları olumsuzlanır, ancak söz dizimi yine de anahtara göre sorgudan daha basittir.
  2. Değişiklikleri otomatik algılama etkinleştirildiyse Find yönteminin maliyeti, modelinizin karmaşıklığı ve nesne önbelleğinizdeki varlık miktarına bağlı olarak bir büyüklük sırası kadar artabilir.

Ayrıca, Bul'un yalnızca aradığınız varlığı döndürdüğünü ve nesne önbelleğinde değilse ilişkili varlıklarını otomatik olarak yüklemediğini unutmayın. İlişkili varlıkları almanız gerekiyorsa, istekli yükleme ile anahtara göre bir sorgu kullanabilirsiniz. Daha fazla bilgi için bkz . 8.1 Gecikmeli Yükleme ve İstekli Yükleme.

3.1.2 Nesne önbelleğinde birçok varlık olduğunda performans sorunları

Nesne önbelleği, Entity Framework'ün genel yanıt hızını artırmaya yardımcı olur. Ancak, nesne önbelleğinde çok büyük miktarda varlık yüklü olduğunda, Ekle, Kaldır, Bul, Giriş, SaveChanges ve daha fazlası gibi belirli işlemleri etkileyebilir. Özellikle DetectChanges çağrısı tetikleyen işlemler çok büyük nesne önbelleklerinden olumsuz etkilenir. DetectChanges nesne grafını nesne durumu yöneticisiyle eşitler ve performansı doğrudan nesne grafiğinin boyutuna göre belirlenir. DetectChanges hakkında daha fazla bilgi için bkz . POCO Varlıklarında Değişiklikleri İzleme.

Entity Framework 6 kullanırken, geliştiriciler addrange ve RemoveRange'i bir koleksiyonda yinelemek ve örnek başına bir kez Add çağrısı yapmak yerine doğrudan bir DbSet üzerinde çağırabiliyor. Aralık yöntemlerini kullanmanın avantajı, DetectChanges maliyetinin eklenen her varlık için bir kez yerine varlık kümesinin tamamı için yalnızca bir kez ödenmesidir.

3.2 Sorgu Planı Önbelleğe Alma

Sorgu ilk kez yürütülürken iç plan derleyicisi aracılığıyla kavramsal sorguyu depo komutuna (örneğin, SQL Server'a karşı çalıştırıldığında yürütülen T-SQL) çevirmek için kullanılır.  Sorgu planı önbelleğe alma etkinleştirilirse, sorgunun bir sonraki yürütülmesinde depo komutu doğrudan yürütme için sorgu planı önbelleğinden alınır ve plan derleyicisi atlanır.

Sorgu planı önbelleği, aynı AppDomain içindeki ObjectContext örnekleri arasında paylaşılır. Sorgu planı önbelleğe alma özelliğinden yararlanmak için ObjectContext örneğini tutmanız gerekmez.

3.2.1 Sorgu Planı Önbelleğe Alma hakkında bazı notlar

  • Sorgu planı önbelleği tüm sorgu türleri için paylaşılır: Entity SQL, LINQ to Entities ve CompiledQuery nesneleri.
  • Varsayılan olarak sorgu planı önbelleğe alma, EntityCommand veya ObjectQuery aracılığıyla yürütülen Entity SQL sorguları için etkinleştirilir. .NET 4.5 ve Entity Framework 6'da Entity Framework'te LINQ to Entities sorguları için de varsayılan olarak etkinleştirilir
    • Sorgu planı önbelleğe alma, EnablePlan Önbelleğe Alma özelliği (EntityCommand veya ObjectQuery'de) false olarak ayarlanarak devre dışı bırakılabilir. Örnek:
                    var query = from customer in context.Customer
                                where customer.CustomerId == id
                                select new
                                {
                                    customer.CustomerId,
                                    customer.Name
                                };
                    ObjectQuery oQuery = query as ObjectQuery;
                    oQuery.EnablePlanCaching = false;
  • Parametreli sorgular için parametrenin değerini değiştirmek yine de önbelleğe alınmış sorguya isabet eder. Ancak parametre modellerinin değiştirilmesi (örneğin, boyut, duyarlık veya ölçek) önbellekte farklı bir girişe isabet eder.
  • Entity SQL kullanılırken sorgu dizesi anahtarın bir parçasıdır. Sorgunun değiştirilmesi, sorgular işlevsel olarak eşdeğer olsa bile farklı önbellek girdilerine neden olur. Bu, büyük/küçük harf veya boşluk değişikliklerini içerir.
  • LINQ kullanılırken sorgu, anahtarın bir bölümünü oluşturmak için işlenir. Bu nedenle LINQ ifadesinin değiştirilmesi farklı bir anahtar oluşturur.
  • Diğer teknik sınırlamalar geçerli olabilir; Daha fazla ayrıntı için bkz. Otomatik Olarak Derlenen Sorgular.

3.2.2 Önbellek çıkarma algoritması

İç algoritmanın nasıl çalıştığını anlamak, sorgu planı önbelleğini ne zaman etkinleştirmeniz veya devre dışı bırakmanız gerektiğini anlamanıza yardımcı olur. Temizleme algoritması aşağıdaki gibidir:

  1. Önbellek belirli sayıda girdi (800) içerdiğinde, önbelleği düzenli aralıklarla (dakikada bir kez) süpüren bir zamanlayıcı başlatırız.
  2. Önbellek süpürme işlemleri sırasında, girişler bir LFRU (En az sık – son kullanılan) temelinde önbellekten kaldırılır. Bu algoritma, hangi girişlerin çıkarıldığına karar verirken hem isabet sayısını hem de yaşı dikkate alır.
  3. Her önbellek taramasının sonunda önbellek yine 800 girdi içerir.

Çıkarılması gereken girdiler belirlenirken tüm önbellek girdileri eşit olarak değerlendirilir. Bu, Bir CompiledQuery için store komutunun, Entity SQL sorgusunun store komutuyla aynı çıkarma olasılığına sahip olduğu anlamına gelir.

Önbellekte 800 varlık olduğunda önbellek çıkarma zamanlayıcısının başlatıldığını, ancak bu süreölçer başlatıldıktan yalnızca 60 saniye sonra önbellek süpürüldüğünü unutmayın. Bu, önbelleğinizin 60 saniyeye kadar büyüyebileceği anlamına gelir.

3.2.3 Sorgu planı önbelleğe alma performansını gösteren Test Ölçümleri

Sorgu planı önbelleğe alma işleminin uygulamanızın performansı üzerindeki etkisini göstermek için Navision modeline karşı bir dizi Entity SQL sorgusu yürüttüğünüz bir test gerçekleştirdik. Navision modelinin açıklaması ve yürütülen sorgu türleri için eke bakın. Bu testte ilk olarak sorgu listesinde yinelenir ve önbelleğe eklemek için her birini bir kez yürütüriz (önbelleğe alma etkinse). Bu adım iyileştirilmemiştir. Ardından, önbellek süpürme işleminin gerçekleşmesini sağlamak için ana iş parçacığını 60 saniye boyunca uyku moduna alacağız; son olarak, önbelleğe alınmış sorguları yürütmek için listede 2. kez yinelenir. Ayrıca, her sorgu kümesi yürütülmeden önce SQL Server plan önbelleği temizlenerek elde ettiğimiz sürelerin sorgu planı önbelleği tarafından sağlanan avantajı doğru yansıtması sağlanır.

3.2.3.1 Test Sonuçları
Test etme EF5 önbellek yok EF5 önbelleğe alınmış EF6 önbellek yok EF6 önbelleğe alınmış
Tüm 18723 sorgularını numaralandırma 124 125.4 124.3 125.3
Süpürmeden kaçınma (karmaşıklıktan bağımsız olarak yalnızca ilk 800 sorgu) 41.7 5.5 40.5 5,4
Yalnızca AggregatingSubtotals sorguları (toplam 178 - süpürme işlemini önler) 39.5 4,5 38.1 4.6

Saniyeler içinde her zaman.

Ahlaki : Çok sayıda farklı sorgu yürütürken (örneğin, dinamik olarak oluşturulan sorgular), önbelleğe alma işe yaramaz ve önbelleğin boşaltılması, plan önbelleğinden en iyi şekilde yararlanabilecek sorguların gerçekten kullanmasını engelleyebilir.

AggregatingSubtotals sorguları, test ettiğimiz sorguların en karmaşıkıdır. Beklendiği gibi, sorgu ne kadar karmaşık olursa, sorgu planı önbelleğe alma özelliğinden o kadar fazla avantaj elde edebilirsiniz.

CompiledQuery, planı önbelleğe alınmış bir LINQ sorgusu olduğundan, Bir CompiledQuery ile eşdeğer Entity SQL sorgusu karşılaştırması benzer sonuçlara sahip olmalıdır. Aslında, bir uygulamanın çok sayıda dinamik Entity SQL sorgusu varsa, önbelleği sorgularla doldurmak da önbellekten boşaltıldıklarında CompiledQueries'in "kaynak koda dönüştürülmesini" etkili bir şekilde neden olur. Bu senaryoda, CompiledQueries'e öncelik vermek için dinamik sorgularda önbelleğe alma devre dışı bırakılarak performans geliştirilebilir. Elbette daha da iyisi, uygulamayı dinamik sorgular yerine parametreli sorguları kullanmak üzere yeniden yazmak olacaktır.

3.3 LINQ sorgularıyla performansı geliştirmek için CompiledQuery kullanma

Testlerimiz, CompiledQuery kullanmanın otomatik olarak derlenmiş LINQ sorgularından %7'lik bir avantaj getirebileceğini gösteriyor; Bu, Entity Framework yığınından kod yürütmeye %7 daha az zaman harcayabileceğiniz anlamına gelir; uygulamanızın %7 daha hızlı olacağı anlamına gelmez. Genel olarak, EF 5.0'da CompiledQuery nesnelerini yazmanın ve korumanın maliyeti, avantajlarıyla karşılaştırıldığında sorunlara değmeyebilir. Mesafeniz farklılık gösterebilir, bu nedenle projeniz ek gönderim gerektiriyorsa bu seçeneği kullanın. CompiledQueries'in yalnızca ObjectContext türetilmiş modellerle uyumlu olduğunu ve DbContext türetilmiş modellerle uyumlu olmadığını unutmayın.

Bir CompiledQuery oluşturma ve çağırma hakkında daha fazla bilgi için bkz . Derlenmiş Sorgular (LINQ to Entities).

CompiledQuery kullanırken dikkate almanız gereken iki nokta vardır: statik örnekleri kullanma gereksinimi ve bunların birleştirilebilirlikle ilgili sorunları. Burada, bu iki önemli noktanın ayrıntılı bir açıklaması aşağıda açıklanmıştır.

3.3.1 Statik CompiledQuery örneklerini kullanma

LINQ sorgusu derlemek zaman alan bir işlem olduğundan, veritabanından veri getirmemiz gerektiğinde bunu yapmak istemeyiz. CompiledQuery örnekleri bir kez derlemenize ve birden çok kez çalıştırmanıza olanak tanır, ancak tekrar tekrar derlemek yerine her seferinde aynı CompiledQuery örneğini yeniden kullanmak için dikkatli olmanız ve temin olmanız gerekir. CompiledQuery örneklerini depolamak için statik üyelerin kullanılması gerekli hale gelir; aksi takdirde herhangi bir avantaj görmezsiniz.

Örneğin, sayfanızın seçili kategoriye ait ürünleri görüntülemeyi işlemek için aşağıdaki yöntem gövdesine sahip olduğunu varsayalım:

    // Warning: this is the wrong way of using CompiledQuery
    using (NorthwindEntities context = new NorthwindEntities())
    {
        string selectedCategory = this.categoriesList.SelectedValue;

        var productsForCategory = CompiledQuery.Compile<NorthwindEntities, string, IQueryable<Product>>(
            (NorthwindEntities nwnd, string category) =>
                nwnd.Products.Where(p => p.Category.CategoryName == category)
        );

        this.productsGrid.DataSource = productsForCategory.Invoke(context, selectedCategory).ToList();
        this.productsGrid.DataBind();
    }

    this.productsGrid.Visible = true;

Bu durumda, yöntem her çağrıldığında anında yeni bir CompiledQuery örneği oluşturacaksınız. Sorgu planı önbelleğinden depo komutunu alarak performans avantajlarını görmek yerine, CompiledQuery her yeni örnek oluşturulduğunda plan derleyicisinden geçer. Aslında, yöntem her çağrıldığında sorgu planı önbelleğinizi yeni bir CompiledQuery girişiyle kirletiyor olacaksınız.

Bunun yerine, derlenmiş sorgunun statik bir örneğini oluşturmak istiyorsunuz, bu nedenle yöntemi her çağrıldığında aynı derlenmiş sorguyu çağırırsınız. Bunun bir yolu, CompiledQuery örneğini nesne bağlamınızın bir üyesi olarak eklemektir.  Ardından, bir yardımcı yöntemi aracılığıyla CompiledQuery'ye erişerek işleri biraz daha temiz hale getirebilirsiniz:

    public partial class NorthwindEntities : ObjectContext
    {
        private static readonly Func<NorthwindEntities, string, IEnumerable<Product>> productsForCategoryCQ = CompiledQuery.Compile(
            (NorthwindEntities context, string categoryName) =>
                context.Products.Where(p => p.Category.CategoryName == categoryName)
            );

        public IEnumerable<Product> GetProductsForCategory(string categoryName)
        {
            return productsForCategoryCQ.Invoke(this, categoryName).ToList();
        }

Bu yardımcı yöntem aşağıdaki gibi çağrılabilir:

    this.productsGrid.DataSource = context.GetProductsForCategory(selectedCategory);

3.3.2 CompiledQuery Üzerinden Oluşturma

Herhangi bir LINQ sorgusu üzerinde oluşturma özelliği son derece kullanışlıdır; bunu yapmak için, IQueryable'ın ardından Skip() veya Count() gibi bir yöntemi çağırmanız yeterlidir. Ancak, bunu yapmak temelde yeni bir IQueryable nesnesi döndürür. Teknik olarak Bir CompiledQuery üzerinden oluşturmanızı durduracak hiçbir şey olmasa da, bunu yapmak plan derleyicisinden yeniden geçmeyi gerektiren yeni bir IQueryable nesnesi oluşturulmasına neden olur.

Bazı bileşenler, gelişmiş işlevselliği etkinleştirmek için oluşturulan IQueryable nesnelerini kullanır. Örneğin, ASP.NET'in GridView'ı SelectMethod özelliği aracılığıyla bir IQueryable nesnesine veri bağlanabilir. GridView daha sonra bu IQueryable nesnesi üzerinde oluşturarak veri modeli üzerinde sıralamaya ve sayfalandırmaya izin verir. Gördüğünüz gibi, GridView için Bir CompiledQuery kullanılması derlenen sorguya isabet etmese de yeni bir otomatik derlenmiş sorgu oluşturur.

Sorguya aşamalı filtreler eklerken bu durumla karşılaşabilirsiniz. Örneğin, isteğe bağlı filtreler için birkaç açılan liste içeren bir Müşteriler sayfanız olduğunu varsayalım (örneğin, Country ve OrdersCount). Bu filtreleri, Bir CompiledQuery'nin IQueryable sonuçları üzerinden oluşturabilirsiniz, ancak bunu yaptığınızda yeni sorgunun plan derleyicisinde her yürütürken geçmesine neden olur.

    using (NorthwindEntities context = new NorthwindEntities())
    {
        IQueryable<Customer> myCustomers = context.InvokeCustomersForEmployee();

        if (this.orderCountFilterList.SelectedItem.Value != defaultFilterText)
        {
            int orderCount = int.Parse(orderCountFilterList.SelectedValue);
            myCustomers = myCustomers.Where(c => c.Orders.Count > orderCount);
        }

        if (this.countryFilterList.SelectedItem.Value != defaultFilterText)
        {
            myCustomers = myCustomers.Where(c => c.Address.Country == countryFilterList.SelectedValue);
        }

        this.customersGrid.DataSource = myCustomers;
        this.customersGrid.DataBind();
    }

 Bu yeniden derlemeyi önlemek için, olası filtreleri hesaba katmak için CompiledQuery'yi yeniden yazabilirsiniz:

    private static readonly Func<NorthwindEntities, int, int?, string, IQueryable<Customer>> customersForEmployeeWithFiltersCQ = CompiledQuery.Compile(
        (NorthwindEntities context, int empId, int? countFilter, string countryFilter) =>
            context.Customers.Where(c => c.Orders.Any(o => o.EmployeeID == empId))
            .Where(c => countFilter.HasValue == false || c.Orders.Count > countFilter)
            .Where(c => countryFilter == null || c.Address.Country == countryFilter)
        );

Kullanıcı arabiriminde şu şekilde çağrılacak:

    using (NorthwindEntities context = new NorthwindEntities())
    {
        int? countFilter = (this.orderCountFilterList.SelectedIndex == 0) ?
            (int?)null :
            int.Parse(this.orderCountFilterList.SelectedValue);

        string countryFilter = (this.countryFilterList.SelectedIndex == 0) ?
            null :
            this.countryFilterList.SelectedValue;

        IQueryable<Customer> myCustomers = context.InvokeCustomersForEmployeeWithFilters(
                countFilter, countryFilter);

        this.customersGrid.DataSource = myCustomers;
        this.customersGrid.DataBind();
    }

 Burada bir denge, oluşturulan depo komutunun her zaman null denetimlere sahip filtreleri olacaktır, ancak bunlar veritabanı sunucusunun iyileştirmesi için oldukça basit olmalıdır:

...
WHERE ((0 = (CASE WHEN (@p__linq__1 IS NOT NULL) THEN cast(1 as bit) WHEN (@p__linq__1 IS NULL) THEN cast(0 as bit) END)) OR ([Project3].[C2] > @p__linq__2)) AND (@p__linq__3 IS NULL OR [Project3].[Country] = @p__linq__4)

3.4 Meta verileri önbelleğe alma

Entity Framework, Meta veri önbelleğe almayı da destekler. Bu temelde tür bilgilerinin önbelleğe alınmasını ve aynı modele yönelik farklı bağlantılar arasında tür-veritabanı eşleme bilgilerinin önbelleğe alınmasını sağlar. Meta veri önbelleği AppDomain başına benzersizdir.

3.4.1 Meta veri Önbelleğe Alma algoritması

  1. Bir modelin meta veri bilgileri, her Varlık Bağlan ion için bir ItemCollection içinde depolanır.

    • Yan not olarak, modelin farklı bölümleri için farklı ItemCollection nesneleri vardır. Örneğin, StoreItemCollections veritabanı modeli hakkındaki bilgileri içerir; ObjectItemCollection, veri modeli hakkında bilgi içerir; EdmItemCollection kavramsal model hakkında bilgi içerir.
  2. İki bağlantı aynı bağlantı dizesi kullanırsa, aynı ItemCollection örneğini paylaşır.

  3. İşlevsel olarak eşdeğer ancak metinsel olarak farklı bağlantı dizesi farklı meta veri önbelleklerine neden olabilir. bağlantı dizesi belirteci oluştururuz, bu nedenle belirteçlerin sırasının değiştirilmesi paylaşılan meta verilerle sonuçlanır. Ancak işlevsel olarak aynı görünen iki bağlantı dizesi belirteçleştirmeden sonra aynı olarak değerlendirilmeyebilir.

  4. ItemCollection düzenli aralıklarla kullanım için denetlenmektedir. Bir çalışma alanına yakın zamanda erişilmediği belirlenirse, bir sonraki önbellek süpürmesinde temizleme için işaretlenir.

  5. Yalnızca varlık oluşturmak Bağlan bir meta veri önbelleği oluşturulmasına neden olur (ancak içindeki öğe koleksiyonları bağlantı açılana kadar başlatılmaz). Bu çalışma alanı, önbelleğe alma algoritması "kullanımda" olmadığını belirleyene kadar bellek içinde kalır.

Müşteri Danışmanlığı Ekibi, büyük modelleri kullanırken "kullanımdan kaldırılmasını" önlemek için bir ItemCollection'a başvuruda bulunduğunu açıklayan bir blog gönderisi yazdı: <https://learn.microsoft.com/archive/blogs/appfabriccat/holding-a-reference-to-the-ef-metadataworkspace-for-wcf-services>.

3.4.2 Meta Veri Önbelleğe Alma ile Sorgu Planı Önbelleğe Alma arasındaki ilişki

Sorgu planı önbellek örneği MetadataWorkspace'in Mağaza türlerinin ItemCollection'ında yer alır. Bu, önbelleğe alınmış depolama komutlarının belirli bir MetadataWorkspace kullanılarak örneklenen tüm bağlamlara karşı sorgular için kullanılacağı anlamına gelir. Ayrıca, biraz farklı olan ve belirteçledikten sonra eşleşmeyen iki bağlantı dizeniz varsa, farklı sorgu planı önbellek örneklerine sahip olacağınız anlamına gelir.

3.5 Sonuçları önbelleğe alma

Sonuçları önbelleğe alma ("ikinci düzey önbelleğe alma" olarak da bilinir) ile sorguların sonuçlarını yerel önbellekte tutarsınız. Sorguyu gönderirken, önce depoda sorgulamadan önce sonuçların yerel olarak kullanılabilir olup olmadığını görürsünüz. Sonuçları önbelleğe alma işlemi Entity Framework tarafından doğrudan desteklenmese de, sarmalama sağlayıcısı kullanarak ikinci düzey önbellek eklemek mümkündür. alachisoft'un NCache tabanlı Entity Framework İkinci Düzey Önbelleği, ikinci düzey bir önbellekle sarmalama sağlayıcısına örnek olarak verilmiştir.

bu ikinci düzey önbelleğe alma uygulaması, LINQ ifadesi değerlendirildikten (ve funclet'lendikten) ve sorgu yürütme planı birinci düzey önbellekten hesaplandıktan veya alındıktan sonra gerçekleşen eklenen bir işlevdir. İkinci düzey önbellek yalnızca ham veritabanı sonuçlarını depolar, bu nedenle gerçekleştirme işlem hattı daha sonra yürütülür.

3.5.1 Sarmalama sağlayıcısıyla sonuçları önbelleğe almak için ek başvurular

  • Julie Lerman, Örnek sarmalama sağlayıcısının Windows Server AppFabric önbelleğe alma özelliğini kullanacak şekilde nasıl güncelleştirildiğini içeren bir "Entity Framework ve Windows Azure'da İkinci Düzey Önbelleğe Alma" MSDN makalesi yazdı:https://msdn.microsoft.com/magazine/hh394143.aspx
  • Entity Framework 5 ile çalışıyorsanız, ekip blogu Entity Framework 5 için önbelleğe alma sağlayıcısıyla işlerin nasıl çalıştırıldığını açıklayan bir gönderi içerir: <https://learn.microsoft.com/archive/blogs/adonet/ef-caching-with-jarek-kowalskis-provider>. Ayrıca, projenize 2. düzey önbelleğe almayı otomatikleştirmeye yardımcı olan bir T4 şablonu içerir.

4 Otomatik Olarak Derlenen Sorgular

Entity Framework kullanılarak bir veritabanına sorgu verildiğinde, sonuçları gerçekleştirmeden önce bir dizi adımdan geçmesi gerekir; Bu tür adımlardan biri Sorgu Derleme'dir. Varlık SQL sorgularının otomatik olarak önbelleğe alınırken iyi performansa sahip olduğu biliniyordu, bu nedenle aynı sorguyu ikinci veya üçüncü kez yürüttüğünde plan derleyicisini atlayabilir ve bunun yerine önbelleğe alınmış planı kullanabilir.

Entity Framework 5' de LINQ to Entities sorguları için otomatik önbelleğe alma da sunulmuştur. Entity Framework'ün geçmiş sürümlerinde performansınızı hızlandırmak için Bir CompiledQuery oluşturmak yaygın bir uygulamaydı, bu da LINQ to Entities sorgunuzun önbelleğe alınabilir olmasını sağlayacaktı. Önbelleğe alma işlemi artık CompiledQuery kullanılmadan otomatik olarak yapıldığından bu özelliğe "otomatik olarak derlenmiş sorgular" diyoruz. Sorgu planı önbelleği ve mekaniği hakkında daha fazla bilgi için bkz. Sorgu Planı Önbelleğe Alma.

Entity Framework bir sorgunun ne zaman yeniden derlendiğini algılar ve sorgu daha önce derlenmiş olsa bile çağrıldığında bunu yapar. Sorgunun yeniden derlenmelerine neden olan yaygın koşullar şunlardır:

  • Sorgunuzla ilişkili MergeOption değerini değiştirme. Önbelleğe alınan sorgu kullanılmaz, bunun yerine plan derleyicisi yeniden çalışır ve yeni oluşturulan plan önbelleğe alınır.
  • ContextOptions.UseCSharpNullComparisonBehavior değerini değiştirme. MergeOption'ı değiştirmekle aynı etkiyi elde edersiniz.

Diğer koşullar sorgunuzun önbelleği kullanmasını engelleyebilir. Sık karşılaşılan örnekler:

  • IEnumerable<T> kullanma. Contains<>(T değeri).
  • Sabitleri olan sorgular üreten işlevleri kullanma.
  • Eşlenmeyen bir nesnenin özelliklerini kullanma.
  • Sorgunuzu yeniden derlenmesi gereken başka bir sorguya bağlama.

4.1 IEnumerable<T> kullanılıyor. T>(T değeri) içerir<

Entity Framework, IEnumerable<T> çağıran sorguları önbelleğe almaz. Koleksiyonun değerleri geçici olarak kabul edildiği için bellek içi bir koleksiyona karşı T>(T değeri) içerir<. Aşağıdaki örnek sorgu önbelleğe alınmaz, bu nedenle her zaman plan derleyicisi tarafından işlenir:

int[] ids = new int[10000];
...
using (var context = new MyContext())
{
    var query = context.MyEntities
                    .Where(entity => ids.Contains(entity.Id));

    var results = query.ToList();
    ...
}

İçer'in yürütüleceği IEnumerable boyutunun sorgunuzun ne kadar hızlı veya ne kadar yavaş derleneceğini belirlediğini unutmayın. Yukarıdaki örnekte gösterilen gibi büyük koleksiyonlar kullanılırken performans önemli ölçüde olumsuz etkilenebilir.

Entity Framework 6, IEnumerable<T> yöntemine yönelik iyileştirmeler içerir. İçeren<T>(T) değeri, sorgular yürütülürken çalışır. Oluşturulan SQL kodunun üretilmesi çok daha hızlıdır ve daha okunabilirdir ve çoğu durumda da sunucuda daha hızlı yürütülür.

4.2 Sabitleri olan sorgular üreten işlevleri kullanma

Skip(), Take(), Contains() ve DefautIfEmpty() LINQ işleçleri parametrelerle SQL sorguları üretmez, bunun yerine bunlara geçirilen değerleri sabit olarak koyar. Bu nedenle, aksi takdirde aynı olabilecek sorgular hem EF yığınında hem de veritabanı sunucusunda sorgu planı önbelleğini kirletebilir ve sonraki sorgu yürütmesinde aynı sabitler kullanılmadığı sürece yeniden kullanılmaz. Örnek:

var id = 10;
...
using (var context = new MyContext())
{
    var query = context.MyEntities.Select(entity => entity.Id).Contains(id);

    var results = query.ToList();
    ...
}

Bu örnekte, bu sorgu id için farklı bir değerle her yürütülürken sorgu yeni bir planda derlenir.

Özellikle sayfalama yaparken Atla ve Al'ın kullanımına dikkat edin. EF6'da bu yöntemler, önbelleğe alınmış sorgu planını etkili bir şekilde yeniden kullanılabilir hale getiren bir lambda aşırı yüklemesine sahiptir çünkü EF bu yöntemlere geçirilen değişkenleri yakalayabilir ve bunları SQLparameters'a çevirebilir. Bu ayrıca, atla ve Al için farklı bir sabite sahip olan her sorgu kendi sorgu planı önbellek girdisini alabileceğinden önbellek temizleyicisini tutmaya da yardımcı olur.

Aşağıdaki kodu göz önünde bulundurun; bu kod en iyi durumda değildir ancak yalnızca bu sorgu sınıfını örnekleye yöneliktir:

var customers = context.Customers.OrderBy(c => c.LastName);
for (var i = 0; i < count; ++i)
{
    var currentCustomer = customers.Skip(i).FirstOrDefault();
    ProcessCustomer(currentCustomer);
}

Bu kodun daha hızlı bir sürümü, Skip'i bir lambda ile çağırmayı içerir:

var customers = context.Customers.OrderBy(c => c.LastName);
for (var i = 0; i < count; ++i)
{
    var currentCustomer = customers.Skip(() => i).FirstOrDefault();
    ProcessCustomer(currentCustomer);
}

sorgu her çalıştırıldığında aynı sorgu planı kullanıldığından ikinci kod parçacığı %11'e kadar daha hızlı çalışabilir ve bu da CPU süresinden tasarruf eder ve sorgu önbelleğinin kirletilmesinden kaçınır. Ayrıca Skip parametresi kapatılırken kod şu anda şöyle görünebilir:

var i = 0;
var skippyCustomers = context.Customers.OrderBy(c => c.LastName).Skip(() => i);
for (; i < count; ++i)
{
    var currentCustomer = skippyCustomers.FirstOrDefault();
    ProcessCustomer(currentCustomer);
}

4.3 Eşlenmemiş bir nesnenin özelliklerini kullanma

Bir sorgu eşlenmeyen nesne türünün özelliklerini parametre olarak kullandığında sorgu önbelleğe alınmaz. Örnek:

using (var context = new MyContext())
{
    var myObject = new NonMappedType();

    var query = from entity in context.MyEntities
                where entity.Name.StartsWith(myObject.MyProperty)
                select entity;

   var results = query.ToList();
    ...
}

Bu örnekte NonMappedType sınıfının Varlık modelinin bir parçası olmadığını varsayalım. Bu sorgu, eşlenmemiş bir tür kullanmamak ve bunun yerine sorgunun parametresi olarak yerel bir değişken kullanmak için kolayca değiştirilebilir:

using (var context = new MyContext())
{
    var myObject = new NonMappedType();
    var myValue = myObject.MyProperty;
    var query = from entity in context.MyEntities
                where entity.Name.StartsWith(myValue)
                select entity;

    var results = query.ToList();
    ...
}

Bu durumda sorgu önbelleğe alınır ve sorgu planı önbelleğinden yararlanır.

4.4 Yeniden derleme gerektiren sorgulara bağlanma

Yukarıdaki örnekte olduğu gibi, yeniden derlenmesi gereken bir sorguyu kullanan ikinci bir sorgunuz varsa, ikinci sorgunuzun tamamı da yeniden derlenir. Bu senaryoya örnek olarak şunlar gösterelim:

int[] ids = new int[10000];
...
using (var context = new MyContext())
{
    var firstQuery = from entity in context.MyEntities
                        where ids.Contains(entity.Id)
                        select entity;

    var secondQuery = from entity in context.MyEntities
                        where firstQuery.Any(otherEntity => otherEntity.Id == entity.Id)
                        select entity;

    var results = secondQuery.ToList();
    ...
}

Örnek geneldir, ancak firstQuery'ye bağlanmanın secondQuery'nin önbelleğe alınamamasına nasıl neden olduğunu gösterir. firstQuery yeniden derleme gerektiren bir sorgu olmasaydı, secondQuery önbelleğe alınmış olurdu.

5 NoTracking Sorgusu

5.1 Durum yönetimi ek yükünü azaltmak için değişiklik izlemeyi devre dışı bırakma

Salt okunur bir senaryodaysanız ve nesneleri ObjectStateManager'a yükleme yükünden kaçınmak istiyorsanız, "İzleme Yok" sorguları oluşturabilirsiniz.  Değişiklik izleme sorgu düzeyinde devre dışı bırakılabilir.

Değişiklik izlemeyi devre dışı bırakarak nesne önbelleğini etkili bir şekilde kapattığınızı unutmayın. Bir varlığı sorguladığınızda, ObjectStateManager'dan daha önce gerçekleştirilmiş sorgu sonuçlarını çekerek gerçekleştirmeyi atlayamazsınız. Aynı bağlamda aynı varlıkları tekrar tekrar sorgularsanız, değişiklik izlemeyi etkinleştirmenin bir performans avantajını görebilirsiniz.

ObjectContext kullanılarak sorgulanırken, ObjectQuery ve ObjectSet örnekleri bir MergeOption ayarlandıktan sonra anımsar ve bunlarda oluşturulan sorgular üst sorgunun etkin MergeOption değerini devralır. DbContext kullanılırken, DbSet'te AsNoTracking() değiştiricisi çağrılarak izleme devre dışı bırakılabilir.

5.1.1 DbContext kullanırken sorgu için değişiklik izlemeyi devre dışı bırakma

Sorgudaki AsNoTracking() yöntemine bir çağrı zincirleyerek sorgu modunu NoTracking'e geçirebilirsiniz. ObjectQuery'nin aksine, DbContext API'sindeki DbSet ve DbQuery sınıflarının MergeOption için değiştirilebilir özelliği yoktur.

    var productsForCategory = from p in context.Products.AsNoTracking()
                                where p.Category.CategoryName == selectedCategory
                                select p;


5.1.2 ObjectContext kullanarak sorgu düzeyinde değişiklik izlemeyi devre dışı bırakma

    var productsForCategory = from p in context.Products
                                where p.Category.CategoryName == selectedCategory
                                select p;

    ((ObjectQuery)productsForCategory).MergeOption = MergeOption.NoTracking;

5.1.3 ObjectContext kullanarak varlık kümesinin tamamı için değişiklik izlemeyi devre dışı bırakma

    context.Products.MergeOption = MergeOption.NoTracking;

    var productsForCategory = from p in context.Products
                                where p.Category.CategoryName == selectedCategory
                                select p;

5.2 NoTracking sorgularının performans avantajını gösteren Test Ölçümleri

Bu testte, İzleme ile Navision modeli için NoTracking sorgularını karşılaştırarak ObjectStateManager'ı doldurmanın maliyetine bakacağız. Navision modelinin açıklaması ve yürütülen sorgu türleri için eke bakın. Bu testte sorgu listesini yineleyeceğiz ve her birini bir kez yürüteceğiz. Testin iki varyasyonunu çalıştırdık, bir kez NoTracking sorgularıyla ve bir kez de varsayılan birleştirme seçeneği olan "AppendOnly" ile. Her varyasyonu 3 kez çalıştırdık ve çalıştırmaların ortalama değerini aldık. Testler arasında SQL Server'da sorgu önbelleğini temizler ve aşağıdaki komutları çalıştırarak tempdb'yi küçültüriz:

  1. DBCC DROPCLEANBUFFERS
  2. DBCC FREEPROCCACHE
  3. DBCC SHRINKDATABASE (tempdb, 0)

Test Sonuçları, 3'ün üzerinde ortanca çalıştırma:

İZLEME YOK – ÇALıŞMA KÜMESI İZLEME YOK – SÜRE YALNıZCA EKLE – ÇALıŞMA KÜMESI YALNIZCA EKLE – SAAT
Entity Framework 5 460361728 1163536 ms 596545536 1273042 ms
Entity Framework 6 647127040 190228 ms 832798720 195521 ms

Entity Framework 5, çalıştırmanın sonunda Entity Framework 6'dan daha küçük bir bellek ayak izine sahip olacaktır. Entity Framework 6 tarafından tüketilen ek bellek, yeni özellikleri ve daha iyi performansı sağlayan ek bellek yapılarının ve kodunun sonucudur.

ObjectStateManager kullanılırken bellek ayak izi açısından da net bir fark vardır. Entity Framework 5, veritabanından oluşturduğumuz tüm varlıkları takip ederken ayak izini %30 artırdı. Entity Framework 6 bunu yaparken ayak izini %28 artırdı.

Entity Framework 6, zaman açısından bu testte Entity Framework 5'i büyük bir marjla geride bıraktı. Entity Framework 6, Entity Framework 5 tarafından tüketilen zamanın yaklaşık %16'sında testi tamamladı. Ayrıca, ObjectStateManager kullanılırken Entity Framework 5'in tamamlanması %9 daha fazla zaman alır. Buna karşılık, ObjectStateManager kullanılırken Entity Framework 6 %3 daha fazla zaman kullanıyor.

6 Sorgu Yürütme Seçenekleri

Entity Framework sorgulamak için birkaç farklı yol sunar. Aşağıdaki seçeneklere göz atacak, her birinin artılarını ve dezavantajlarını karşılaştıracak ve performans özelliklerini inceleyeceğiz:

  • LINQ to Entities.
  • Varlıklara LINQ İzleme Yok.
  • ObjectQuery üzerinden Entity SQL.
  • EntityCommand üzerinden Entity SQL.
  • Executestorequery.
  • Sqlquery.
  • Compiledquery.

6.1 LINQ to Entities sorguları

var q = context.Products.Where(p => p.Category.CategoryName == "Beverages");

Artıları

  • CUD işlemleri için uygundur.
  • Tamamen gerçekleştirilmiş nesneler.
  • Programlama dilinde yerleşik söz dizimi ile yazmak en kolayıdır.
  • İyi performans.

Eksileri

  • Belirli teknik kısıtlamalar, örneğin:
    • OUTER JOIN sorguları için DefaultIfEmpty kullanan desenler, Entity SQL'deki basit OUTER JOIN deyimlerinden daha karmaşık sorgulara neden olur.
    • Genel desen eşleştirmesi ile LIKE'i yine de kullanamazsınız.

6.2 Varlıklara LINQ İzleme sorgusu yok

Bağlam ObjectContext türediğinde:

context.Products.MergeOption = MergeOption.NoTracking;
var q = context.Products.Where(p => p.Category.CategoryName == "Beverages");

Bağlam DbContext türetildiğinde:

var q = context.Products.AsNoTracking()
                        .Where(p => p.Category.CategoryName == "Beverages");

Artıları

  • Normal LINQ sorgularının performansı iyileştirildi.
  • Tamamen gerçekleştirilmiş nesneler.
  • Programlama dilinde yerleşik söz dizimi ile yazmak en kolayıdır.

Eksileri

  • CUD işlemleri için uygun değildir.
  • Belirli teknik kısıtlamalar, örneğin:
    • OUTER JOIN sorguları için DefaultIfEmpty kullanan desenler, Entity SQL'deki basit OUTER JOIN deyimlerinden daha karmaşık sorgulara neden olur.
    • Genel desen eşleştirmesi ile LIKE'i yine de kullanamazsınız.

NoTracking belirtilmemiş olsa bile skaler özellikleri yansıtan sorguların izlenmediğini unutmayın. Örnek:

var q = context.Products.Where(p => p.Category.CategoryName == "Beverages").Select(p => new { p.ProductName });

Bu sorgu açıkça NoTracking olarak belirtilmez, ancak nesne durum yöneticisi tarafından bilinen bir türü gerçekleştirmediğinden gerçekleştirilmiş sonuç izlenmez.

ObjectQuery üzerinden 6.3 Varlık SQL'i

ObjectQuery<Product> products = context.Products.Where("it.Category.CategoryName = 'Beverages'");

Artıları

  • CUD işlemleri için uygundur.
  • Tamamen gerçekleştirilmiş nesneler.
  • Sorgu planı önbelleğe almayı destekler.

Eksileri

  • Kullanıcı hatasına, dilde yerleşik olan sorgu yapılarından daha eğilimli metinsel sorgu dizeleri içerir.

Varlık Komutu Üzerinden 6.4 Entity SQL

EntityCommand cmd = eConn.CreateCommand();
cmd.CommandText = "Select p From NorthwindEntities.Products As p Where p.Category.CategoryName = 'Beverages'";

using (EntityDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
    while (reader.Read())
    {
        // manually 'materialize' the product
    }
}

Artıları

  • .NET 4.0'da sorgu planı önbelleğe almayı destekler (plan önbelleğe alma, .NET 4.5'teki diğer tüm sorgu türleri tarafından desteklenir).

Eksileri

  • Kullanıcı hatasına, dilde yerleşik olan sorgu yapılarından daha eğilimli metinsel sorgu dizeleri içerir.
  • CUD işlemleri için uygun değildir.
  • Sonuçlar otomatik olarak gerçekleşmez ve veri okuyucudan okunmalıdır.

6.5 SqlQuery ve ExecuteStoreQuery

Veritabanında SqlQuery:

// use this to obtain entities and not track them
var q1 = context.Database.SqlQuery<Product>("select * from products");

DbSet üzerinde SqlQuery:

// use this to obtain entities and have them tracked
var q2 = context.Products.SqlQuery("select * from products");

Executestorequery:

var beverages = context.ExecuteStoreQuery<Product>(
@"     SELECT        P.ProductID, P.ProductName, P.SupplierID, P.CategoryID, P.QuantityPerUnit, P.UnitPrice, P.UnitsInStock, P.UnitsOnOrder, P.ReorderLevel, P.Discontinued, P.DiscontinuedDate
       FROM            Products AS P INNER JOIN Categories AS C ON P.CategoryID = C.CategoryID
       WHERE        (C.CategoryName = 'Beverages')"
);

Artıları

  • Plan derleyicisi atlandığından genellikle en hızlı performans.
  • Tamamen gerçekleştirilmiş nesneler.
  • DbSet'ten kullanıldığında CUD işlemleri için uygundur.

Eksileri

  • Sorgu metinseldir ve hataya açıktır.
  • Sorgu, kavramsal semantik yerine mağaza semantiği kullanılarak belirli bir arka uçtan bağlanır.
  • Devralma mevcut olduğunda, el yapımı sorgunun istenen tür için eşleme koşullarını hesaba eklemesi gerekir.

6.6 Derlenmiş Sorgu

private static readonly Func<NorthwindEntities, string, IQueryable<Product>> productsForCategoryCQ = CompiledQuery.Compile(
    (NorthwindEntities context, string categoryName) =>
        context.Products.Where(p => p.Category.CategoryName == categoryName)
        );
…
var q = context.InvokeProductsForCategoryCQ("Beverages");

Artıları

  • Normal LINQ sorgularına göre %7'ye kadar performans geliştirmesi sağlar.
  • Tamamen gerçekleştirilmiş nesneler.
  • CUD işlemleri için uygundur.

Eksileri

  • Artan karmaşıklık ve programlama ek yükü.
  • Derlenmiş sorgunun üzerine oluşturma sırasında performans artışı kaybolur.
  • Bazı LINQ sorguları, anonim türlerin projeksiyonları gibi Bir CompiledQuery olarak yazılamaz.

6.7 Farklı sorgu seçeneklerinin Performans Karşılaştırması

Bağlam oluşturmanın zamanlanmadığı basit mikrobenchmark'lar teste konuldu. Denetlenen bir ortamda önbelleğe alınmayan bir dizi varlık için sorgulamayı 5000 kez ölçtük. Bu sayılar bir uyarıyla alınmalıdır: Bunlar bir uygulama tarafından üretilen gerçek sayıları yansıtmaz, ancak bunun yerine, farklı sorgulama seçenekleri elmadan elmaya karşılaştırıldığında performans farkının ne kadarının olduğunu gösteren çok doğru bir ölçümdür ve yeni bağlam oluşturmanın maliyeti hariçtir.

EF Test etme Saat (ms) Bellek
EF5 ObjectContext ESQL 2414 38801408
EF5 ObjectContext Linq Sorgusu 2692 38277120
EF5 DbContext Linq Sorgusu İzleme Yok 2818 41840640
EF5 DbContext Linq Sorgusu 2930 41771008
EF5 ObjectContext Linq Sorgusu İzleme Yok 3013 38412288
EF6 ObjectContext ESQL 2059 46039040
EF6 ObjectContext Linq Sorgusu 3074 45248512
EF6 DbContext Linq Sorgusu İzleme Yok 3125 47575040
EF6 DbContext Linq Sorgusu 3420 47652864
EF6 ObjectContext Linq Sorgusu İzleme Yok 3593 45260800

EF5 micro benchmarks, 5000 warm iterations

EF6 micro benchmarks, 5000 warm iterations

Mikrobenchmark'lar koddaki küçük değişikliklere karşı çok hassastır. Bu durumda, Entity Framework 5 ile Entity Framework 6 maliyetleri arasındaki fark, kesme ve işlem iyileştirmelerinin eklenmesinden kaynaklanmaktadır. Ancak bu mikrobenchmarks sayıları, Entity Framework'ün yaptığı şeyin çok küçük bir parçasına yönelik yükseltilmiş bir görüntüdür. Gerçek hayattaki sıcak sorgu senaryoları, Entity Framework 5'ten Entity Framework 6'ya yükseltirken performans gerilemesi görmemelidir.

Farklı sorgu seçeneklerinin gerçek dünya performansını karşılaştırmak için, kategori adı "İçecekler" olan tüm ürünleri seçmek için farklı bir sorgu seçeneği kullandığımız 5 ayrı test varyasyonu oluşturduk. Her yineleme bağlamı oluşturma maliyetini ve döndürülen tüm varlıkları gerçekleştirme maliyetini içerir. 1000 zamanlanmış yinelemenin toplamı alınmadan önce 10 yineleme iyileştirilmemiş olarak çalıştırılır. Gösterilen sonuçlar, her testin 5 çalıştırmasından alınan ortanca çalıştırmadır. Daha fazla bilgi için bkz. Test kodunu içeren Ek B.

EF Test etme Saat (ms) Bellek
EF5 ObjectContext Entity Command 621 39350272
EF5 Veritabanında DbContext Sql Sorgusu 825 37519360
EF5 ObjectContext Depolama Sorgusu 878 39460864
EF5 ObjectContext Linq Sorgusu İzleme Yok 969 38293504
EF5 Nesne Sorgusu kullanan ObjectContext Entity Sql 1089 38981632
EF5 ObjectContext Derlenmiş Sorgu 1099 38682624
EF5 ObjectContext Linq Sorgusu 1152 38178816
EF5 DbContext Linq Sorgusu İzleme Yok 1208 41803776
EF5 DbSet'te DbContext Sql Sorgusu 1414 37982208
EF5 DbContext Linq Sorgusu 1574 41738240
EF6 ObjectContext Entity Command 480 47247360
EF6 ObjectContext Depolama Sorgusu 493 46739456
EF6 Veritabanında DbContext Sql Sorgusu 614 41607168
EF6 ObjectContext Linq Sorgusu İzleme Yok 684 46333952
EF6 Nesne Sorgusu kullanan ObjectContext Entity Sql 767 48865280
EF6 ObjectContext Derlenmiş Sorgu 788 48467968
EF6 DbContext Linq Sorgusu İzleme Yok 878 47554560
EF6 ObjectContext Linq Sorgusu 953 47632384
EF6 DbSet'te DbContext Sql Sorgusu 1023 41992192
EF6 DbContext Linq Sorgusu 1290 47529984

EF5 warm query 1000 iterations

EF6 warm query 1000 iterations

Dekont

Eksiksizlik için, EntityCommand üzerinde Entity SQL sorgusu yürüttüğümiz bir varyasyonu dahil ettik. Ancak, bu tür sorgular için sonuçlar gerçekleştirilmediğinden, karşılaştırma mutlaka elmadan elmaya değildir. Test, karşılaştırmayı daha adil hale getirmeye çalışmak için gerçekleştirilmesi için yakın bir yaklaşık değer içerir.

Bu uçtan uca durumda Entity Framework 6, çok daha hafif DbContext başlatma ve daha hızlı MetadataCollection<T> aramaları dahil olmak üzere yığının çeşitli bölümlerinde yapılan performans geliştirmeleri nedeniyle Entity Framework 5'i daha iyi performansa sahiptir.

7 Tasarım süresi performansıyla ilgili dikkat edilmesi gerekenler

7.1 Devralma Stratejileri

Entity Framework kullanırken performansta dikkat edilmesi gereken bir diğer nokta da kullandığınız devralma stratejisidir. Entity Framework 3 temel devralma türünü ve bunların birleşimlerini destekler:

  • Hiyerarşi Başına Tablo (TPH) – burada her devralma kümesi, hiyerarşideki belirli bir türün satırda temsil edildiğini belirtmek için ayrıştırıcı sütunu olan bir tabloyla eşlenir.
  • Tür Başına Tablo (TPT) – burada her türün veritabanında kendi tablosu vardır; alt tablolar yalnızca üst tablonun içermediği sütunları tanımlar.
  • Sınıf Başına Tablo (TPC) – burada her türün veritabanında kendi tam tablosu vardır; alt tablolar, üst türlerde tanımlananlar da dahil olmak üzere tüm alanlarını tanımlar.

Modeliniz TPT devralma kullanıyorsa, oluşturulan sorgular diğer devralma stratejileriyle oluşturulan sorgulardan daha karmaşık olur ve bu da depoda daha uzun yürütme sürelerine neden olabilir.  TPT modeli üzerinden sorgu oluşturmak ve sonuçta elde edilen nesneleri oluşturmak genellikle daha uzun sürer.

"Entity Framework'te TPT (Tür Başına Tablo) Devralma kullanılırken performansla ilgili dikkat edilmesi gerekenler" MSDN blog gönderisine bakın: <https://learn.microsoft.com/archive/blogs/adonet/performance-considerations-when-using-tpt-table-per-type-inheritance-in-the-entity-framework>.

7.1.1 Önce Model veya Kod öncelikli uygulamalarda TPT'yi önleme

TPT şeması olan mevcut bir veritabanı üzerinde model oluşturduğunuzda, çok fazla seçeneğiniz yoktur. Ancak Model First veya Code First kullanarak bir uygulama oluştururken performansla ilgili endişeler için TPT devralmayı önlemeniz gerekir.

Varlık Tasarım Aracı Sihirbazı'nda Model First kullandığınızda, modelinizdeki herhangi bir devralma için TPT alırsınız. Model First ile bir TPH devralma stratejisine geçmek istiyorsanız, Visual Studio Galerisi' nden ( <http://visualstudiogallery.msdn.microsoft.com/df3541c3-d833-4b65-b942-989e7ec74c87/>) sağlanan "Entity Tasarım Aracı Database Generation Power Pack"i kullanabilirsiniz.

Devralma ile bir modelin eşlemesini yapılandırmak için Code First kullanıldığında EF varsayılan olarak TPH kullanır, bu nedenle devralma hiyerarşisindeki tüm varlıklar aynı tabloya eşlenir. Daha fazla ayrıntı için MSDN Dergisi'ndeki ( http://msdn.microsoft.com/magazine/hh126815.aspx) "Entity Framework4.1'de İlk Kod" makalesinin "Fluent API ile Eşleme" bölümüne bakın.

7.2 Model oluşturma süresini geliştirmek için EF4'ten yükseltme

Modelin depolama katmanını (SSDL) oluşturan algoritmada SQL Server'a özgü bir iyileştirme Entity Framework 5 ve 6'da ve Visual Studio 2010 SP1 yüklendiğinde Entity Framework 4'e yönelik bir güncelleştirme olarak kullanılabilir. Aşağıdaki test sonuçları, çok büyük bir model oluştururken elde edilen iyileştirmeyi (bu örnekte Navision modeli) gösterir. Daha fazla ayrıntı için bkz. Ek C.

Model 1005 varlık kümesi ve 4227 ilişkilendirme kümesi içerir.

Yapılandırma Harcanan zamanın dökümü
Visual Studio 2010, Entity Framework 4 SSDL Nesli: 2 sa 27 dk
Eşleme Oluşturma: 1 saniye
CSDL Oluşturma: 1 saniye
ObjectLayer Oluşturma: 1 saniye
Görünüm Oluşturma: 2 s 14 dk
Visual Studio 2010 SP1, Entity Framework 4 SSDL Oluşturma: 1 saniye
Eşleme Oluşturma: 1 saniye
CSDL Oluşturma: 1 saniye
ObjectLayer Oluşturma: 1 saniye
Görünüm Oluşturma: 1 sa 53 dk
Visual Studio 2013, Entity Framework 5 SSDL Oluşturma: 1 saniye
Eşleme Oluşturma: 1 saniye
CSDL Oluşturma: 1 saniye
ObjectLayer Oluşturma: 1 saniye
Görünüm Oluşturma: 65 dakika
Visual Studio 2013, Entity Framework 6 SSDL Oluşturma: 1 saniye
Eşleme Oluşturma: 1 saniye
CSDL Oluşturma: 1 saniye
ObjectLayer Oluşturma: 1 saniye
Görünüm Oluşturma: 28 saniye.

SSDL'yi oluştururken istemci geliştirme makinesinin sonuçların sunucudan döndürülmesi için boşta beklerken yükün neredeyse tamamen SQL Server'da harcandığını belirtmek gerekir. DTA'lar bu gelişmeyi özellikle takdir etmelidir. Model oluşturma maliyetinin tamamının şimdi Görünüm Oluşturma'da gerçekleştiğini de belirtmek gerekir.

7.3 Büyük Modelleri Önce Veritabanı ve Model Öncesiyle Bölme

Model boyutu arttıkça tasarımcı yüzeyi dağınık hale gelir ve kullanımı zorlaşır. Genellikle 300'den fazla varlığı olan bir modelin tasarımcıyı etkili bir şekilde kullanamayacak kadar büyük olduğunu düşünüyoruz. Aşağıdaki blog gönderisinde büyük modelleri bölmeye yönelik çeşitli seçenekler açıklanmaktadır: <https://learn.microsoft.com/archive/blogs/adonet/working-with-large-models-in-entity-framework-part-2>.

Gönderi, Entity Framework'ün ilk sürümü için yazılmıştır, ancak adımlar yine de geçerlidir.

7.4 Varlık Veri Kaynağı Denetimi ile ilgili performans konuları

EntityDataSource Denetimi'ni kullanan bir web uygulamasının performansının önemli ölçüde kötüleştiği çok iş parçacıklı performans ve stres testlerinde vakalar gördük. Temel neden, EntityDataSource'un varlık olarak kullanılacak türleri bulmak için Web uygulaması tarafından başvurulan derlemelerde MetadataWorkspace.LoadFromAssembly öğesini tekrar tekrar çağırmasıdır.

Çözüm, EntityDataSource'un ContextTypeName değerini türetilmiş ObjectContext sınıfınızın tür adına ayarlamaktır. Bu, varlık türleri için başvuruda bulunılan tüm derlemeleri tarar mekanizmayı kapatır.

ContextTypeName alanının ayarlanması, .NET 4.0'daki EntityDataSource'un bir derlemeden yansıma aracılığıyla bir tür yükleyemediklerinde Düşünceler ionTypeLoadException oluşturması işlevsel bir sorunu da önler. Bu sorun .NET 4.5'te düzeltilmiştir.

7.5 POCO varlıkları ve değişiklik izleme proxy'leri

Entity Framework, veri sınıflarında değişiklik yapmadan veri modelinizle birlikte özel veri sınıflarını kullanmanıza olanak tanır. Bu, veri modelinizle mevcut etki alanı nesneleri gibi "düz eski" CLR nesnelerini (POCO) kullanabileceğiniz anlamına gelir. Veri modelinde tanımlanan varlıklara eşlenen bu POCO veri sınıfları (kalıcılık bilgisi olmayan nesneler olarak da bilinir), Varlık Veri Modeli araçları tarafından oluşturulan varlık türleriyle aynı sorgu, ekleme, güncelleştirme ve silme davranışlarının çoğunu destekler.

Entity Framework, POCO türlerinizden türetilen ve POCO varlıklarında gecikmeli yükleme ve otomatik değişiklik izleme gibi özellikleri etkinleştirmek istediğinizde kullanılan ara sunucu sınıfları da oluşturabilir. POCO sınıflarınızın, Entity Framework'ün proxy'leri kullanmasına izin vermek için burada açıklandığı gibi belirli gereksinimleri karşılaması gerekir: http://msdn.microsoft.com/library/dd468057.aspx.

Varlıklarınızın özelliklerinden herhangi birinin değeri değiştiğinde şans izleme proxy'leri nesne durumu yöneticisini bilgilendirir, böylece Entity Framework varlıklarınızın gerçek durumunu her zaman bilir. Bu, özelliklerinizin ayarlayıcı yöntemlerinin gövdesine bildirim olayları ekleyerek ve nesne durum yöneticisinin bu tür olayları işlemesini sağlayarak yapılır. Entity Framework tarafından oluşturulan olaylar kümesi nedeniyle ara sunucu varlığı oluşturmanın genellikle ara sunucu olmayan poco varlığı oluşturmaktan daha pahalı olacağını unutmayın.

POCO varlığında değişiklik izleme ara sunucusu olmadığında, varlıklarınızın içeriği önceki kaydedilmiş bir durumun kopyasıyla karşılaştırılarak değişiklikler bulunur. Bu derin karşılaştırma, bağlamınızdaki birçok varlığınız olduğunda veya varlıklarınızın çok büyük miktarda özelliği olduğunda, son karşılaştırmadan bu yana hiçbiri değişmese bile uzun bir süreç haline gelir.

Özetle: Değişiklik izleme proxy'sini oluştururken bir performans isabeti ödersiniz, ancak değişiklik izleme, varlıklarınızda çok sayıda özellik olduğunda veya modelinizde çok sayıda varlık olduğunda değişiklik algılama işlemini hızlandırmanıza yardımcı olur. Varlık sayısının çok fazla artmadığı az sayıda özelliğe sahip varlıklar için değişiklik izleme proxy'lerinin olması pek yararlı olmayabilir.

8.1 Gecikmeli Yükleme ve İstekli Yükleme karşılaştırması

Entity Framework, hedef varlığınızla ilgili varlıkları yüklemek için birkaç farklı yol sunar. Örneğin, Ürünler'i sorguladığınızda, ilgili Siparişlerin Nesne Durum Yöneticisi'ne yüklenmesinin farklı yolları vardır. Performans açısından bakıldığında, ilgili varlıkları yüklerken dikkate alınması gereken en büyük soru, Gecikmeli Yükleme mi yoksa İstekli Yükleme mi kullanılacağı olacaktır.

Hızlı Yükleme kullanılırken, ilgili varlıklar hedef varlık kümenizle birlikte yüklenir. Sorgunuzda hangi ilgili varlıkları getirmek istediğinizi belirtmek için Include deyimini kullanırsınız.

Gecikmeli Yükleme kullanırken, ilk sorgunuz yalnızca hedef varlık kümesini getirir. Ancak bir gezinti özelliğine her erişdiğinizde, ilgili varlığı yüklemek için depoya başka bir sorgu verilir.

Bir varlık yüklendikten sonra, gecikmeli yükleme veya hevesle yükleme kullanıyor olun, varlığın diğer sorguları doğrudan Nesne Durum Yöneticisi'nden yükler.

8.2 Gecikmeli Yükleme ve İstekli Yükleme arasında seçim yapma

Önemli olan, uygulamanız için doğru seçimi yapabilmeniz için Gecikmeli Yükleme ile İstekli Yükleme arasındaki farkı anlamanızdır. Bu, büyük bir yük içerebilecek tek bir istek yerine veritabanına karşı birden çok istek arasındaki dengeyi değerlendirmenize yardımcı olur. Uygulamanızın bazı bölümlerinde hevesle yükleme, diğer bölümlerinde ise gecikmeli yükleme kullanmak uygun olabilir.

Arka planda neler olduğuna bir örnek olarak, Birleşik Krallık'ta yaşayan müşterileri ve bunların sipariş sayısını sorgulamak istediğinizi varsayalım.

Eager Yükleme kullanma

using (NorthwindEntities context = new NorthwindEntities())
{
    var ukCustomers = context.Customers.Include(c => c.Orders).Where(c => c.Address.Country == "UK");
    var chosenCustomer = AskUserToPickCustomer(ukCustomers);
    Console.WriteLine("Customer Id: {0} has {1} orders", customer.CustomerID, customer.Orders.Count);
}

Gecikmeli Yükleme Kullanma

using (NorthwindEntities context = new NorthwindEntities())
{
    context.ContextOptions.LazyLoadingEnabled = true;

    //Notice that the Include method call is missing in the query
    var ukCustomers = context.Customers.Where(c => c.Address.Country == "UK");

    var chosenCustomer = AskUserToPickCustomer(ukCustomers);
    Console.WriteLine("Customer Id: {0} has {1} orders", customer.CustomerID, customer.Orders.Count);
}

Hızlı yükleme kullanırken, tüm müşterileri ve tüm siparişleri döndüren tek bir sorgu verirsiniz. Store komutu şöyle görünür:

SELECT
[Project1].[C1] AS [C1],
[Project1].[CustomerID] AS [CustomerID],
[Project1].[CompanyName] AS [CompanyName],
[Project1].[ContactName] AS [ContactName],
[Project1].[ContactTitle] AS [ContactTitle],
[Project1].[Address] AS [Address],
[Project1].[City] AS [City],
[Project1].[Region] AS [Region],
[Project1].[PostalCode] AS [PostalCode],
[Project1].[Country] AS [Country],
[Project1].[Phone] AS [Phone],
[Project1].[Fax] AS [Fax],
[Project1].[C2] AS [C2],
[Project1].[OrderID] AS [OrderID],
[Project1].[CustomerID1] AS [CustomerID1],
[Project1].[EmployeeID] AS [EmployeeID],
[Project1].[OrderDate] AS [OrderDate],
[Project1].[RequiredDate] AS [RequiredDate],
[Project1].[ShippedDate] AS [ShippedDate],
[Project1].[ShipVia] AS [ShipVia],
[Project1].[Freight] AS [Freight],
[Project1].[ShipName] AS [ShipName],
[Project1].[ShipAddress] AS [ShipAddress],
[Project1].[ShipCity] AS [ShipCity],
[Project1].[ShipRegion] AS [ShipRegion],
[Project1].[ShipPostalCode] AS [ShipPostalCode],
[Project1].[ShipCountry] AS [ShipCountry]
FROM ( SELECT
      [Extent1].[CustomerID] AS [CustomerID],
       [Extent1].[CompanyName] AS [CompanyName],
       [Extent1].[ContactName] AS [ContactName],
       [Extent1].[ContactTitle] AS [ContactTitle],
       [Extent1].[Address] AS [Address],
       [Extent1].[City] AS [City],
       [Extent1].[Region] AS [Region],
       [Extent1].[PostalCode] AS [PostalCode],
       [Extent1].[Country] AS [Country],
       [Extent1].[Phone] AS [Phone],
       [Extent1].[Fax] AS [Fax],
      1 AS [C1],
       [Extent2].[OrderID] AS [OrderID],
       [Extent2].[CustomerID] AS [CustomerID1],
       [Extent2].[EmployeeID] AS [EmployeeID],
       [Extent2].[OrderDate] AS [OrderDate],
       [Extent2].[RequiredDate] AS [RequiredDate],
       [Extent2].[ShippedDate] AS [ShippedDate],
       [Extent2].[ShipVia] AS [ShipVia],
       [Extent2].[Freight] AS [Freight],
       [Extent2].[ShipName] AS [ShipName],
       [Extent2].[ShipAddress] AS [ShipAddress],
       [Extent2].[ShipCity] AS [ShipCity],
       [Extent2].[ShipRegion] AS [ShipRegion],
       [Extent2].[ShipPostalCode] AS [ShipPostalCode],
       [Extent2].[ShipCountry] AS [ShipCountry],
      CASE WHEN ([Extent2].[OrderID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
      FROM  [dbo].[Customers] AS [Extent1]
      LEFT OUTER JOIN [dbo].[Orders] AS [Extent2] ON [Extent1].[CustomerID] = [Extent2].[CustomerID]
      WHERE N'UK' = [Extent1].[Country]
)  AS [Project1]
ORDER BY [Project1].[CustomerID] ASC, [Project1].[C2] ASC

Yavaş yükleme kullanırken başlangıçta aşağıdaki sorguyu yayımlayacaksınız:

SELECT
[Extent1].[CustomerID] AS [CustomerID],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[ContactName] AS [ContactName],
[Extent1].[ContactTitle] AS [ContactTitle],
[Extent1].[Address] AS [Address],
[Extent1].[City] AS [City],
[Extent1].[Region] AS [Region],
[Extent1].[PostalCode] AS [PostalCode],
[Extent1].[Country] AS [Country],
[Extent1].[Phone] AS [Phone],
[Extent1].[Fax] AS [Fax]
FROM [dbo].[Customers] AS [Extent1]
WHERE N'UK' = [Extent1].[Country]

Müşterinin Siparişler gezinti özelliğine her erişişiniz için depoda aşağıdaki gibi başka bir sorgu verilir:

exec sp_executesql N'SELECT
[Extent1].[OrderID] AS [OrderID],
[Extent1].[CustomerID] AS [CustomerID],
[Extent1].[EmployeeID] AS [EmployeeID],
[Extent1].[OrderDate] AS [OrderDate],
[Extent1].[RequiredDate] AS [RequiredDate],
[Extent1].[ShippedDate] AS [ShippedDate],
[Extent1].[ShipVia] AS [ShipVia],
[Extent1].[Freight] AS [Freight],
[Extent1].[ShipName] AS [ShipName],
[Extent1].[ShipAddress] AS [ShipAddress],
[Extent1].[ShipCity] AS [ShipCity],
[Extent1].[ShipRegion] AS [ShipRegion],
[Extent1].[ShipPostalCode] AS [ShipPostalCode],
[Extent1].[ShipCountry] AS [ShipCountry]
FROM [dbo].[Orders] AS [Extent1]
WHERE [Extent1].[CustomerID] = @EntityKeyValue1',N'@EntityKeyValue1 nchar(5)',@EntityKeyValue1=N'AROUT'

Daha fazla bilgi için bkz . İlgili Nesneleri Yükleme.

8.2.1 Lazy Loading versus Eager Loading bilgi sayfası

İstekli yükleme ve gecikmeli yükleme arasında seçim yapmak için herkese uyan bir boyut gibi bir şey yoktur. İyi bilgilendirilmiş bir karar verebilmek için öncelikle her iki strateji arasındaki farkları anlamaya çalışın; ayrıca kodunuzun aşağıdaki senaryolardan birine uygun olup olmadığını da göz önünde bulundurun:

Senaryo Önerimiz
Getirilen varlıklardan birçok gezinti özelliğine erişmeniz gerekiyor mu? Hayır - Her iki seçenek de büyük olasılıkla yeterli olacaktır. Bununla birlikte, sorgunuzun getirdiği yük çok büyük değilse, nesnelerinizi gerçekleştirmek için daha az ağ gidiş dönüşleri gerektirdiğinden, Eager yüklemesini kullanarak performans avantajlarıyla karşılaşabilirsiniz.

Evet - Varlıklardan birçok gezinti özelliğine erişmeniz gerekiyorsa, sorgunuzda Eager yükleme ile birden çok include deyimi kullanarak bunu yaparsınız. Ne kadar çok varlık eklerseniz, sorgunuz o kadar büyük yük döndürür. Sorgunuza üç veya daha fazla varlık ekledikten sonra Gecikmeli yüklemeye geçmeyi göz önünde bulundurun.
Çalışma zamanında tam olarak hangi verilere ihtiyaç duyulacağını biliyor musunuz? Hayır - Yavaş yükleme sizin için daha iyi olacaktır. Aksi takdirde, ihtiyacınız olmayan verileri sorgulamanız gerekebilir.

Evet - İstekli yükleme muhtemelen en iyi seçeneğinizdir; tüm kümelerin daha hızlı yüklenmesine yardımcı olur. Sorgunuz çok büyük miktarda veri almayı gerektiriyorsa ve bu işlem çok yavaş hale geliyorsa, bunun yerine Gecikmeli yüklemeyi deneyin.
Kodunuz veritabanınızdan uzakta mı yürütülüyor? (artan ağ gecikme süresi) Hayır - Ağ gecikmesi sorun oluşturmadığında Gecikmeli yükleme kullanmak kodunuzu basitleştirebilir. Uygulamanızın topolojisinin değişebileceğini unutmayın; bu nedenle veritabanı yakınlığı almayın.

Evet - Ağ bir sorun olduğunda, senaryonuz için neyin daha uygun olduğuna yalnızca siz karar vekleyebilirsiniz. Genellikle Daha az gidiş dönüş gerektirdiği için İstekli yükleme daha iyi olacaktır.

8.2.2 Birden çok dahil ile ilgili performans endişeleri

Sunucu yanıt süresi sorunlarını içeren performans sorularını duyduğumuzda, sorunun kaynağı genellikle birden çok Include deyimine sahip sorgular olur. Sorguya ilgili varlıkların dahil edilmesi güçlü olsa da, kapakların altında neler olduğunu anlamak önemlidir.

Birden çok Include deyimine sahip bir sorgunun iç plan derleyicimizden geçerek depo komutunu oluşturması oldukça uzun sürer. Bu sürenin büyük bölümü, sonuçta elde edilen sorguyu iyileştirmeye çalışmak için harcanıyor. Oluşturulan depo komutu, eşlemenize bağlı olarak her Dahil Et için bir Dış Birleşim veya Birleşim içerir. Bunun gibi sorgular veritabanınızdan tek bir yükte büyük bağlı grafikler getirir ve bu da özellikle yükte çok fazla yedeklilik olduğunda (örneğin, ilişkileri bire çok yönde çapraz geçiş yapmak için birden çok Include düzeyi kullanıldığında) bant genişliği sorunlarını ortaya çıkarır.

ToTraceString kullanarak ve yük boyutunu görmek için SQL Server Management Studio'da depo komutunu yürüterek sorgu için temel alınan TSQL'e erişerek sorgularınızın aşırı büyük yük döndürdüğü durumları denetleyebilirsiniz. Böyle durumlarda, yalnızca ihtiyacınız olan verileri getirmek için sorgunuzdaki Include deyimlerinin sayısını azaltmayı deneyebilirsiniz. Veya sorgunuzu daha küçük bir alt sorgu dizisine bölebilirsiniz, örneğin:

Sorguyu bozmadan önce:

using (NorthwindEntities context = new NorthwindEntities())
{
    var customers = from c in context.Customers.Include(c => c.Orders)
                    where c.LastName.StartsWith(lastNameParameter)
                    select c;

    foreach (Customer customer in customers)
    {
        ...
    }
}

Sorguyu kırdıktan sonra:

using (NorthwindEntities context = new NorthwindEntities())
{
    var orders = from o in context.Orders
                 where o.Customer.LastName.StartsWith(lastNameParameter)
                 select o;

    orders.Load();

    var customers = from c in context.Customers
                    where c.LastName.StartsWith(lastNameParameter)
                    select c;

    foreach (Customer customer in customers)
    {
        ...
    }
}

Bağlamın kimlik çözümlemesini ve ilişkilendirme düzeltmesini otomatik olarak gerçekleştirmesi özelliğini kullandığımız için bu yalnızca izlenen sorgularda çalışır.

Yavaş yüklemede olduğu gibi, daha küçük yükler için daha fazla sorgu olacaktır. Her varlıktan yalnızca ihtiyacınız olan verileri açıkça seçmek için tek tek özelliklerin projeksiyonlarını da kullanabilirsiniz, ancak bu durumda varlıkları yüklemezsiniz ve güncelleştirmeler desteklenmez.

8.2.3 Özelliklerin yavaş yüklenmesini sağlamak için geçici çözüm

Entity Framework şu anda skaler veya karmaşık özelliklerin yavaş yüklenmesini desteklemez. Ancak, BLOB gibi büyük bir nesne içeren bir tablonuz varsa, büyük özellikleri ayrı bir varlığa ayırmak için tablo bölmeyi kullanabilirsiniz. Örneğin, varbinary fotoğraf sütunu içeren bir Product tablonuz olduğunu varsayalım. Sorgularınızda bu özelliğe sık sık erişmeniz gerekmiyorsa, varlığın yalnızca normalde ihtiyacınız olan bölümlerini getirmek için tablo bölmeyi kullanabilirsiniz. Ürün fotoğrafını temsil eden varlık yalnızca açıkça ihtiyacınız olduğunda yüklenir.

Tablo bölmeyi etkinleştirmeyi gösteren iyi bir kaynak, Gil Fink'in "Entity Framework'te Tablo Bölme" blog gönderisidir: <http://blogs.microsoft.co.il/blogs/gilf/archive/2009/10/13/table-splitting-in-entity-framework.aspx>.

9 Diğer önemli noktalar

9.1 Sunucu Çöp Toplama

Bazı kullanıcılar, Çöp Toplayıcı düzgün yapılandırılmadığında bekledikleri paralelliği sınırlayan kaynak çekişmesi yaşayabilir. EF çok iş parçacıklı bir senaryoda veya sunucu tarafı sistemine benzeyen herhangi bir uygulamada kullanıldığında, Sunucu Çöp Toplama'yı etkinleştirdiğinizden emin olun. Bu işlem, uygulama yapılandırma dosyanızdaki basit bir ayar aracılığıyla yapılır:

<?xmlversion="1.0" encoding="utf-8" ?>
<configuration>
        <runtime>
               <gcServer enabled="true" />
        </runtime>
</configuration>

Bu, iş parçacığı çekişmenizi azaltmalı ve CPU doygunluğuna sahip senaryolarda aktarım hızınızı %30'a kadar artırmalıdır. Genel olarak, uygulamanızın nasıl davrandığını her zaman klasik Çöp Toplama (kullanıcı arabirimi ve istemci tarafı senaryoları için daha iyi ayarlanmıştır) ve Sunucu Çöp Toplama'yı kullanarak test etmelisiniz.

9.2 AutoDetectChanges

Daha önce de belirtildiği gibi, Nesne önbelleğinde birçok varlık olduğunda Entity Framework performans sorunları gösterebilir. Ekle, Kaldır, Bul, Giriş ve SaveChanges gibi belirli işlemler, nesne önbelleğinin ne kadar büyük hale geldiğini temel alarak büyük miktarda CPU tüketebilecek DetectChanges çağrılarını tetikler. Bunun nedeni, nesne önbelleğinin ve nesne durum yöneticisinin, oluşturulan verilerin çok çeşitli senaryolar altında doğru olması için bir bağlama gerçekleştirilen her işlemde mümkün olduğunca eşitlenmiş kalmaya çalışmasıdır.

Entity Framework'ün otomatik değişiklik algılamasını uygulamanızın tüm ömrü boyunca etkin bırakmak genellikle iyi bir uygulamadır. Senaryonuz yüksek CPU kullanımından olumsuz etkileniyorsa ve profilleriniz suçlunun DetectChanges çağrısı olduğunu gösteriyorsa kodunuzun hassas bölümünde AutoDetectChanges'i geçici olarak kapatmayı göz önünde bulundurun:

try
{
    context.Configuration.AutoDetectChangesEnabled = false;
    var product = context.Products.Find(productId);
    ...
}
finally
{
    context.Configuration.AutoDetectChangesEnabled = true;
}

AutoDetectChanges'i kapatmadan önce, bunun Entity Framework'ün varlıklarda gerçekleşen değişikliklerle ilgili belirli bilgileri izleme yeteneğini kaybetmesine neden olabileceğini anlamak iyi olur. Yanlış işlenirse, bu durum uygulamanızda veri tutarsızlığına neden olabilir. AutoDetectChanges'i kapatma hakkında daha fazla bilgi için bkz <http://blog.oneunicorn.com/2012/03/12/secrets-of-detectchanges-part-3-switching-off-automatic-detectchanges/>. .

9.3 İstek başına bağlam

Entity Framework'ün bağlamlarının en iyi performans deneyimini sağlamak için kısa ömürlü örnekler olarak kullanılması amaçlanmaktadır. Bağlamların kısa ömürlü olması ve atılması beklenir ve bu nedenle çok basit olacak şekilde uygulanmıştır ve mümkün olduğunda meta verileri yeniden kullanır. Web senaryolarında bunu göz önünde bulundurmak ve tek bir isteğin süresinden daha uzun bir bağlama sahip olmak önemlidir. Benzer şekilde, web dışı senaryolarda, Entity Framework'teki farklı önbelleğe alma düzeylerini anlamanıza bağlı olarak bağlam atılmalıdır. Genel olarak bakıldığında, uygulamanın ömrü boyunca bağlam örneğinin yanı sıra iş parçacığı ve statik bağlamlar başına bağlamlara sahip olmaktan kaçınılmalıdır.

9.4 Veritabanı null semantiği

Entity Framework varsayılan olarak C# null karşılaştırma semantiğine sahip SQL kodu oluşturur. Aşağıdaki örnek sorguyu göz önünde bulundurun:

            int? categoryId = 7;
            int? supplierId = 8;
            decimal? unitPrice = 0;
            short? unitsInStock = 100;
            short? unitsOnOrder = 20;
            short? reorderLevel = null;

            var q = from p incontext.Products
                    where p.Category.CategoryName == "Beverages"
                          || (p.CategoryID == categoryId
                                || p.SupplierID == supplierId
                                || p.UnitPrice == unitPrice
                                || p.UnitsInStock == unitsInStock
                                || p.UnitsOnOrder == unitsOnOrder
                                || p.ReorderLevel == reorderLevel)
                    select p;

            var r = q.ToList();

Bu örnekte, bir dizi null atanabilir değişkeni varlıktaki SupplierID ve UnitPrice gibi null atanabilir özelliklerle karşılaştırıyoruz. Bu sorgu için oluşturulan SQL, parametre değerinin sütun değeriyle aynı olup olmadığını veya hem parametrenin hem de sütun değerlerinin null olup olmadığını sorar. Bu, veritabanı sunucusunun null değerleri işleme biçimini gizler ve farklı veritabanı satıcıları arasında tutarlı bir C# null deneyimi sağlar. Öte yandan, oluşturulan kod biraz karmaşıktır ve sorgunun where deyimindeki karşılaştırma miktarı çok fazla arttığında iyi performans göstermeyebilir.

Bu durumla başa çıkmanın bir yolu, veritabanı null semantiği kullanmaktır. Artık Entity Framework veritabanı altyapısının null değerleri işleme biçimini ortaya çıkaran daha basit BIR SQL oluşturacağı için bunun C# null semantiğinden farklı davranabileceğini unutmayın. Veritabanı null semantiği, bağlam yapılandırmasına karşı tek bir yapılandırma satırıyla bağlam başına etkinleştirilebilir:

                context.Configuration.UseDatabaseNullSemantics = true;

Küçük ve orta ölçekli sorgular, veritabanı null semantiği kullanılırken algılanabilir bir performans geliştirmesi görüntülemez, ancak çok sayıda olası null karşılaştırması olan sorgularda fark daha belirgin hale gelir.

Yukarıdaki örnek sorguda, denetimli bir ortamda çalışan bir mikrobenchmark'ta performans farkı %2'den azdı.

9.5 Zaman Uyumsuz

Entity Framework 6, .NET 4.5 veya sonraki sürümlerde çalışırken zaman uyumsuz işlemler için destek sunmuştur. Çoğunlukla, GÇ ile ilgili çekişmesi olan uygulamalar zaman uyumsuz sorgu kullanma ve kaydetme işlemlerinden en iyi şekilde yararlanır. Uygulamanız GÇ çekişmesi yaşamazsa, zaman uyumsuz kullanımı en iyi durumlarda zaman uyumlu olarak çalıştırılır ve sonucu zaman uyumlu bir çağrıyla aynı süre içinde döndürür veya en kötü durumda, zaman uyumsuz bir göreve yürütmeyi ertelemeniz ve senaryonuzun tamamlanmasına ek süre eklemeniz yeterlidir.

Zaman uyumsuz programlamanın, zaman uyumsuzun uygulamanızın performansını geliştirip geliştirmeyeceği konusunda karar vermenize yardımcı olacak şekilde nasıl çalıştığı hakkında bilgi için bkz . Async ve Await ile Zaman Uyumsuz Programlama. Entity Framework'te zaman uyumsuz işlemlerin kullanımı hakkında daha fazla bilgi için bkz . Zaman Uyumsuz Sorgu ve Kaydetme.

9.6 NGEN

Entity Framework 6, .NET framework'ün varsayılan yüklemesinde gelmez. Bu nedenle, Entity Framework derlemeleri varsayılan olarak NGEN'd değildir. Bu, tüm Entity Framework kodunun diğer MSIL derlemeleriyle aynı JIT'ing maliyetlerine tabi olduğu anlamına gelir. Bu, geliştirme sırasında F5 deneyimini ve ayrıca üretim ortamlarında uygulamanızın soğuk başlatmasını düşürebilir. JIT'ing'in CPU ve bellek maliyetlerini azaltmak için Entity Framework görüntülerini NGEN'e uygun şekilde göndermeniz önerilir. NGEN ile Entity Framework 6'nın başlangıç performansını geliştirme hakkında daha fazla bilgi için bkz . NGen ile Başlangıç Performansını Geliştirme.

9.7 Code First ile EDMX karşılaştırması

Entity Framework kavramsal modelin (nesneler), depolama şemasının (veritabanı) bellek içi gösterimine ve ikisi arasında eşlemeye sahip olarak nesne odaklı programlama ile ilişkisel veritabanları arasındaki empedans uyumsuzluğu sorununun nedenleri. Bu meta veriler Varlık Veri Modeli veya kısaca EDM olarak adlandırılır. Bu EDM'den Entity Framework, verileri bellekteki nesnelerden veritabanına ve geri döndürmek için görünümleri türetecektir.

Entity Framework kavramsal modeli, depolama şemasını ve eşlemeyi resmi olarak belirten bir EDMX dosyasıyla kullanıldığında, model yükleme aşamasının yalnızca EDM'nin doğru olduğunu doğrulaması gerekir (örneğin, eşlemelerin eksik olmadığından emin olun), sonra görünümleri oluşturun, sonra görünümleri doğrulayın ve bu meta verileri kullanıma hazır hale getirin. Ancak o zaman bir sorgu yürütülebilir veya veri deposuna yeni veriler kaydedilebilir.

Code First yaklaşımı, gelişmiş bir Varlık Veri Modeli oluşturucusunun merkezinde yer alır. Entity Framework'ün sağlanan koddan bir EDM üretmesi gerekir; bunu, modelde yer alan sınıfları analiz ederek, kuralları uygulayarak ve Fluent API aracılığıyla modeli yapılandırarak yapar. EDM oluşturulduktan sonra, Entity Framework temelde projede bir EDMX dosyasının mevcut olmasıyla aynı şekilde davranır. Bu nedenle, Code First'ten model oluşturmak, EDMX'e sahip olmakla karşılaştırıldığında Entity Framework için daha yavaş bir başlangıç süresine dönüşen ek karmaşıklık ekler. Maliyet, oluşturulan modelin boyutuna ve karmaşıklığına tamamen bağlıdır.

Code First yerine EDMX kullanmayı seçerken Code First tarafından sunulan esnekliğin modeli ilk kez oluşturma maliyetini artırdığını bilmeniz önemlidir. Uygulamanız bu ilk yüklemenin maliyetine dayanabiliyorsa genellikle tercih edilen yöntem İlk Kod olacaktır.

10 Performansı Araştırma

10.1 Visual Studio Profil Oluşturucu'yu Kullanma

Entity Framework ile ilgili performans sorunları yaşıyorsanız, uygulamanızın zamanını nereye harcadığını görmek için Visual Studio'da yerleşik olan gibi bir profil oluşturucu kullanabilirsiniz. Bu, Entity Framework'ün soğuk ve sıcak sorgular sırasında zamanını nerede geçirdiğini gösteren "ADO.NET Entity Framework'ün Performansını Keşfetme - Bölüm 1" blog gönderisinde ( <https://learn.microsoft.com/archive/blogs/adonet/exploring-the-performance-of-the-ado-net-entity-framework-part-1>) pasta grafikleri oluşturmak için kullandığımız araçtır.

Veri ve Modelleme Müşteri Danışmanlığı Ekibi tarafından yazılan "Visual Studio 2010 Profil Oluşturucu kullanarak Varlık Çerçevesi Profili Oluşturma" blog gönderisi, bir performans sorununu araştırmak için profil oluşturucuyu nasıl kullandıklarına ilişkin gerçek bir örnek gösterir.  <https://learn.microsoft.com/archive/blogs/dmcat/profiling-entity-framework-using-the-visual-studio-2010-profiler>. Bu gönderi bir Windows uygulaması için yazılmıştır. Bir web uygulamasının profilini oluşturmanız gerekiyorsa Windows Performans Kaydedicisi (WPR) ve Windows Performans Analizi (WPA) araçları Visual Studio'dan çalışmaktan daha iyi çalışabilir. WPR ve WPA, Windows Değerlendirme ve Dağıtım Seti'ne dahil edilen Windows Performans Araç Seti'nin bir parçasıdır.

10.2 Uygulama/Veritabanı profili oluşturma

Visual Studio'da yerleşik olarak bulunan profil oluşturucu gibi araçlar, uygulamanızın nerede zaman geçirdiğini size bildirir.  Çalışan uygulamanızın üretim veya üretim öncesi aşamalarında gereksinimlere bağlı olarak dinamik analizini gerçekleştiren ve yaygın tuzakları ve veritabanı erişiminin desenlerini engelleyen başka bir profil oluşturucu türü mevcuttur.

Ticari olarak kullanılabilir iki profil oluşturucu, Entity Framework Profiler ( <http://efprof.com>) ve ORMProfiler ( <http://ormprofiler.com>).

Uygulamanız Code First kullanan bir MVC uygulamasıysa StackExchange'in MiniProfiler'ını kullanabilirsiniz. Scott Hanselman bu aracı şu konumdaki blogunda açıklar: <http://www.hanselman.com/blog/NuGetPackageOfTheWeek9ASPNETMiniProfilerFromStackExchangeRocksYourWorld.aspx>.

Uygulamanızın veritabanı etkinliğinin profilini oluşturma hakkında daha fazla bilgi için Julie Lerman'ın Entity Framework'te Veritabanı Etkinliği Profili Oluşturma başlıklı MSDN Dergisi makalesine bakın.

10.3 Veritabanı günlükçü

Entity Framework 6 kullanıyorsanız yerleşik günlüğe kaydetme işlevini de kullanmayı göz önünde bulundurun. Bağlamın Database özelliği basit bir tek satırlı yapılandırma aracılığıyla etkinliğini günlüğe kaydetmesi için yönerge alabilir:

    using (var context = newQueryComparison.DbC.NorthwindEntities())
    {
        context.Database.Log = Console.WriteLine;
        var q = context.Products.Where(p => p.Category.CategoryName == "Beverages");
        q.ToList();
    }

Bu örnekte veritabanı etkinliği konsola kaydedilir, ancak Log özelliği herhangi bir Eylem<dizesi> temsilcisini çağıracak şekilde yapılandırılabilir.

Veritabanı günlüğünü yeniden derlemeden etkinleştirmek istiyorsanız ve Entity Framework 6.1 veya sonraki bir sürümünü kullanıyorsanız, uygulamanızın web.config veya app.config dosyasına bir kesme noktası ekleyerek bunu yapabilirsiniz.

  <interceptors>
    <interceptor type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework">
      <parameters>
        <parameter value="C:\Path\To\My\LogOutput.txt"/>
      </parameters>
    </interceptor>
  </interceptors>

Yeniden derlemeden günlüğe kaydetme ekleme hakkında daha fazla bilgi için adresine <http://blog.oneunicorn.com/2014/02/09/ef-6-1-turning-on-logging-without-recompiling/>gidin.

11 Ek

11.1 A. Test Ortamı

Bu ortam, istemci uygulamasından ayrı bir makinede veritabanıyla 2 makineli bir kurulum kullanır. Makineler aynı rafta olduğundan ağ gecikme süresi nispeten düşüktür ancak tek makineli bir ortamdan daha gerçekçidir.

11.1.1 Uygulama Sunucusu

11.1.1.1 Yazılım Ortamı
  • Entity Framework 4 Yazılım Ortamı
    • İşletim Sistemi Adı: Windows Server 2008 R2 Enterprise SP1.
    • Visual Studio 2010 – Ultimate.
    • Visual Studio 2010 SP1 (yalnızca bazı karşılaştırmalar için).
  • Entity Framework 5 ve 6 Yazılım Ortamı
    • İşletim Sistemi Adı: Windows 8.1 Enterprise
    • Visual Studio 2013 – Ultimate.
11.1.1.2 Donanım Ortamı
  • Çift İşlemci: Intel(R) Xeon(R) CPU L5520 W3530 @ 2,27GHz, 2261 Mhz8 GHz, 4 Çekirdek, 84 Mantıksal İşlemci.
  • 2412 GB RamRAM.
  • 136 GB SCSI250GB SATA 7200 rpm 3 GB/sn sürücü 4 bölüme ayrılmıştır.

11.1.2 VERITABANı sunucusu

11.1.2.1 Yazılım Ortamı
  • İşletim Sistemi Adı: Windows Server 2008 R28.1 Enterprise SP1.
  • SQL Server 2008 R22012.
11.1.2.2 Donanım Ortamı
  • Tek İşlemci: Intel(R) Xeon(R) CPU L5520 @ 2.27GHz, 2261 MhzES-1620 0 @ 3.60 GHz, 4 Çekirdek, 8 Mantıksal İşlemci.
  • 824 GB RamRAM.
  • 4 bölüme ayrılmış 465 GB ATA500GB SATA 7200 rpm 6 GB/sn sürücü.

11.2 B. Sorgu performansı karşılaştırma testleri

Bu testleri yürütmek için Northwind modeli kullanıldı. Entity Framework tasarımcısı kullanılarak veritabanından oluşturulmuştur. Ardından, sorgu yürütme seçeneklerinin performansını karşılaştırmak için aşağıdaki kod kullanıldı:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity.Infrastructure;
using System.Data.EntityClient;
using System.Data.Objects;
using System.Linq;

namespace QueryComparison
{
    public partial class NorthwindEntities : ObjectContext
    {
        private static readonly Func<NorthwindEntities, string, IQueryable<Product>> productsForCategoryCQ = CompiledQuery.Compile(
            (NorthwindEntities context, string categoryName) =>
                context.Products.Where(p => p.Category.CategoryName == categoryName)
                );

        public IQueryable<Product> InvokeProductsForCategoryCQ(string categoryName)
        {
            return productsForCategoryCQ(this, categoryName);
        }
    }

    public class QueryTypePerfComparison
    {
        private static string entityConnectionStr = @"metadata=res://*/Northwind.csdl|res://*/Northwind.ssdl|res://*/Northwind.msl;provider=System.Data.SqlClient;provider connection string='data source=.;initial catalog=Northwind;integrated security=True;multipleactiveresultsets=True;App=EntityFramework'";

        public void LINQIncludingContextCreation()
        {
            using (NorthwindEntities context = new NorthwindEntities())
            {                 
                var q = context.Products.Where(p => p.Category.CategoryName == "Beverages");
                q.ToList();
            }
        }

        public void LINQNoTracking()
        {
            using (NorthwindEntities context = new NorthwindEntities())
            {
                context.Products.MergeOption = MergeOption.NoTracking;

                var q = context.Products.Where(p => p.Category.CategoryName == "Beverages");
                q.ToList();
            }
        }

        public void CompiledQuery()
        {
            using (NorthwindEntities context = new NorthwindEntities())
            {
                var q = context.InvokeProductsForCategoryCQ("Beverages");
                q.ToList();
            }
        }

        public void ObjectQuery()
        {
            using (NorthwindEntities context = new NorthwindEntities())
            {
                ObjectQuery<Product> products = context.Products.Where("it.Category.CategoryName = 'Beverages'");
                products.ToList();
            }
        }

        public void EntityCommand()
        {
            using (EntityConnection eConn = new EntityConnection(entityConnectionStr))
            {
                eConn.Open();
                EntityCommand cmd = eConn.CreateCommand();
                cmd.CommandText = "Select p From NorthwindEntities.Products As p Where p.Category.CategoryName = 'Beverages'";

                using (EntityDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
                {
                    List<Product> productsList = new List<Product>();
                    while (reader.Read())
                    {
                        DbDataRecord record = (DbDataRecord)reader.GetValue(0);

                        // 'materialize' the product by accessing each field and value. Because we are materializing products, we won't have any nested data readers or records.
                        int fieldCount = record.FieldCount;

                        // Treat all products as Product, even if they are the subtype DiscontinuedProduct.
                        Product product = new Product();  

                        product.ProductID = record.GetInt32(0);
                        product.ProductName = record.GetString(1);
                        product.SupplierID = record.GetInt32(2);
                        product.CategoryID = record.GetInt32(3);
                        product.QuantityPerUnit = record.GetString(4);
                        product.UnitPrice = record.GetDecimal(5);
                        product.UnitsInStock = record.GetInt16(6);
                        product.UnitsOnOrder = record.GetInt16(7);
                        product.ReorderLevel = record.GetInt16(8);
                        product.Discontinued = record.GetBoolean(9);

                        productsList.Add(product);
                    }
                }
            }
        }

        public void ExecuteStoreQuery()
        {
            using (NorthwindEntities context = new NorthwindEntities())
            {
                ObjectResult<Product> beverages = context.ExecuteStoreQuery<Product>(
@"    SELECT        P.ProductID, P.ProductName, P.SupplierID, P.CategoryID, P.QuantityPerUnit, P.UnitPrice, P.UnitsInStock, P.UnitsOnOrder, P.ReorderLevel, P.Discontinued
    FROM            Products AS P INNER JOIN Categories AS C ON P.CategoryID = C.CategoryID
    WHERE        (C.CategoryName = 'Beverages')"
);
                beverages.ToList();
            }
        }

        public void ExecuteStoreQueryDbContext()
        {
            using (var context = new QueryComparison.DbC.NorthwindEntities())
            {
                var beverages = context.Database.SqlQuery\<QueryComparison.DbC.Product>(
@"    SELECT        P.ProductID, P.ProductName, P.SupplierID, P.CategoryID, P.QuantityPerUnit, P.UnitPrice, P.UnitsInStock, P.UnitsOnOrder, P.ReorderLevel, P.Discontinued
    FROM            Products AS P INNER JOIN Categories AS C ON P.CategoryID = C.CategoryID
    WHERE        (C.CategoryName = 'Beverages')"
);
                beverages.ToList();
            }
        }

        public void ExecuteStoreQueryDbSet()
        {
            using (var context = new QueryComparison.DbC.NorthwindEntities())
            {
                var beverages = context.Products.SqlQuery(
@"    SELECT        P.ProductID, P.ProductName, P.SupplierID, P.CategoryID, P.QuantityPerUnit, P.UnitPrice, P.UnitsInStock, P.UnitsOnOrder, P.ReorderLevel, P.Discontinued
    FROM            Products AS P INNER JOIN Categories AS C ON P.CategoryID = C.CategoryID
    WHERE        (C.CategoryName = 'Beverages')"
);
                beverages.ToList();
            }
        }

        public void LINQIncludingContextCreationDbContext()
        {
            using (var context = new QueryComparison.DbC.NorthwindEntities())
            {                 
                var q = context.Products.Where(p => p.Category.CategoryName == "Beverages");
                q.ToList();
            }
        }

        public void LINQNoTrackingDbContext()
        {
            using (var context = new QueryComparison.DbC.NorthwindEntities())
            {
                var q = context.Products.AsNoTracking().Where(p => p.Category.CategoryName == "Beverages");
                q.ToList();
            }
        }
    }
}

11.3 C. Navision Modeli

Navision veritabanı, Microsoft Dynamics – NAV tanıtımını yapmak için kullanılan büyük bir veritabanıdır. Oluşturulan kavramsal model 1005 varlık kümesi ve 4227 ilişkilendirme kümesi içerir. Testte kullanılan model "düz"dür; devralma eklenmemiştir.

11.3.1 Navision testleri için kullanılan sorgular

Navision modeliyle kullanılan sorgular listesi 3 varlık SQL sorgusu kategorisi içerir:

11.3.1.1 Arama

Toplama içermeyen basit bir arama sorgusu

  • Sayı: 16232
  • Örnek:
  <Query complexity="Lookup">
    <CommandText>Select value distinct top(4) e.Idle_Time From NavisionFKContext.Session as e</CommandText>
  </Query>
11.3.1.2 TekLi Toplama

Birden çok toplaması olan ancak alt toplamları olmayan normal bi sorgusu (tek sorgu)

  • Sayı: 2313
  • Örnek:
  <Query complexity="SingleAggregating">
    <CommandText>NavisionFK.MDF_SessionLogin_Time_Max()</CommandText>
  </Query>

Burada MDF_SessionLogin_Time_Max() modelde şu şekilde tanımlanır:

  <Function Name="MDF_SessionLogin_Time_Max" ReturnType="Collection(DateTime)">
    <DefiningExpression>SELECT VALUE Edm.Min(E.Login_Time) FROM NavisionFKContext.Session as E</DefiningExpression>
  </Function>
11.3.1.3 ToplamaToplamlar

Toplamalar ve alt toplamlar içeren bir BI sorgusu (tümünü birleşim yoluyla)

  • Sayı: 178
  • Örnek:
  <Query complexity="AggregatingSubtotals">
    <CommandText>
using NavisionFK;
function AmountConsumed(entities Collection([CRONUS_International_Ltd__Zone])) as
(
    Edm.Sum(select value N.Block_Movement FROM entities as E, E.CRONUS_International_Ltd__Bin as N)
)
function AmountConsumed(P1 Edm.Int32) as
(
    AmountConsumed(select value e from NavisionFKContext.CRONUS_International_Ltd__Zone as e where e.Zone_Ranking = P1)
)
----------------------------------------------------------------------------------------------------------------------
(
    select top(10) Zone_Ranking, Cross_Dock_Bin_Zone, AmountConsumed(GroupPartition(E))
    from NavisionFKContext.CRONUS_International_Ltd__Zone as E
    where AmountConsumed(E.Zone_Ranking) > @MinAmountConsumed
    group by E.Zone_Ranking, E.Cross_Dock_Bin_Zone
)
union all
(
    select top(10) Zone_Ranking, Cast(null as Edm.Byte) as P2, AmountConsumed(GroupPartition(E))
    from NavisionFKContext.CRONUS_International_Ltd__Zone as E
    where AmountConsumed(E.Zone_Ranking) > @MinAmountConsumed
    group by E.Zone_Ranking
)
union all
{
    Row(Cast(null as Edm.Int32) as P1, Cast(null as Edm.Byte) as P2, AmountConsumed(select value E
                                                                         from NavisionFKContext.CRONUS_International_Ltd__Zone as E
                                                                         where AmountConsumed(E.Zone_Ranking) > @MinAmountConsumed))
}</CommandText>
    <Parameters>
      <Parameter Name="MinAmountConsumed" DbType="Int32" Value="10000" />
    </Parameters>
  </Query>