İlkay İlknur

just a developer...

WCF Servislerinde Generic Parametre Kullanımı

Merhaba Arkadaşlar,

Günlük uygulama geliştirme sürecimiz içerisinde en çok kullandığımız yapılardan birisi de servisler olmakta. Servis kelimesini kullanınca da .NET Framework tarafında aklımıza hemen WCF gelmekte. Bunun yanında uygulamalarımızı geliştirirken en çok kullandığımız yapılardan biri de generic tipler. Genericler bildiğiniz üzere C# 2.0 ile beraber dilin içerisinde katılmakla beraber uygulamaların genişletilmesi ve daha esnek bir hale getirilmesi genericlerin dile katılması ile beraber kolaylaştı. Bu makalemizin konusu ise aslında generic tipler ile WCF servislerinin birleşiminden oluşmakta.

Örnek senaryomuzu ise şu an şirkette geçici süreliğine atandığım ASP.NET MVC projesi üzerinde yapılan bir uygulamadan yola çıkarak vermek istiyorum. Öncelikli olarak senaryoyu aktararak başlayalım. Geliştirilen web uygulamalarının herhangi bir şekilde veritabanına erişimi bulunmadığından dolayı web uygulamaları içerisindeki tüm business ve veritabanı işlemleri WCF servisleri tarafından gerçekleştirilmekte. Bu doğrultuda da yapılan işlemler farklı servisler tarafından dışarıya açılmakta. Ancak metotlara baktığımızda gerçekleştirilmiş olan tüm metotların tanımlanan bir Request sınıfını parametre olarak aldığını ve işlemini tamamladığında da Response ismiyle tanımlanan bir nesne örneğini geri döndürdüğünü görmekteyiz. Böylece eğer bir metoda parametre göndermek istiyorsak ilgili parametreyi Request sınıfı içerisinde bulunan bir object property içerisine koyup servis tarafına göndermekteyiz. Servis tarafından ise gerekli Convert işlemi gerçekleştirilerek gereken adımlar uygulanmakta.

Tam da geçtiğimiz günlerde sabah serviste gelirken bu senaryoyu gözlerimi kapatıp canlandırırken birden aklıma başka bir yöntem geldi.(Tabi "serviste başka işin yok muydu da bunları düşünüyorsun uyusaydın ya" da diyebilirsiniz)Smile Her ne kadar object ile ilgili parametreyi alma yöntemini daha önce kendi projelerimde kullanmış olsam da unboxing işlemlerinde bir performans kaybı yaşadığımı görmekteydim. Bunun yerine acaba bu Request sınıfına bir generic parametre eklesem ve benim Request sınıfı içerisine yerleştireceğim parametremde otomatik olarak benim parametreyi tanımlarken verdiğim tipte olsa ve ben bu gereksiz unboxing işleminden kurtulsam nasıl olur ? Kulağa kötü gelmiyor Smile  O zaman hemen deneyelim ve ilk olarak basit bir Request sınıfı yaratalım.

[DataContract]
public class Request<T>
{
[DataMember]
public T RequestObject { getset; }
}

Bir de örneğimiz içerisinde bize yardımcı olması amacıyla bir Person sınıfı yaratalım ve servis içerisine yazacağımız bir metodunda aldığı Person nesnesini loglayacağını düşünelim.

[DataContract]
public class Person
{
[DataMember]
public int Age { getset; }
[DataMember]
public string Name { getset; }
[DataMember]
public string Surname { getset; }
}

Son olarak Service Contract'ımızı yaratalım.

[ServiceContract]
public interface IGenericService
{
[OperationContract]
void LogPerson(Request<Person> person);
}

Şimdi ise Service Contract'ımızı implemente edecek olan servis metodunu yazalım.

public class GenericService : IGenericService
{    
#region IGenericService Members

public void LogPerson(Request<Person> person)
{
//Loglama İşlemleri
}            
#endregion
}

Evet servisimiz artık hazır durumda. Şimdi istemci uygulamamıza geçerek servis referansını ekleyelim ve sınıfımızı yaratarak ilgili metodumuzu çağıralım.

Uppps !! Gerçekleştirdiğimiz metodun aldığı parametreye bakarsanız biraz şaşkınlığa uğrayabilirsiniz. Belki de proxy tipi yaratırken bir hata ile karşılaştı da diyebilirsiniz. Undecided Gelin hep beraber durumu inceleyelim.

İstemci Tarafında Generic Parametre Alan Tiplerin Üretimi

Yukarıdaki örneğimizde aslında karşılaştığımız durum biraz özel bir durum. Eğer bir metodumuz içerisinde Generic bir tip alan bir parametre bulunuyorsa bunun için özel olarak bir DataContract üretilmekte ve bu metot içerisinde yaratılan özel tip kullanılmakta. Peki tip ismini üretirken kullanılan herhangi bir yöntem var mı derseniz, şu şekilde bir yaklaşımın kullanıldığını söyleyebiliriz.

TypeName+"Of"+ParameterType+Hash

TypeName : İlk olarak ilgili tipin ismi yazılmakta.

ParameterType : İçerisine Generic olarak aldığı tipin adı. Eğer birden fazla generic parametre alınırsa yine tüm tip isimleri yaratılan tip isminin içerisine konulmakta.

Hash : Başka metotlarda da aynı şekilde bir kullanım olduğu durumlarda tiplerin karışmaması amacıyla ve tiplerin unique olmasının sağlanması amacıyla konulan hash.

İşte yukarıda bahsettiğimiz durumlar nedeniyle istemci tarafında bu şekilde bir tip adı ile karşılaştık.

Bu noktada da aklımıza şu soru gelmekte : Yaratılan bu tip ismini kendimiz belirleyemezmiyiz yada müdahele edemezmiyiz ?

Bu soruya cevap olarak kullanabileceğimiz yöntem ise DataContract içerisinde bulunan Name parametresi ile tipin ismini belirleme yöntemi olacak.

Yine servis tarafına geçip Request tipini tanımladığımız kısma geri dönersek, DataContract tanımlaması yaparken kullandığımız Name parametresi ile tipimizin isminin belirlenmesine müdahele edebilmekteyiz. Burada istersek yukarıda bahsettiğim ParameterType ve Hash elemanlarını tip ismi içerisinde bulunup bulunmamasını da belirleyebilmekteyiz. Bunları yaparken de string formatlama yöntemini kullanıyor olacağız.

[DataContract(Name="Request_{0}_{#}")]
public class Request<T>
{
[DataMember]
public T RequestObject { getset; }
}

Burada Name içerisinde yaptığımız bildirimle aslında default olarak kullanılan yöntemi belirtmekteyiz. Yani {0} belirttiğimiz yere ParameterType gelmekte ve ondan sonra da {#} ifadesinin yerine de tekil bir Hash bilgisi üretilmekte.(Eğer tipimiz birden fazla generic parametre alırsa tüm generic parametrelere {0},{1}... şeklinde erişebiliriz.

[DataContract(Name="MyRequestClass")]
public class Request<T>
{
[DataMember]
public T RequestObject { getset; }
}

Yukarıdaki kullanımda ise sadece bizim belirttiğimiz isim ile tip üretilmekte. Şimdi isterseniz bu şekilde istemci tarafına geçerek servis referansını güncelleyelim ve metodumuzu çağıralım.

protected void Page_Load(object sender, EventArgs e)
{
GenericServiceClient client = new GenericServiceClient();
MyRequestClass myRequest = new MyRequestClass()
{
RequestObject = new GenericServiceReference.Person()
{
Name = "İlkay",
Surname = "İlknur",
Age = 20
}
};
client.LogPerson(myRequest);}

Böylece yukarıda gördüğümüz gibi adını kendimiz belirlediğimiz tipi metoda parametre olarak vererek işlemlerimizi gerçekleştirdik. Yazının en başına dönersek bana göre bu yöntem daha doğru gibi geldi. Özellikle sınıflarımız içerisinde bulunan elemanların sayısı artmasıyla ve daha kompleks tiplerin bulunmasıyla unboxing süresinin daha da artacağını düşünmekteyim. En kısa zamanda da bu konu ile ilgili performans araştırmalarını da yapıyor olacağım. Bunun yanında generic kullanmanın bana göre gelen tek dezavantajı ise proxy içerisindeki tip sayısının artması. Bunun haricinde eğer sizlerin de bu konuda herhangi bir tecrübeniz oldu ise yorum olarak yazarsanız yazıyı güncelleyip daha da faydalı bir hale getirebiliriz.

Bir sonraki yazımızda görüşmek üzere,

Hoşçakalın



Yorum Gönder