Dapper vs Entity Framework — Detaylı Benchmark Testleri

Salih Cantekin
9 min readFeb 21, 2022

Yazılımcılar olarak her zaman elimizden gelenin en iyisini yaparak ortaya verimli uygulamalar çıkartmaya çalışsak da, başka sistemlerle yapılan entegrasyonlar bazen elimizi kolumuzu bağlıyor. En çok entegrasyonu yapılan uygulamalar büyük ihtimalle veritabanlarıdır. Hal böyle olunca da veritabanları ile uygulamamız arasında köprü vazifesi gören ORM araçlarının önemi de artıyor. Her ne kadar Dapper ve EntityFramework araçlarının başka kategorilerde olduğuna inandığım için birbiri ile karşılaştırılmasını yanlış buluyor olsam da, bu yazımızda bazı karşılaştırma örneklerini detaylı bir şekilde göreceğiz. Yıllardır bir EF kullanıcısı olarak Dapper’ın tüm yönlerini bildiğimi söyleyemem maalesef. Bu sebeple eğer karşılaştırmada Dapper tarafına haksızlık ettiğim, yada yanlış bir kullanım yapmış olduğumu düşünen varsa lütfen bilgilendirsin beni. Bilmemek ayıp değil, değil mi :) Bu arada bu projenin kaynak kodlarına erişmek isterseniz sizi şöyle alayım.

🚪Giriş

Dapper StackOverFlow yazılımcıları tarafından geliştirilmiş micro-ORM sınıfındaki bir araç. Diğer tarafta da, .Net geliştiricileri tarafından en çok tercih edilen ORM lerden birisi olan ve Microsoft tarafından geliştirilen EntityFramework var. EF micro-ORM sınıfında değil, aksine tam kapsamlı bir ORM aracı. İşte tam olarak bu sebeple ikisinin birbiri ile karşılaştırılması bana yanlış geliyor ama buluştukları ortak noktalar da olduğu için burada bazı testler yapacağız. Bu testleri yaparken de alanınca çok iyi olan “BenchmarkDotNet” isimli bir kütüphaneyi kullanacağım. Bu araç hakkında bilgi almak isterseniz şurayı ziyaret edebilirsiniz veya doküman okumak yerine videosunu izlemek isterseniz sizi şuraya alayım.

Proje Tanımı

Bu proje bünyesinde 7 farklı kategoride testler gerçekleştireceğiz. Her kategoride, o kategori ile ilişkili testler olacak. Bu testlerde hem Dapper hem de EF’i test ediyor olacağız.

Not: İlişkisel verilerin eklenmesi, güncellenmesi, getirilmesi gibi işlemleri bu proje bünyesinde yapmayacağız.

Test Kategorileri; Insert, InsertMany, Update, Select, Search, Function, Delete

✔️Test Hazırlığı

Proje Tarafı; Uygulamamız NET6 ile geliştilmiş bir Console uygulaması olacak.

Dapper Tarafı; Dapper v2.0, Dapper.FluentMap v2.0, Dapper.FluentMap.Dommel v2.0.

Şunu söylemeliyim ki, Dapper’ı ve Dapper ile ilgili Mapping işlemlerini öğrenip kullanırken çok zorlandım. Birden fazla extension kütüphanesi var ve sıra ile hepsini denedim. EF ile Dapper arasında adil bir ortam oluşturabilmek için ortak bir model kullandım ve bu modelin mapping işlemlerini de ikisi için de aynı olacak şekilde tamamlamak gerekiyordu. Yaptığım testlerde Dapper için geliştirilen yardımcı kütüphanelerin eksiklerini fark edip değiştirmek zorunda kaldım. En sonunda “Dommel” kütüphanesinde karar kıldım. EntityFramework de ise bu özellikler gömülü olarak geliyor.

EF Tarafı; EntityFramework’un 6.0 versiyonu kullanılacak

Veri Oluşturma Tarafı; Veritabanına işlemlerinde kullanılmak üzere veri oluşturma konusunda gayet başarılı olan bir araç kullanacağım. Daha önce detaylıca anlattığım bir araç olan Bogus.

Veritabanı Tarafı; Veritabanı olarak yerel bilgisayarımdaki Docker üzerinde bir Container olarak çalışan SqlServer 2019(x64) versiyonunu kullanacağım. Bu veritabanında ise Student isimli bir tablo var. Detayları şu şekilde.

