Bir DBContext’in Çeşitli Senaryolar İçin Özelleştirilmesi

Selamlar,

Bu makalede, ihtiyaca göre çeşitli senaryolarda, .NET 5.0 üzerinde bir DBContext’i nasıl özelleştirebileceğimizi inceleyeceğiz.

İlk olarak eğer uygulamamız DBFirst ise, yani var olan bir DB üzerinde Entitylerimizi aşağıdaki gibi bir komut ile oluşturuldu ise, oluşan DBContext’den inheritance alınarak yeni bir CustomContext oluşturulur. Eğer bu işlem yapılmaz ise, var olan DBContext üzerinde yapılan değişiklikler, aşağıdaki komutun tekrarında ezilecektir.

DB/BlogContext : DBContext’den oluşturulan, tüm özelleştirmelerin yapılacağı custom context.


Resim kaynağı: https://woxapp.com/uploads/images/4_MVVM.png

1.Durum DB’de olmayan Custom bir Model’in DBContext’de varmış gibi gösterilmesi:

Amaç Repository katmanı kullanılarak, istenen Sql komutunun çalıştırılması ve geriye, Database’de olmayan bir ViewModel(CustomMenuModel)’in döndürülmesidir.

Model/CustomMenuModel.cs: Aşağıda görüldüğü gibi DB’de olmayan ViewModel, Menu oluşturmak amacı ile kullanılmıştır. BaseEntity’den türetilmiştir.

Repository katmanı aşağıda görüldüğü gibi BaseEntity’den türeyen bir sınıf beklemektedir. Bundan dolayı ilgili CustoMenuModel, BaseEntity‘den türetilmiştir.

Şimdi sıra geldi bu CustomModeli DBContex’e tanıtmaya.

Dashboard.DB/PartialEntites/BlogContext: Aşağıda görüldüğü gibi “CustomMenuModel“, Entity tarafında tanımlanırken hata alınmaması amacı ile, “OnModelCreating()” methodunda “entity.HasNoKey()” olarak işaretlenmiştir. BlogContext’e tanımlanan CustomMenuModel’e, repository katmanında artık rahatlıkla erişilebilecektir.

Service/MenuService.cs: Aşağıdaki örnekde, SlqDB tarafında tanımlı bir “fn.GetMenu()“‘den dönen function sonuçları, CustomMenuModel olarak geri dönülmüş ve Repository katmanında ilgili ViewModel,sanki DB’de varmış gibi, DBContext üzerinden sorgulama imkanı sağlamıştır.

Repository/GeneralRepository.cs => GetSql(): Aşağıda görüldüğü gibi, CustomMenuModel DB’de olmadığı halde “GetSql()” methodunda, BlogContext üzerinden tanımlı şekilde erişilebilmektedir. İlgili method’da kendisine parametre olarak verilen Raw SqlQuery, Execute edilmektedir. Ayrıca Repository’de kullanılacak tüm Entitylerin, BaseEntity sınıfından türetilmesi zorunlu hale getirilmiştir. Makalenin devamında, ilgili Entitylerin “BaseEntity“‘den nasıl türetildiği gösterilecektir.

2.Durum DBContext’de select işlemi yapılırken, işaretli Entitylerde, “Deleted” alanın “false” olduğu yani silinmemiş kayıtların getirilmesi:

Aşağıda görüldüğü gibi, “BlogContex” sınıfında “OnModelCreating()” methodunda, => “modelBuilder.AddGlobalFilter()” extension’ı aşağıdaki gibi çağrılmıştır. Amaç Global Filter ile “ISoftDeletable” interface’inden türeyen sınıflarda, query yazılırken sadece =>”Deleted == false” olan satırların gelmesinin sağlanmasıdır.

Dashboard.DB/PartialEntites/BlogContext:

DB/Extensions/ModelBuilderExtensions:

  • Aşağıda görüldüğü gibi AddGlobalFilter()’da “ISoftDeletable” interface’inden türeyen Entityler seçilir.
  • ModelBuilder extension olan “SetSoftDeleteFilter()” methodu çağrılır.
  • SetSoftDeleteFilter() methodu invoke edilir ve aşağıda görülen tanımlama ile filter anında, otomatik olarak “Deleted” property’si false olan kayıtlar çekilir.

