İçeriğe geç

C# .NET ile PostgreSQL Stored Procedures: Dapper Kullanarak Pratik Kodlama Deneyimlerim

Geçenlerde bir proje üzerinde çalışırken, veritabanı işlemlerini hızlandırmak için stored procedure’lere başvurmak zorunda kaldım. Aslında her zamanki gibi basit bir CRUD operasyonuyla başlayacaktım ama veriler karmaşıklaşınca, PostgreSQL’in stored procedure’lerini kullanmak en mantıklısı gibi geldi. Ben de Dapper’ı devreye soktum, çünkü bu araçla hızlı ve temiz kod yazmayı seviyorum. Neyse efendim, o günün sonunda her şey yoluna girdi, ama başlangıçta biraz tökezledim tabii.

Stored procedure’ler hakkında konuşmak gerekirse, bunlar veritabanında önceden derlenmiş sorgular demek, değil mi? Yani sunucuda çalıştırdığında daha hızlı oluyor, özellikle büyük veri setleriyle uğraşırken. Bana göre, C# geliştiricileri için PostgreSQL ile entegre etmek mükemmel bir yol, çünkü hem güvenlik sağlıyor hem de kodun okunabilirliğini artırıyor. Bu arada, Dapper gibi bir micro-ORM kullanınca, her şey daha da kolaylaşıyor. Sen de muhtemelen benzer projelerde karşılaştın, değil mi?

Evet, projeme döneyim. REST API geliştirirken, kullanıcı verilerini işlemek için bir procedure yazdım. Önce PostgreSQL tarafında function oluşturmak gerekiyor, PL/pgSQL ile. Mesela, bir kullanıcı ekleme procedure’si için şöyle bir şey yaptım: CREATE OR REPLACE FUNCTION add_user(p_name TEXT, p_email TEXT) RETURNS INTEGER AS $$ BEGIN INSERT INTO users(name, email) VALUES(p_name, p_email) RETURNING id; END; $$ LANGUAGE plpgsql; Basit gibi görünüyor ama parametreleri doğru geçmek önemli.

Fakat burada bir duraksadım. Dapper ile çağırmak için ExecuteScalar mı yoksa Query mı kullanayım diye düşündüm. Neticede ExecuteScalar ile ID’yi döndürmek en iyisiydi. C# kodunda şöyle yazdım: using (var connection = new NpgsqlConnection(connectionString)) { var id = connection.ExecuteScalar(“SELECT add_user(@name, @email)”, new { name = “Test”, email = “test@example.com” }); } Evet gayet güzel çalıştı derken, parametre isimlerini eşleştirmeyi unutmamak lazım. (ki bu kısım bazen karıştırıyor insanı)

Bu arada aklıma geldi, geçen bir projede parametre tipini yanlış vermiştim, PostgreSQL hata verdi. Sanırım TEXT yerine VARCHAR koymuştum, neyse efendim düzelttim. Şimdi, stored procedure’lerin avantajlarını düşününce, SQL injection’a karşı koruma sağlıyor, bu yüzden REST API’lerde vazgeçilmez. Aslında ben her projede kullanmaya çalışıyorum, ama küçük uygulamalarda overkill olabiliyor galiba.

Dapper ile İleri Seviye Kullanım

Dapper’ı stored procedure ile kullanmak için QuerySingle veya Execute gibi metotlar var. Mesela bir liste döndüren procedure için: var results = connection.Query(“get_users”, commandType: CommandType.StoredProcedure); Burada commandType’ı belirtmek zorunlu, yoksa Dapper sorgu olarak algılıyor. Bana göre bu, kodu temiz tutuyor, hele ki Vue.js frontend’le entegre ederken.

Fakat dikkat et, PostgreSQL’de function’lar stored procedure gibi davranıyor, ama OUT parametreleri için biraz farklı. Bir keresinde output parametresi eklemek istedim, Dapper dynamic parameters ile hallettim. Yani: var parameters = new DynamicParameters(); parameters.Add(“p_id”, dbType: DbType.Int32, direction: ParameterDirection.Output); connection.Execute(“update_user”, parameters, commandType: CommandType.StoredProcedure); Sonra parameters.Get(“p_id”); ile alıyorsun. Garip değil mi, ama işe yarıyor.

