Entity Framework ve Reflection Dayanışması İle Model Güncelleme

Merhaba,

Yazılım projelerinde biz yazılımcılara zamansal, projeyede performans ve hız olarak her türlü artı kazandıran Entity Framework mimarisi oldukça uygun maliyetde bir inşa süreci geçirmemizi sağlamaktadır. Tabi bu süreç yer yer yaşanan aksaklıklar yahut mimarisel prosedürlerden kaynaklanan uzun uzun tanımlar, kompleks ve kullanışsız syntaxlar ile yapılar vs. gibi faktörlerin etkisiyle uzamakta, 1 olan zorluk nedensiz 3’e çıkmaktadır.

Bana sorarsanız Entity Framework mimarisinde yaşanan bu bahsettiğim handikaplara benzer olan onlarca durumdan bir taneside model güncelleme safhasıdır. Elinizdeki herhangi bir modeli düzenleyip, veritabanına fiziksel olarak bu değişikliği yansıtmanın en basit ve sade yolu aşağıdaki gibidir…(Dikkat : Bundan sonraki örneklendirmelerde Northwind veritabanı ve tabloları kullanılacaktır.)

    class Program
    {
        static void Main(string[] args)
        {
            NorthwindEntities VeriTabani = new NorthwindEntities();
            Personeller Personel = VeriTabani.Personeller.FirstOrDefault(p => p.PersonelID == 3);
            Personel.Adi = "Gençay";
            Personel.SoyAdi = "Yıldız";
            VeriTabani.SaveChanges();
        }
    }

Tabi ki de bu şekilde kullanım yerine daha sistematik çalışıp farklı sınıflarda bu işlemi fonksiyonel hale getirebilir, Base Class’lar üzerinde Update işlevselliğinde metotlar oluşturabilir ya da daha farklı fikirlerle hareket edebiliriz. Ben bu son dediğimi, yani farklı bir fikir ile bu işlemi gerçekleştirmeyi göstereceğim.

Reflection kütüphanesinin nimetlerinden faydalanarak Entity Framework üzerinde bir çalışma gerçekleştireceğiz. Oluşturacağım yapı birazdan da göreceğiniz üzere Generic Extension metot olarak tasarlanacak ve bu yüzden her model üzerinden direkt olarak erişilebilir olacaktır.

Hadi buyrun yapımızı inşa edelim.

    static class Extension
    {
        public static T ModelGuncelle<T>(this T EskiModel, T YeniModel, NorthwindEntities VeriTabani) where T : class, new()
        {
            EskiModel.GetType().GetProperties().ToList().ForEach(em => YeniModel.GetType().GetProperties().ToList().ForEach(ym =>
            {
                if (!em.Name.ToUpper().Contains("ID") || !ym.Name.ToUpper().Contains("ID"))
                    if (ym.Name == em.Name)
                        em.SetValue(EskiModel, ym.GetValue(YeniModel));
            }));

            VeriTabani.SaveChanges();

            return EskiModel;
        }
    }

Yukarıdaki kod bloğunda görmüş olduğunuz gibi “ModelGuncelle” Extension metodunu Generic bir şekilde oluşturmuş olduk. Metodun içeriğine bakarsak eğer, kullanıldığı modelin reflection ile propertylerine erişmekte ve parametreden gelen güncel modeldeki bilgileri tek tek ilgili modelin nesnesine set etmekte ve parametreden gelen o an kullanmakta olduğumuz veritabanı nesnesinin “SaveChanges” metodunu tetiklemektedir. Tabi bu işlemi yaparken “ıd” kolonlarının önlemi alınmakta ve gerekli algoritmik çalışmalar yapılmaktadır.

Şimdide “ModelGuncelle” metodunun kullanımını ele alalım.

    class Program
    {
        static void Main(string[] args)
        {
            NorthwindEntities VeriTabani = new NorthwindEntities();
            Personeller Personel = VeriTabani.Personeller.FirstOrDefault(p => p.PersonelID == 29);
            Personeller GuncelPersonel = new Personeller { Adi = "Gençay", SoyAdi = "Yıldız", Unvan = "Yazılım ve Veritabanı Uzmanı" };
            Personel.ModelGuncelle(GuncelPersonel, VeriTabani);
        }
    }

Görüldüğü üzere yapmış olduğumuz çalışma yukarıdaki gibi kolay ve pratik bir güncelleme işlevi sunmaktadır.

Bunun dışında yapımızı dahada geliştirmemiz gerekebilir. Örneğin aşağıdaki durumu ele alalım.

    class Program
    {
        static void Main(string[] args)
        {
            NorthwindEntities VeriTabani = new NorthwindEntities();
            Personeller Personel = VeriTabani.Personeller.FirstOrDefault(p => p.PersonelID == 29);
            Personeller GuncelPersonel = new Personeller { Adi = "Gençay", SoyAdi = null, Unvan = "Yazılım ve Veritabanı Uzmanı" };
            Personel.ModelGuncelle(GuncelPersonel, VeriTabani);
        }
    }

Dikkat ederseniz SoyAdi kolonu “null” değer almaktadır. Bu şekilde projemizi derleyip çalıştırdığımız zaman hata alacağız. Çünkü Northwind veritabanının Personeller tablosunda SoyAdi kolonu null olamaz. Bunun dışında null olarak ayarladığınız propertylerin aslında veritabanında bulunduğu şekilde kalmasınıda isteyebilirsiniz. O halde aşağıdaki gibi bir if kontrolü işinizi fazlasıyla görecektir…

    static class Extension
    {
        public static T ModelGuncelle<T>(this T EskiModel, T YeniModel, NorthwindEntities VeriTabani) where T : class, new()
        {
            EskiModel.GetType().GetProperties().ToList().ForEach(em => YeniModel.GetType().GetProperties().ToList().ForEach(ym =>
            {
                if (!em.Name.ToUpper().Contains("ID") || !ym.Name.ToUpper().Contains("ID"))
                    if (ym.GetValue(YeniModel) != null)
                        if (ym.Name == em.Name)
                            em.SetValue(EskiModel, ym.GetValue(YeniModel));
            }));

            VeriTabani.SaveChanges();

            return EskiModel;
        }
    }

İşte bu şekilde bir kontrolden sonra bir önceki vermiş olduğumuz null kullanım örneğinden beklenen hata alınmayacaktır.

Şimdi bir durumu daha konuşmak istiyorum ki o da modelimize eklenen harici propertylerdir. Kastettiğim herhangi bir modele partial oluşturarak bu modele ek propertylerde kazandırmış olabilirsiniz. Kazandırılan bu property read only ya da write only olabilir. Bu tarz durumlarda oluşturmuş olduğumuz “ModelGuncelle” metodu işlevselliğini yitirecek ve hata verecektir. Çünkü birazdan inceleyeceğimiz üzere şuan yapmış olduğumuz mevcut reflection çalışması ile kullanılan modellerin tüm propertyleri üzerinde gerekli şartlar doğrultusunda set ve get işlemleri gerçekleştirilmektedir. Ee haliyle düşünürseniz sadece get bloğu olan bir propertynin set bloğunun tetiklenmek istenmesi şuandaki algoritma inşamıza göre olasıdır.

Öncelikle gelin olası hatayı verecek durumu örneklendirecek bir yapı oluşturalım. Ardından çözümünü konuşalım.

    partial class Personeller
    {
        public int PersonelSatisSayisi { get; } = 0;
        public List<Satislar> PersonelinEnGozdeSatislari
        {
            set
            {
                 ...
            }
        }
    }

Yukarıdaki kod bloğunu incelerseniz eğer Personeller entitysine bir partial oluşturulmuş ve bu partial üzerinden Personeller sınıfına iki adet property eklenmiştir. Bu propertylerden biri get(read only) bir diğeride set(write only) olarak tasarlanmıştır.

Eğer ki biz metodumuzu Personeller modeli üzerinde derleyip çalıştırırsak yukarıdaki satırlarda da bahsettiğim gibi reflection işlemlerinde ilgili propertyler üzerinde set ve get işlemleri yapılmaya çalışılacaktır ve artık yapısına göre her iki propertyden de exception alınacaktır.

İşte bu olası durumu kontrol altına alabilmek için ben Attribute yöntemi tasarlamış bulunmaktayım. Tasarladığım bir attribute ile modeller içerisindeki propertyleri işaretlemekte ve reflection işlemleri sırasında da o anki property ilgili attribute ile işaretli ise işlemlere tabi tutulmamaktadır.

Öncelikle attributeu oluşturalım ve ardından yukarıda eklediğimiz propertyleri bu attribute ile işaretleyelim.

    partial class Personeller
    {
        [Model(false)]
        public int PersonelSatisSayisi { get; } = 0;
        [Model(false)]
        public List<Satislar> PersonelinEnGozdeSatislari
        {
            set
            {

            }
        }
    }

    class ModelAttribute : Attribute
    {
        public bool Durum { get; set; }
        public ModelAttribute() { }
        public ModelAttribute(bool Durum) => this.Durum = Durum;
    }

Görüldüğü üzere Model isminde bir attribute oluşturdum. İsmi ve içeriği çok önemli değil. Ben burada örnek olması açısından içerisine model attributeu ile işaretlenen propertynin durumunuda belirterek “bu property modeldir ya da değildir”i daha net diyebilmek için Durum bilgisi ekledim.

Şimdi sıra “ModelGuncelle” isimli Extension metodumuzu güncellemeye geldi.

    static class Extension
    {
        public static T ModelGuncelle<T>(this T EskiModel, T YeniModel, NorthwindEntities VeriTabani) where T : class, new()
        {
            EskiModel.GetType().GetProperties().ToList().ForEach(em => YeniModel.GetType().GetProperties().ToList().ForEach(ym =>
            {
                if (!em.Name.ToUpper().Contains("ID") || !ym.Name.ToUpper().Contains("ID"))
                    if (ym.Name == em.Name)
                    {
                        bool GetSetDurum = true;
                        var propertyattributes = em.GetCustomAttributes(true).ToList();
                        if (propertyattributes.Any() && propertyattributes != null)
                            propertyattributes.ForEach(attr =>
                            {
                                if (attr is ModelAttribute mattr)
                                    if (mattr.Durum == false)
                                        GetSetDurum = !GetSetDurum;
                            });

                        if (GetSetDurum)
                            if (ym.GetValue(YeniModel) != null)
                                em.SetValue(EskiModel, ym.GetValue(YeniModel));
                    }
            }));

            VeriTabani.SaveChanges();

            return EskiModel;
        }
    }

Yukarıdaki kod bloğunu incelerseniz eğer reflection ile propertylerini taradığımız modeldeki ilgili propertynin attributelarını elde ederek ModelAttribute olup olmadığına bakıyoruz, eğer Model attribute ise Durum propertysini kontrol edip false ise ilgili property üzerinde işlem yapmıyoruz.

İşte bu şekilde de güzel, temiz ve bir o kadar da korunaklı Entity Framework ve Reflection ikilisiyle modellerimizi fiziksel olarak güncellemiş oluyoruz.

Ayriyetten; güncelleme durumlarında illa bir harici modele bağlı kalmamak, sadece tek bir propertynin değerini güncellemek için “ModelGuncelle” metodunun aşağıdaki gibi bir overloadını oluşturabiliriz.

        public static T ModelGuncelle<T>(this T EskiModel, string PropertyAdi, object Deger, NorthwindEntities VeriTabani) where T : class, new()
        {
            EskiModel.GetType().GetProperties().ToList().ForEach(em =>
            {
                if (!em.Name.ToUpper().Contains("ID"))
                    if (PropertyAdi == em.Name)
                    {
                        bool GetSetDurum = true;
                        var propertyattributes = em.GetCustomAttributes(true).ToList();
                        if (propertyattributes.Any() && propertyattributes != null)
                            propertyattributes.ForEach(attr =>
                            {
                                if (attr is ModelAttribute mattr)
                                    if (mattr.Durum == false)
                                        GetSetDurum = !GetSetDurum;
                            });

                        if (GetSetDurum)
                            if (Deger != null)
                                em.SetValue(EskiModel, Deger);
                    }
            });

            VeriTabani.SaveChanges();

            return EskiModel;
        }

Bu overloadın da kullanılışı aşağıdaki gibi olacaktır.

    class Program
    {
        static void Main(string[] args)
        {
            NorthwindEntities VeriTabani = new NorthwindEntities();
            Personeller Personel = VeriTabani.Personeller.FirstOrDefault(p => p.PersonelID == 29);
            Personeller GuncelPersonel = new Personeller() { Adi = "Mehmet", SoyAdi = "Ahmet" };
            Personel.ModelGuncelle("Adi", "Hilmi", VeriTabani);

            //Veya

            Satislar Satis = VeriTabani.Satislar.First();
            Satis.ModelGuncelle("SatisTarihi", DateTime.Now, VeriTabani);
        }
    }

Evet sevgili okurlarım…
Ağır bir yazı olduğunun farkındayım. Ama tüm detayları aktarabilmek için bu şekilde makaleyi şişirmem gerektiğine umarım sizlerde kanaat getirmişsinizdir. Makale sürecinde içerikleriyle bize eşlik eden ve nihayetinde tam inşa edilen ilgili Extension metodunu aşağıda açık kaynak olarak sizlere sunuyorum.

Entity Framework – Reflection Extension Method dosyasını indirmek için tıklayınız.

Okuduğunuz için teşekkür ederim.
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

Bir Cevap Yazın

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

*

Copy Protected by Chetan's WP-Copyprotect.