Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

C# – ExpandoObject İle Dinamik Nesne Oluşturma

Merhaba,

Hiç bir nesneyi runtime’da dinamik olarak tasarlama ihtiyacı hissettiniz mi? Ya da client’ın talebi üzerine üretilen data object içerisinde bulunacak olan alanların dışarıdan(client tarafından) belirlendiği senaryolara denk geldiniz mi? Bu yazıyı okuyorsanız muhtemeldir ki evet 🙂 O halde gelin, C# programlama dili aracılığıyla runtime’da dinamik nesne üretiminin inceliklerini tam teferruatlı değerlendirelim ve bahsedilen bu ihtiyaçlara ne şekilde aksiyon alınabileceğini teknik açıdan inceleyelim.

Nesneyi Dinamik Olarak Runtime’da Neden Oluşturmak İsteriz?

Biliyorsunuz ki, C#’ta bir nesne öncelikle modellenmeli ardından o modelden üretilen instance’lar üzerinden somut hale getirilmelidir.

    class Employee
    {
        public string Name { get; set; }
        public string Surname { get; set; }
        public int Age { get; set; }
    }

Yukarıdaki kod bloğunu incelerseniz eğer ‘Employee’ türünde nesne/instance oluşturulabilecek bir model tasarlanmıştır. Tasarlanan bu model üzerinden new Employee() komutunu kullanarak istediğimiz kadar somut nesne oluşturabilir ve bu değerler üzerinde işlemler gerçekleştirebiliriz. Burada dikkat edilmesi gereken husus, her bir ‘Employee’ türünden nesne içerisinde olabilecek alanlar net bir şekilde görülebilmektedir! Yani bizler herhangi bir ‘Employee’ nesnesini ele aldığımızda derleyici tarafından bu türdeki property’lerin neler olduğu bilinecektir. Çünkü bu nesnenin modellemesi derleme sürecinde tasarlanarak, compiler’a sunulmuştur. Haliyle ‘Employee’ türünden gelen tüm isteklerde yahut operasyonlarda bu tür içerisindeki member’lara rahatlıkla erişebilir ve işlevsellik gösterebiliriz.

Ama bazı özel durumlar vardır ki, bu şekilde derleme zamanında modellemesi yapılmış nesneler ihtiyacı giderememekte, bilakis ilgili modellerin çalışma zamanında üretilmesi gerekmektedir. Bunu daha iyi anlayabilmek için aşağıdaki örnek varsayım üzerinden istişare eylemek daha verimli olacaktır;

Çalıştığınız firmada bir ihtiyaca istinaden; gelen istekte sadece belirtilen kolonlara ait verilerin sorgulanarak, serialize edilip JSON olarak sunulmasını sağlayan bir API’ın geliştirilmesi talep edilmektedir.

Böyle bir talep neticesinde, veritabanından elde edilecek olan verilerin istekteki kolonlara göre şekilleneceği ve haliyle bu verileri karşılayacak veri modelinin opsiyonel olarak şekil değiştireceği aşikardır.

C# - ExpandoObject İle Dinamik Nesne Oluşturma

Misal, istekte sadece Ad ve Soyad bilgilerinin istenildiğini varsayarsak eğer üretilmesi gereken modelin sadece Ad ve Soyad bilgilerini taşıyacak property’lerden ibaret olması gerekmektedir ki serialize işlemi neticesinde görseldekine benzer bir JSON değer üretilebilsin.

C# - ExpandoObject İle Dinamik Nesne Oluşturma

Benzer şekilde Ad, Soyad ve Memleket bilgileri varsa bu sefer de, serialize neticesinde yine görseldeki gibi bir JSON’ın üretilmesi için dinamik olarak benzer özelliklere karşılık property’leri barındıran bir model tanımlaması gerekecektir.

Bu durumu genellediğimizde artık derleme sürecinde yapılacak herhangi bir modellemenin, bu isteği karşılayabilecek netlikte olmadığı, haliyle içerisindeki alanların opsiyonel olarak üretilmesi gerektiği için runtime’da dinamik bir şekilde tasarlanması gerektiği görülebilmektedir.

Yani anlayacağınız, talep neticesinde üretilecek datanın yapısı(içerisinde bulunacak alanları) dışarıdan belirlendiği durumlarda nesnelerimizi derleme aşamasında modellemek yerine, runtime’da dinamik bir şekilde üretebilme ihtiyacı hissetmekteyiz, ki ancak bu şekilde bir yaklaşımla bu tarz durumlara çözüm getirebilmekteyiz.

ExpandoObject Türü Nedir? Ne İşe Yarar?

ExpandoObject sınıfı, runtime’da herhangi bir object/instance üretmemizi sağlayan ve bu instance’ın member’larını yine runtime’da ekleyebilmemize yahut silmemize ve ayrıca bu member’ların değerlerini ayarlamamıza veya okumamıza olanak tanıyan dinamik bir nesnedir.

.NET’te ExpandoObject sınıfına, System.Dynamic namespace’i altından erişilebilmektedir.

ExpandoObject sınıfını JavaScript’te ki dot syntax(nokta sözdizimi) ile eşdeğer görebiliriz.

JavaScript’te bir objeye dot syntax sayesinde herhangi bir yeni property aşağıdaki gibi direkt eklenebilmektedir.
C# - ExpandoObject İle Dinamik Nesne Oluşturmaİşte bu özelliğin direkt olarak C#’ta ki muadili de ExpandoObject nesnesinidir. ExpandoObject ile dinamik oluşturulan nesneye object.property semantiği eşliğinde direkt bir property eklenebilmekte ve aynı şekilde ilgili property’nin değeri okunabilmektedir. Zaten bu davranışlara dair pratikleri yazımızın devamında tek tek örnekleriyle inceliyor olacağız. Şimdi gelin bir ExpandoObject nesnesinin nasıl oluşturulduğunu ve ne şekilde bir yapısının olduğunu inceleyerek devam edelim.

ExpandoObject Nesnesi Nasıl Oluşturulur ve Yapısı Nasıldır?

Bir ExpandoObject nesnesi oluşturabilmek için aşağıdaki semantikte instance üretilmesi gerekir.

            dynamic expandoObject = new ExpandoObject();

Yukarıda oluşturulan ExpandoObject nesnesi(new ExpandoObject()) dikkat ederseniz eğer direkt olarak dynamic anahtar sözcüğünden bir referansla işaretlenmiştir. Böylece ilgili nesnede dynamic keyword’ünün niteliği olan late binding(geç bağlama) özelliği etkinleştirilmektedir. İşte bu late binding özelliği sayesinde bu nesneye runtime’da member’lar eklenip, çıkarılabilecektir.

Peki bu ExpandoObject’e yeni bir member nasıl eklenir?
ExpandoObject’e yeni bir member eklemek için aşağıdaki gibi çalışılması yeterlidir.

            dynamic expandoObject = new ExpandoObject();
            expandoObject.X = "value1";
            expandoObject.Y = 0;
            expandoObject.Z = new List<object>();

Görüldüğü üzere ExpandoObject’e yukarıdaki gibi member’lar eklenebilmektedir. Tabi burada eklenen member’ların property olması sizleri yanıltmasın. İstendiği taktirde delegate eşliğinde aşağıdaki gibi metotlar da eklenebilmektedir.

            dynamic expandoObject = new ExpandoObject();
            expandoObject.Y = 0;
            expandoObject.W = (Action)(() => expandoObject.Y++);

Hatta ExpandoObject içerisine bir event’in nasıl eklendiğini de aşağıdaki örnek üzerinden inceleyebilirsiniz.

            dynamic expandoObject = new ExpandoObject();
            //Event'i karşılayacak property oluşturun ve ilk olarak null atayın.
            expandoObject.Event = null;
            //Sonra bir olay ekleyin.
            expandoObject.Event += new EventHandler((sender, e) =>
            {
                Console.WriteLine($"Event tetiklendi : {sender}");
            });

            //Olayı tetikleyin.
            expandoObject.Event("gençay", new EventArgs());

Peki ExpandoObject’in yapısı nasıldır?
ExpandoObject, kendisine runtime’da eklenen member’ları özünde bir dictionary’de tutmaktadır. Bunu görebilmek için ilgili dinamik nesneyi IDictionary<string, object> türüne cast etmemiz yeterli olacaktır.

            dynamic expandoObject = new ExpandoObject();
            expandoObject.X = "value1";
            expandoObject.Y = 0;
            expandoObject.Z = new List<object>();

            IDictionary<string, object> dataDictionary = (IDictionary<string, object>)expandoObject;
            foreach (KeyValuePair<string, object> item in dataDictionary)
                Console.WriteLine($"Key : {item.Key} - Value : {item.Value}");