Student Tablosu Detayları

📓Başlamadan Önce, Önemli Notlar

Bu proje bünyesinde her iki aracın da ortak olarak sahip olduğu özellikleri test etmeye çalışacağım. Dapper extension kütüphanelerle geliştirilebiliyor ancak tek bir kütüphane kullanarak bu testleri yapmak istedim çünkü EF için de başka kütüphaneler kullanarak performans kazancı yaratabiliriz. Dapper için en kullanışlı olan Dommel kütüphanesini seçtim. Her iki araçta ortak olan özellikleri test ettiğimiz için veritabanlarına bağlantılarda kullanılacak olan Connection/DbContext nesneleri bu testler boyunca Singleton Pattern içerisinde kullanıldı. Test başlarken bir kez yaratıp tüm testlerde bu nesneleri kullandım. Çünkü Connection veya DbContext’lerin oluşturulması işlemleri uygulamayı kullandığımız ortamlara göre değişiklik gösterebilir. Örnek olarak .Net içerisindeki Dependency Injection ile DbContextPool kullanarak bağlantılarımızın daha verimli olması sağlanabilir.

Dapper ilk çıktığı günden bugüne kadar hep hızlıydı. Asıl geliştirilme amacı da buydu zaten. EF tarafında ise, yazılımcıların hayatlarını kolaylaştırmak ön plandaydı. Bunu yaparken çok yavaş çalıştığı zamanlar da oldu ancak günden güne Microsoft mühendisleri ve açık kaynak koduna gönül vermiş insanların da çabalarıyla hızına hız kattı. Yakın zamanda EF7 geliyor ve içerisinde performans iyileştirmeleri dışında bir sürü yenilik de içeriyor olacak. Belki EF7 ile birlikte yeniden bir test yapabiliriz :)

🥪Insert Test

Job Tanımı

Bu testimizde hem Dapper hem de EF içinde gelen Insert metodlarının testini yapacağız. Sadece Student objesi üzerinden kayıt işlemi gerçekleştirmeyeceğiz, aynı işi bir de ham SQL cümlesi kullarak da yapacağız. Çoklu kayıt için ayrı bir testimiz olduğu için bu test içerisinde her seferinde tek bir kayıt işlemi gerçekleştireceğiz. Buradaki testimiz tamamlandıktan sonra tablomuzda 4040 adet kayıt oluşmuş oluyor. Test sonuçlarına bir göz atalım.

Single Insert Test Result
Tek Kayıt Ekleme Test Sonuçları

Sonuçlardan da anlayacağımız üzere Dapper burada EF den daha hızlı. Nesne üzerinden insert işlemi gerçekleştirdiğimizde farkın biraz daha fazla olduğunu görebiliriz ancak ham SQL cümlesi kullarak yaptığımız işlemlerde neredeyse aynı sonuçları elde ediyoruz. Bunun dışında en son sütunda görüleceği üzere Dapper insert işlemlerindeki bellek yönetimi konusunda da EF’den az bir farkla önde. Yanlız dikkatinizi çekmek istediğim başka bir nokta var. Aşağıdaki görüntüde, test işlemleri sırasında oluşan kayıtlardan aykırı olanlara ait bilgiler var. Yani diyelim ki 100 tane kayıt eklemişiz ve 90 tanesi ortalama 8ms civarında tamamlanmış ama kalan 10 tanesi de 25ms civarında sürmüş. İşte bu durumun genel ortalamayı etkilememesi için genelde ortalama içerisinde dahil edilmezler ve kayıtlar arasından çıkartılırlar. Bu outlier sayısı ne kadar fazlaysa sistemimiz o kadar AZ stabil çalışıyordur. Bu sayıların az olması bizim için daha iyi yani. Bu sonuçlara bakacak olursak EF’in burada Dapper’dan biraz daha stabil bir duruş sergilediğini söyleyebiliriz.

Tek Kayıt Ekleme Aykırı Veriler

🥪Insert Many Test

Job Tanımı

Bu testimizde veritabanına çoklu kayıtlar ekleyeceğiz. Sırası ile önce 10, 100 ve 1000 kayıt aynı anda eklenmeye çalışılcak. Ham SQL cümlesi ile değil, direk Dapper ve EF’in içerisinde gelen InsertMany ve AddRange metodlarını kullanıyoruz. Üçüncü parti kütüphaneler kullanarak BulkInsert metodunu elde edip daha hızlı sonuçlar elde edebiliriz ancak built-in olarak gelen metodları kullanıyoruz. Bu testimiz tamamlandıktan sonra tablodaki kayıt sayımız 126140 oluyor.