Son olarak ilgili Entityler’in ISoftDeletable ve BaseEntity’den türetilmesi aşağıdaki gibi farklı bir partial class’da yapılmıştır.


Resim Kaynağı: https://colleeneakins.com/wp-content/uploads/2017/06/dont-get-deleted-mobile-app-feature.png

DB/PartialEntites/PartialEntites.cs: Aşağıda görüldüğü gibi DbUser “BaseEntity“‘den türetildiği için, Repository katmanında çağrılabilmiştir. Ayrıca “ISoftDeletable” interface’inden türediği için, select query’de “Deleted==true” olan kayıtlar global olarak filitrelenir ve gelmez.

Örnek kullanımda: Sadece silinmemiş User kayıtları aşağıdaki select sorgusundan gelir.
var userList = _context.DbUser.ToList(); 

DB/Extensions/ISoftDeletable.cs:

3.Durum DBContext’de yapılan her türlü Linq query işleminde, oluşan Sql Querylerin Output Window’da monitor edilmesi:

Öncelikle aşağıdaki paket projeye dahil edilmelidir.

Amaç Linq sorgularının sonucunda oluşan Sql Querylerin, Sql Profiler kullanılmadan, Visual Studio ortamında Output Window’da Global olarak monitor edilebilmesidir.

DB/PartialEntites/BlogContext.cs: Aşağıda görüldüğü gibi “BlogLoggerFactory” methodu ile, “LogLevel.Information” olacak şekilde OutPut Window’a oluşan Sql Query yazdırılır.

BlogContext/OnConfiguring(): Aşağıda görüldüğü gibi Debug modda, yukarıda tanımlanan “BlogLoggerFactory“, OnConfiguration’da “UseLogFactory()” methodunda çağrılmıştır. Böylece oluşan SqlQuery, Output window’a yazdırılmıştır. Release modda, performans amaçlı bu Log işlemi kapatılmıştır. Eğer istenir ise, EF 5.0 ile gelen IQueryable bir nesnenin Linq querysi sonuna, “.ToQueryString()” extension’ı konularak da oluşan SqlQuery string şeklinde alınabilir.

4.Durum DBContext Entity Katmanında, SaveChanges() Methodu çağrıldığında, Reporsitory Katmanı Kullanılmadan Araya Girip Log Alma:

DB/PartialEntites/CustomSaveChangesInterceptor.cs: Burada esas amaç, bir kaydetme işlemi olmadan önce, ilgili log’un arada başka bir işleme gerek duyulmadan alınabilmesidir.

  • “eventData.Context.ChangeTracker.DebugView.LongView”: Kaydedilen datanın, save işleminden önce yukarıda görüldüğü gibi önceki ve sonraki hallerine, property bazında erişilebilmektedir. Böylece, istenir ise tüm entity bazında “Audit Log” => “SaveChangesInterceptor” sınıfı sayesinde, kolaylıkla alınabilmektedir.

Yukarıda görülen Interceptor, “BlogContext” sınıfında aşağıda görülen “OnConfiguring()” methodunda tanımlanır.

BlogContext/OnConfiguring():

Geldik bir makalenin daha sonuna. Bu makalede, güncel hayatta ihtiyaç duyabileceğiniz bazı durumlara karşı, DBContext katmanını nasıl ve ne amaçlı özelleştirebileceğinize değinilmiştir. Bazen Global’da yapılan bu özelleştirmeler, proje genelinde hem kod tekrarından kurtaracak, hem zaman kazandıracak hem de tekbir yerden yönetilebildiği için, test ve debug işlemlerini hızlandıracaktır.

Yeni bir makalede görüşmek üzere hepinize hoşçakalın.

Kaynaklar:

Herkes Görsün:

Bunlar da hoşunuza gidebilir...

2 Cevaplar

  1. Onur Faruk dedi ki:

    Soft delete için esas bu harika:
    https://www.thereformedprogrammer.net/introducing-the-efcore-softdeleteservices-library-to-automate-soft-deletes/
    başka bir implementasyona gerek yok bence.

    • borsoft dedi ki:

      Benim örnekde SoftDelete yapmıyorum, soft delete olan kayıtları global olarak Linq sorguda getirmiyorm.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir