Uzun bir aradan sonra, şu günlerde sıkça uğraştığım Entity Framework‘ün yeni versiyonuyla gelen bir özellik ile ihmal ettiğim blog’u biraz hareketlendirmek istedim… Uzun süre yazmayınca, insan nasıl giriş yapacağını da bilemiyor. Neyse…Fazla uzatmadan konumuza gelelim.
Entity Framework’ün yeni versiyonu dedim ama 6.x çıkalı sanırım oldu bayaa. Hatta geçen hafta, en son 6.1.2 versiyonu yayınlandı. Entity Framework ilk çıktığında, genişletilebilirliği ve yaratılan sorgulara müdahale edilebilirliği çok konuşulmuştu. 4.x versiyonu ile beraber, çeşitli wrapper’lar ile belli noktalara müdahale etmek mümkün hale geliyordu. Resmi olarak EF ekibi tarafından geliştirilmiş olmasa da, eski o ekipde çalışan kişilerin geliştirdiği ve yayınladığı Provider Wrapper’lar kısmen de olsa çok hayat kurtarmıştı. Kısaca EF kullanan kişilerin, bazı noktalarda ciddi şekilde aralara girip müdahale etme ihtiyacı oluyordu.
Entity Framework 6.x ile built-in gelen “Interception” yaklaşımı bu ihtiyacı gerektiren durumlarda güzel bir çözüm olarak bizlere sunuldu. Interception yaklaşımı ile artık Entity Framework’ün veritabanına gittiği son noktalarda araya girmek ve müdahale etmek mümkün olabiliyor.
IDbCommandInterceptor interface’inden oluşturacağımız sınıfın ilgili metodlarını yazdığımız zaman, tüm “Execution” noktalarında müdahale etmek oldukça kolay.
Neden böyle bir şeye ihtiyacımız olsun ki?
Biraz daha gerçek dünya örneği vermek sanırım netleşmesi adına daha doğru olacaktır. Mesela EF ile yaptığınız sorguları kendi formatınızda istediğiniz yere log’lamak isteyebilirsiniz ya da en çok hangi parametreler ile veritabanına gidildiğini log’layıp, veritabanı tarafında buna göre INDEX çalışması yapabilirsiniz. Ya da sorgulanmaması gereken tablolar var ise bunlara çeşitli kontroller yaparak kısıtlamalar getirebilirsiniz.
Biraz daha net olması için, EF ile uğraşan herkesin ve hatta özellikle SQL Admin’lerin rahatsız olduğu alias ile ilgili bir örnek vermek isterim. Aşağıdaki sorgunun çıktısını eminim herkes tahmin edebilir.
var result = context.Students.Where(s => s.FirstName== "Arda").FirstOrDefault();
EF sorguları oluştururken, tablolara [Extendx] şeklinde bir alias verip bunlara göre sorguları oluşturmakta bildiğiniz üzere. Mesela [Extendx]’i bu Interception yaklaşımı sayesinde kaldırıp, istediğimiz değer ile değiştirmemiz mümkün.
internal class DatabaseAccessInterceptor : IDbCommandInterceptor { public void NonQueryExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext) { } public void NonQueryExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext) { } public void ReaderExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext) { } public void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext) { var alias = new Dictionary<string, string>(); var commantText = command.CommandText; var pattern = @"\[dbo\]\.\[([\w\d]*)\] AS \[(Extent\d*)\]"; var matches = Regex.Matches(commantText, pattern); foreach (Match match in matches) { if (match.Groups.Count == 3) { alias.Add(match.Groups[2].Value, match.Groups[1].Value); } } foreach (var pair in alias) { command.CommandText = command.CommandText.Replace(String.Format("[{0}]", pair.Key), String.Format("[{0}]", pair.Value)); } } public void ScalarExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { } public void ScalarExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { } }
Not: Yukarıdaki kod, tüm extend ifadelerini değiştirmez. Sadece örnek olması adına basitçe yazmak istedim. Genişletip kullanmak ödev olsun…
Yukarıdaki kodda dikkat ederseniz Scalar,Reader,NonQuery gibi ADO.NET’den tanıdık gelen ifadeler var. Bunlar Entity Framework’ün oluşturduğu sorgulara göre araya girebileceğimiz metodları temsil ediyor. Bunların içerisinde istediğimiz işlemleri yapabiliyoruz.
Peki bu yarattığımız sınıfları, EF Context’inde nasıl tanımlıyoruz? Bunun için EF 6.x ile beraber yine oldukça gelişen konfigürasyon özelliklerinden faydalanabiliyoruz. Kod tarafında context sınıfımız içerisinde aşağıdaki gibi bir tanımla, yazmış olduğumuz “Interception”nın kullanılacağını belirtebiliyoruz.
public SchoolContext(): base("SchoolContext") { DbInterception.Add(new DatabaseAccessInterceptor()); }
Ya da app.config/web.config içerisinde de aşağıdaki gibi belirtebiliyoruz.
<entityFramework> <interceptors> <interceptor type="{namespace.classname}, {assembly}"> </interceptor> </interceptors> </entityFramework>
Umarım bu basit ve kolay özellik, EF kullanıyorsanız yeni ve farklı çözümler yaratmanızda faydalı olur. Biraz hızlı oldu farkındayım ama takıldığınız ya da sormak istediğiniz bir şey olursa, yorum yazarak ya da e-mail atarak bana iletebilirsiniz.