Çoklu Kayıt Test Sonucu

Burada ise bambaşka bir sonuç görüyoruz. EntityFramework’ün Dapper’dan daha iyi çoklu ekleme performansı gösterdiğini görüyoruz ve veritabanına aynı anda eklenen kayıt sayısı arttıkça Dapper’ın performansının daha kötüye gittiğini de görüyoruz. 10 kayıt eklenirken EntityFramework Dapper’dan neredeyse iki katı kadar, 100 kayıt için 10 katına yakın ve 1000 kayıt için ise 20 katı kadar hızlı görünüyor. Her ne kadar EF daha hızlı olsa da bellek yönetimi konusunda Dapper’ın yine daha iyi olduğunu görüyoruz.

🥪Update Test

Job Tanımı

Sıra güncelleme testimizde. Kayıt ekleme (Insert) testinde olduğu gibi burada da hem kütüphaneler içerisinde gelen nesne ile Update metodlarını kullanacağız hem de ham SQL cümlesi kullarak güncelleme işlemini test edeceğiz. Update edeceğimiz kayıtların veritabanından getirilmesi işlemleri test sonuçlarını etkilemesin diye, test başlamadan önce veritabanından rastgele 1000 tane kayıt çekip belleğe alıyoruz ve Update etme sırasında buradan almış olduğumuz kayıtları kullanıyoruz. Diğer taraftan ham SQL cümlesi kullarak yapacağımız güncelleme işlemlerinde ise rastgele ID lere sahip olan kayıtların “FirstName” parametresiniz “XXX” olarak işaretliyoruz. Sonuçlara göz atalım.

Güncelleme Test Sonuçları

Güncelleme işlemlerinde her iki yöntemde de Dapper’ın az bir farkla EF’den daha hızlı olduğunu görebiliriz. Bu aykırı bir sonuç muhtemelen ancak EF ile nesne kullarak yaptığımız güncelleme işlemlerinde 100kb bellek kullandığını görmek beni şaşırttı. Diğer taraftan ham SQL cümlesi ile yaptığımız güncelleme işlemlerinde Dapper’ın stabil bir görüntü ortaya koymadığını da aşağıdaki ekran görüntüsünde görebiliyoruz.

Güncelleme Test, Aykırılıkları

🔎Select Test

Job Tanımı

Select testi Search testi gibi en önemli testlerden birisi. Çünkü veriyi okuma işlemleri genel olarak veri oluşturma veya güncelleme işlemlerinden daha çok kullanılıyor. Buradaki performanslar projenin büyük bölümünü etkileyebilir. Find metodları ID ile kayıtlara erişmemizi sağlıyor. SingleOrDefault metodunu ham SQL sorgusu ile kullanıyoruz ve şartımıza uyan tek kaydı getiriyoruz. Veritabanından rastgele çekilmiş bir isim bilgisi ile uyuşan tüm kayıtları veritabanından çekmek için ise LinQ ve ham SQL kullarak sorgulama yapıyoruz. Bunun dışında tablodaki tüm kayıtların getirilmesi işlemlerini de ekliyoruz. Bu çok fazla kullanılan bir yöntem olmayacaktır çünkü 126140 kaydın uygulama seviyesine çekilmesi her zaman karşımıza çıkmaz :) Gelin sonuçları inceleyelim.

Select Test Sonuçları

Tüm kayıtların getirilmesi işlemlerinde Dapper’ın EF’den neredeyse iki katı kadar hızlı olduğunu görüyoruz. Bellek konusunda da yine daha iyi. Ancak bunun tercih sebebi olmayacağından bahsetmiştim. Geriye kalan 4 farklı testten 2 ünde EntityFramework’ün Dapper’dan daha hızlı çalıştığını görebiliriz. Kalan 2 sinde ise Dapper’ın performanslı çalıştığını görebiliriz. Ham sorguların çalıştırılması işlemlerinde ise EntityFramework’ün daha verimli çalıştığını söyleyebiliriz.

🔎Search Test

Job Tanımı

Select sorguları kadar search sorguları da bizim için gayet önemli. Bu testimizde veritabanındaki kayıtlar arasında aramalar yapacağız. Veritabanı performansından ziyade, kullandığımız ORM araçlarının bu şartların sql’le dönüştürülmesi işlemlerinde ne kadar iyi olduğunu ölçmüş olacağız. Sonuçlara bir göz atalım.

Search Test Sonuçları

Karşılaştırılmaya eklenmemesi gerekiyor ancak burada EntityFramework için bir avantaj ortaya çıkıyor. Kullandığımız veritabanına göre şekillenen özelliklere sahip bir yapısı ortaya çıkıyor EF tarafında. EF.Functions altındaki metodları kullandığımızda yazmış olduğumuz LinQ sorguları C# tarafında değil veritabanında kullanılmış oluyor. Dolayısı ile veritabanı fonksiyonları veriyi getirmede daha hızlı davranıyor. “Mehmet” ismine sahip kullanıcıların sayısını hesapladığımız testimizin kazananı az bir farkla Dapper oluyor. Bunun dışında “A” harfi ile başlayan kayıtların sayısını hesaplarken EF’in Dapper’dan yaklaşık iki katı kadar hızlı olduğunu görüyoruz. EF.Functions ile kullanıldığında ise fark daha da büyüyor. İçerisinde “A” harfi geçen kayıtların sorgulanması konusunda EF yine Dapper’dan daha hızlı olduğunu gösteriyor. EF.Functions için ise durum yine biraz daha hızlı olarak karşımıza çıkıyor. İki tarih arasındaki kayıtların sayısının hesaplanması konusunda ise Dapper’ın daha hızlı olduğunu ancak bellek yönetimi tarafında bu testte sınıfta kaldığını söyleyelim.

🌀Function Test

Job Tanımı

Bu testimizde sayfalama verilerinin getirilmesi ve tüm kayıtların sayısının hesaplanması testlerini yapacağız. Sonuçlara bir göz atalım.

Functions Test Sonuçları

Her üç testte de Dapper’ın az bir farkla daha hızlı olduğunu söyleyebiliriz. Bellek yönetimi konusunda ise yine daha iyi bir iş ortaya çıkarıyor. Ancak yine Outliers kısmında çok stabil bir görüntü ortaya koymuyor Dapper.

Outliers

⛔Delete Test

Job Tanımı

Bu testimizde kayıt silme işlemlerini, Insert ve Update işlemlerinde yaptığımız gibi hem nesne kullanarak hem de ham SQL cümlesi ile yapacağız. Sonuçlara bir göz atalım.

Kayıt Silme Test Sonuçları

Yine benzer sonuçlar elde ettik. Nesne ile silme işleminde Dapper’ın, ham SQL cümlesi ile EF’in daha verimli olduğunu görüyoruz. Dapper yine bellek yönetimi konusunda daha iyi olduğunu gösteriyor.

🤗Düşünceler

Daha önce de belirttiğim gibi bu iki aracın birbiri ile karşılaştırılmaması gerektiğini düşünüyorum. Çünkü her iki araç da kendi alanlarında diğerinden daha iyi yönlere sahip. Ancak şunu da belirtmek gerekir. EF çok yol katetti kendini çok fazla geliştirdi. EF gibi bir full-ORM aracınız varsa bu aracın bize sunduğu çok çok önemli özelliklere sahip olduğunu da unutmamak lazım. ChangeTracker, Split Queries, GlobalFilters, Transaction Management, Relational Insert, Computed Values, Complex Queries, Configurable Migrations, Migration Management, Db-First / Code-First Flexibility gibi bir çok önemli özelliği sayabiliriz. Her yeni versiyonda daha yeni özellikler de geliyor. (EF7 ile birlikte Bulk işlemler gibi). Bu iki aracı birbiri yerine kullanmaktansa birlikte kullanabileceğimiz yapılar da ortaya çıkartabiliriz. CQRS Pattern’i veya Repository Pattern’i kullarak her iki araçtan iyi olduğu alanlarda faydalanabiliriz. Ben yine büyük çoğunlukla EntityFramework kullanmaya devam edeceğim. Yukarıda saydığım bir çok önemli özelliğinden faydalanıyorum ve ufak tefek performans kayıplarını tolere ediyor bu sayede. Hem bakarsınız performans konusunda daha iyiye gider ve hem süper özellikleri olan hem de hızlı bir araç kullanmış oluruz :)

Bir sonraki yazımızda görüşmek üzere.

--

--