Son zamanlarda severek kullanmaya başladığım Dapper Micro ORM’yi birlikte inceleyelim. Dapper Stackoverflow.com ekibi tarafından geliştirilmiş, ücretsiz ve açık kaynak kodlu bir c# kütüphanesidir. SQL Server, MySQL, Sqlite, SqlCE, Firebird.. vb veritabanlarına desteği bulunmaktadır. Dapper Klasik ORM’lere göre (Entity Framework, Nhibernate..) daha fazla kod hakimiyeti sunmaktadır. Bununla birlikte performans açısından oldukça iyi gözüküyor. Modellerimizi ilişkilerimizi biz kendimiz oluşturuyoruz. Dapper bize veritabanı ile model arasındaki etkileşimin kolaylaşmasını sağlıyor.
Dapper yüklemek için projemize sağ tıklayıp “Manage Nugget Packages” tıklıyoruz. Browse sekmesinde dapper yazıp aratıyoruz. kurulum işlemini yapıyoruz.
Önce klasik Datareader’la Ogrenci tablosundan verileri listeleyelim
string conStr = "Server=(local);Database=dbOgrenci; Trusted_Connection = True;"; using (SqlConnection db = new SqlConnection(conStr)) { List<Ogrenci> liste = new List<Ogrenci>(); if (db.State == ConnectionState.Closed) db.Open(); SqlCommand cmd = new SqlCommand(); cmd.Connection = db; cmd.CommandText = "Select * From Ogrenci"; SqlDataReader dr = cmd.ExecuteReader(); while (dr.Read()) { liste.Add( new Ogrenci() { ID_OGRENCI = Convert.ToInt32(dr["ID_OGRENCI"]), ADSOYAD = dr["ADSOYAD"].ToString(), ADRES = dr["ADRES"].ToString() }); } grid.DataSource = liste; }
Aynı işlemi Dapper ile yapalım
using (IDbConnection db = new SqlConnection(conStr)) { if (db.State == ConnectionState.Closed) db.Open(); var sonuc = db.Query<Ogrenci>("Select * From Ogrenci"); grid.DataSource = sonuc; }
İşimiz epeyce kolaylaşmış oldu. Burada “SqlConnection” sınıfına Extension method olarak eklenmiş olan “Query” medhodunu çağırıyoruz. Bu method bize Ogrenci tablosundaki verileri getiriyor ve Ogrenci modelimize otomatik çeviriyor. Bunun için tablo alan adlarımızla C# model adlarımızı aynı isimde yapıyoruz. Şimdi adım adım dapper methodlarını ve özelliklerini inceleyelim.
Parametre ile Tablodan Veri Sorgulamak
string conStr = "Server=(local);Database=dbOgrenci; Trusted_Connection = True;"; using (IDbConnection db = new SqlConnection(conStr)) { if (db.State == ConnectionState.Closed) db.Open(); var sonuc = db.Query<Ogrenci>("Select * From Ogrenci Where ID_OGRENCI=@ID_OGRENCI", new { @ID_OGRENCI=1 }); grid.DataSource = sonuc; }
Stored Procedure ile Veri Listelemek
string conStr = "Server=(local);Database=dbOgrenci; Trusted_Connection = True;"; using (IDbConnection db = new SqlConnection(conStr)) { if (db.State == ConnectionState.Closed) db.Open(); var sonuc = db.Query<Ogrenci>("spOgrenci", new { @ISLEM = 1 }, commandType: CommandType.StoredProcedure); grid.DataSource = sonuc; }
Stored Procedure ile Insert İşlemi
Tsql StoredProcedure
IF @ISLEM=2 BEGIN INSERT INTO Ogrenci(ADSOYAD, ADRES) VALUES(@ADSOYAD, @ADRES) SELECT CAST(SCOPE_IDENTITY() AS INT) AS ID_OGRENCI END
C# Kodu
string conStr = "Server=(local);Database=dbOgrenci; Trusted_Connection = True;"; using (IDbConnection db = new SqlConnection(conStr)) { if (db.State == ConnectionState.Closed) db.Open(); var sonuc = db.QueryFirstOrDefault<Ogrenci>("spOgrenci", new { ADSOYAD = txtAdSoyad.Text, ADRES = txtAdres.Text, ISLEM = 2 }, commandType: CommandType.StoredProcedure); MessageBox.Show(" Kaydedilen ID_OGRENCI: " + sonuc.ID_OGRENCI.ToString()); }
burada new{ … } kısmında elle parametreleri yazdık. Peki bunun daha kolay bir yolu var mı? Yani bize parametre ile Ogrenci türünde bir nesne geldiğinde bunu nasıl ekleyeceğiz?
Ogrenci eklenecekOgrenci = new Ogrenci(); eklenecekOgrenci.ID_OGRENCI = 1; eklenecekOgrenci.ADSOYAD = "Fatih Sarı"; eklenecekOgrenci.ADRES = "İstanbul"; using (IDbConnection db = new SqlConnection(conStr)) { if (db.State == ConnectionState.Closed) db.Open(); var sonuc = db.Execute("spOgrenci", eklenecekOgrenci , commandType: CommandType.StoredProcedure); }
Dapper bizim için eklenecekOgrenci nesnemizin property’lerini stored procedure parametreleri ile eşleştirebiliyor. Böylece çok fazla property’si olan modelleri bile Dapper eşleştirdiği için bize hızlı ve sade kod yazma avantajı sunuyor.
Birden Fazla Sonuç Kümesi Döndürme (Çoklu Sorgu)
string conStr = "Server=(local);Database=dbOgrenci; Trusted_Connection = True;"; using (IDbConnection db = new SqlConnection(conStr)) { if (db.State == ConnectionState.Closed) db.Open(); var sonuc = db.QueryMultiple("Select * From Ogrenci; Select * From Ogretmen;"); List<Ogrenci> ogrenciListesi = sonuc.Read<Ogrenci>().ToList(); List<Ogretmen> ogretmenListesi = sonuc.Read<Ogretmen>().ToList(); gridOgrenci.DataSource = ogrenciListesi; gridOgretmen.DataSource = ogretmenListesi; }
Burada hem öğrenci hemde öğretmen listesini listelemiş olduk. sonuc.Read komutu sayesinde bir sonraki sonuç kümesine geçip getirmeyi sağladık.
İlişkili Tablolardan Dönen Verileri Modellere Aktarmak
Şimdi şöyle bir senaryomuz olsun. Öğrenciye ait sinav sonuçlarını listeleyelim. Bu sınav sonuçlarının içine öğrenci nesnesini de dolduralım.
Öğrenci Model Class’ımız
public class OgrenciSinav { public int SINAVNO { get; set; } public int SONUC { get; set; } public Ogrenci ogrenci { get; set; } }
Listeleme Kodu
string conStr = "Server=(local);Database=dbOgrenci; Trusted_Connection = True;"; using (IDbConnection db = new SqlConnection(conStr)) { if (db.State == ConnectionState.Closed) db.Open(); string sql = @"Select * from OgrenciSinav Inner Join Ogrenci ON Ogrenci.ID_OGRENCI=OgrenciSinav.ID_OGRENCI"; var sonuc = db.Query<OgrenciSinav,Ogrenci,OgrenciSinav>(sql, (os,o)=> { os.ogrenci = o; return os; }, splitOn: "ID_OGRENCI"); }
Burada Inner join ile 2 tabloyu bağladık. Bu kod ile OgrenciSinav listesi getiriliyor. Her bir OgrenciSinav nesnesi içindeki ogrenci nesnesine ilişkili olan Ogrenci atanıyor. (os.ogrenci=o; return o;)
splitOn komutunu yazmamızın sebebi ilişkilendirdiğimiz sütunu dapper’e belirtiyoruz. splitOn kullanmazsak “System.ArgumentException” (Additional information: When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id) gibi bir hata alabiliriz.
Birden Fazla İlişkili Sonuç Kümesi Döndürme (Çoklu Sorgu)
Şimdi zor bir senaryo düşünelim.
Öğrenci listemiz olacak.
Her öğrenciye ait birden fazla evrak ve satış olacak.
Her satışa ait birden fazla ödeme planı olacak.
Dapper ve linq yardımıyla bunu yapalım.
List<Ogrenci> ogrenciListesi = new List<Ogrenci>(); List<Satis> satisListesi = new List<Satis>(); List<OdemePlan> odemePlanListesi = new List<OdemePlan>(); List<Evrak> evrakListesi = new List<Evrak>(); using (SqlConnection db = new SqlConnection(conStr)) { if (db.State == ConnectionState.Closed) db.Open(); string sql = @"SELECT * FROM Ogrenci SELECT * FROM Satis SELECT * FROM OdemePlan SELECT * FROM Evrak"; var sonuc = db.QueryMultiple(sql); ogrenciListesi = sonuc.Read<Ogrenci>().ToList(); satisListesi = sonuc.Read<Satis>().ToList(); odemePlanListesi = sonuc.Read<OdemePlan>().ToList(); evrakListesi = sonuc.Read<Evrak>().ToList(); } foreach (Ogrenci o in ogrenciListesi) { //Satışlar Ekleniyor var _satisList = satisListesi.Where(u => u.ID_OGRENCI == o.ID_OGRENCI).ToList(); if (_satisList != null) { o.SatisListesi = new List<Satis>(); o.SatisListesi.AddRange(_satisList); foreach (Satis s in _satisList) { //Satışa ait ödeme planları ekleniyor var _odemePlanList = odemePlanListesi.Where(u => u.ID_SATIS == s.ID_SATIS).ToList(); if (_odemePlanList != null) { s.OdemePlanListesi = new List<OdemePlan>(); s.OdemePlanListesi.AddRange(_odemePlanList); } } } //Evraklar ekleniyor var _evrakListesi = evrakListesi.Where(u => u.ID_OGRENCI == o.ID_OGRENCI).ToList(); if (_evrakListesi != null) { o.EvrakListesi = new List<Evrak>(); o.EvrakListesi.AddRange(_evrakListesi); } }
Dinamik Parametre ile Output ve Return Değişkenler Kullanmak
var p = new DynamicParameters(); p.Add("@A", 1); p.Add("@B", dbType: DbType.Int32, direction: ParameterDirection.Output); p.Add("@C", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue); db.Execute("spOgrenci", p, commandType: CommandType.StoredProcedure); int sonucB = p.Get<int>("@B"); int sonucC = p.Get<int>("@C");
QueryAsync ile Arka Planda İşlem Yapmak
Büyük boyutta veri listeliyorsanız bu yazılımınızın donmasına sebebiyet verebilir. Bunun için QueryAsync methodunu kullanarak verilerin arka planda gelmesini sağlayabilirsiniz.
public async void ListeleDapper() { string conStr = "Server=(local);Database=dbOgrenci; Trusted_Connection = True;"; using (IDbConnection db = new SqlConnection(conStr)) { if (db.State == ConnectionState.Closed) db.Open(); var sonuc = await db.QueryAsync<Ogrenci>("Select * from Ogrenci"); grid.DataSource = sonuc; } }
Not: Örneklerin yer aldığı C# uygulamasına Buradan ulaşabilirsiniz.
Dapper’in Açık Kaynak Kodlarına Buradan Ulaşabilirsiniz.