C# - ExpandoObject İle Dinamik Nesne Oluşturma

Haliyle cast işlemi neticesinde dictionary olarak elde edilmiş ExpandoObject nesnesine ilgili koleksiyon üzerinden Add ve Remove fonksiyonları eşliğinde member eklenip, çıkarılabilmektedir.

            dynamic expandoObject = new ExpandoObject();
            expandoObject.X = "value1";
            expandoObject.Y = 0;
            expandoObject.Z = new List<object>();

            IDictionary<string, object> dataDictionary = (IDictionary<string, object>)expandoObject;
            dataDictionary.Add("W", "value 2");

Bu da member ekleme açısından ayrı bir yöntemdir.

ExpandoObject’te herhangi bir member nasıl tetiklenir?
ExpandoObject içerisindeki herhangi bir member’ı tetikleyebilmek klasik member access operatörü eşliğinde mümkündür.

            dynamic expandoObject = new ExpandoObject();
            expandoObject.X = "value1";

            Console.WriteLine(expandoObject.X);
Bir ExpandoObject Koleksiyonu Nasıl Serialize Edilir?

Bir ExpandoObject koleksiyonunu serialize edebilmek oldukça kolay bir uğraştır. Aşağıdaki örneği incelerseniz eğer JsonSerializer sınıfı aracılığıyla hızlı bir şekilde serialize işlemi gerçekleştirilebilir.

            dynamic person1 = new ExpandoObject();
            person1.name = "gençay";
            person1.surname = "yıldız";
            person1.age = 30;

            dynamic person2 = new ExpandoObject();
            person2.name = "mustafa";
            person2.surname = "yıldız";
            person2.age = 70;

            dynamic person3 = new ExpandoObject();
            person3.name = "lale";
            person3.surname = "cümbüloğulları";
            person3.age = 25;

            List<ExpandoObject> datas = new()
            {
                person1,
                person2,
                person3
            };


            var jsonData = JsonSerializer.Serialize(datas);
Bir JSON Data Nasıl ExpandoObject Olarak Deserialize Edilir?

Gelen bir JSON datayı karşılayacak modeliniz olmadığı durumlarda aşağıdaki gibi ExpandoObject olarak karşılayabilir ve hızlıca gerekli dönüşümleri sağlayabilirsiniz.

            HttpClient httpClient = new();
            //response type : userId, id, title, body
            HttpResponseMessage responseMessage = await httpClient.GetAsync("https://jsonplaceholder.typicode.com/posts");
            string jsonData = await responseMessage.Content.ReadAsStringAsync();

            dynamic data = JsonSerializer.Deserialize<List<ExpandoObject>>(jsonData);
            foreach (var item in data)
            {
                Console.WriteLine($"User Id \t: {item.userId}");
                Console.WriteLine($"Id \t: {item.id}");
                Console.WriteLine($"Title \t: {item.title}");
                Console.WriteLine($"Body \t: {item.body}");
            }
Member Değişikliklerinde Bildirim Alınması

ExpandoObject nesnesi içerisine herhangi bir member eklendiğinde yahut silindiğinde ya da var olan member’lardan birinin değeri değiştirildiğinde bu değişikliklerden haberdar olabilmek için INotifyPropertyChanged arayüzü aracılığıyla bildirimde bulunabilmektedir. Misal olarak aşağıdaki kod bloğunu inceleyebilirsiniz.

            dynamic employee = new ExpandoObject();
            ((INotifyPropertyChanged)employee).PropertyChanged += new PropertyChangedEventHandler((sender, propertyChagedEventArgs) =>
            {
                Console.WriteLine($"'{propertyChagedEventArgs.PropertyName}' property'si değişti.");
            });

            employee.name = "gençay";
            employee.surname = "yıldız";

            ((IDictionary<string, object>)employee).Remove("surname");

C# - ExpandoObject İle Dinamik Nesne Oluşturma

İlgilenenlerin faydalanması dileğiyle…
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