Neyse, pratik bir örnek vereyim. Diyelim ki bir rapor procedure’si yazdın, birden fazla tablo join’liyor. PostgreSQL’de şöyle: CREATE OR REPLACE FUNCTION get_report(p_date DATE) RETURNS TABLE(id INT, name TEXT) AS $$ BEGIN RETURN QUERY SELECT u.id, u.name FROM users u JOIN orders o ON u.id = o.user_id WHERE o.date = p_date; END; $$ LANGUAGE plpgsql; C# tarafında: var report = connection.Query(“get_report”, new { date = DateTime.Now }, commandType: CommandType.StoredProcedure).ToList(); İşte bu kadar basit. Sen ne dersin, kolay mı?

Bu arada, PostgreSQL resmi belgelerinde daha detaylı örnekler var, bir bak derim. Gerçi ben genelde kendi testlerimle öğreniyorum, ama yeni başlayanlar için faydalı olur.

Hata Ayıklama ve İpuçları

Stored procedure’lerde hata debugging’i biraz zahmetli olabiliyor. Mesela, C# kodunda exception alıyorsun ama detay PostgreSQL log’unda. Ben pgAdmin kullanıyorum, console’da RAISE NOTICE ile debug mesajları ekliyorum. Neticede, geliştirme sürecini hızlandırıyor. Fakat bazen connection string’de charset uyumsuzluğu çıkıyor, UTF8’i doğru ayarlamak lazım.

Aslında bir keresinde, procedure içinde transaction kullanmayı unuttum, veri tutarsızlığı oldu. Dapper ile transaction’ı şöyle yönetiyorsun: using (var transaction = connection.BeginTransaction()) { connection.Execute(“procedure1”, transaction: transaction); connection.Execute(“procedure2”, transaction: transaction); transaction.Commit(); } Evet, güvenli hale geliyor böylece. (mağlum veri bütünlüğü önemli)

Bu konuda Google’da ‘dapper postgresql stored procedure example’ ara, bol kod örneği çıkıyor. Ben de oradan ilham aldım bazen. Açıkçası, Dapper’ın lightweight olması büyük avantaj, Entity Framework’e göre daha hızlı.

Şimdi, performans açısından bakarsak, stored procedure’ler indekslerle birleşince süper oluyor. Ama aşırı karmaşık yazarsan, bakım zorlaşıyor. Bana göre dengeli kullanmak en iyisi. Gerçi bazıları inline SQL’i tercih ediyor, ama ben stored procedure’ciyim.

Konudan biraz sapayım, geçen hafta bir elektronik devre tasarımı projesinde benzer bir mantık kullandım, gömülü sistemde veritabanı sorgusu gibi. Neyse, geri dönelim. Bir ipucu daha: Parametreli procedure’lerde null değerleri handle etmek için COALESCE kullan, PostgreSQL’de hayat kurtarıyor.

Sonuç olarak, C# .NET ile PostgreSQL stored procedure’lerini Dapper’la entegre etmek, REST API’lerimi güçlendiriyor. Eğer sen de benzer bir projede uğraşıyorsan, denemeni tavsiye ederim. Küçük başla, sonra genişlet. Neticede pratik yapmak en iyisi.

Bu arada, Dapper’ın GitHub sayfasında community örnekleri var, bir göz at. Veya ‘c# dapper transaction postgresql’ diye ara, pratik çözümler çıkıyor.

Geçen gün ailemle Bursa’da kısa bir kamp gezisi yaptık, dağcılık tutkumu biraz tatmin etti. Çadırı kurarken aklıma bir kodlama fikri geldi, ama tabii ki laptop’u yanımda değildi. Neyse efendim, döndükten sonra denedim: Bir PostgreSQL procedure’ünde loop hatası yapmıştım, sonsuz döngüye girmişti sunucuyu yoruyordu. Hemen debug edip düzelttim, win oldu işte :). Artık procedure’lerimi daha dikkatli test ediyorum.

O kamp sırasında, yıldızlara bakarken ailemle sohbet ettik, kodlama stresinden uzaklaştım. Fakat eve varır varmaz, o loop hatasını fark ettim – connection timeout vermişti. Dapper ile retry logic ekledim, sorun çözüldü. Küçük bir fail ama hızlı win, değil mi?