Aylık Arşiv: Eylül 2016

C# Gerçek Hayat Örneği : Reflection ve Expression Tree

Merhaba.

Bu yazıda C#’ın ileri diyebileceğimiz bir kaç özelliğini kullandığım bir örnekten bahsedeceğim. Bir konuyu anlatmaktan ziyade ihtiyaç duyduğumuz senaryoyu nasıl gerçekleştirdiğimi anlatacağım. İnternette ciddi bir araştırma zamanı geçirdiğim için paylaşma ihtiyacı hissettim. Ama nasip olursa bu konular, özellikle de Reflection hakkında bir kaç giriş makalesi yazma planım var.

Asıl konumuza gelecek olursak.. İhtiyacımız olan karışık bir senaryoyu gerçekleştirmek için yoğun bir mücadele sonucu aşağıdaki metodları yazdım.

Öncelikle sistemimizde bir proxy yapısı var. Proxy’ye interface i vererek servis metodlarının remote çalıştırılmasını sağlıyoruz.

Örnek verecek olursak.. Amacım kod içerisinde kullanımı aşağıdaki gibi olan yapıyı çalışma zamanında oluşturup çalıştırmak.

using (var service = new Proxy<IUrunler>())

{

   var response = service.Execute<Response<List<Urunler>>>(srv => srv.GetUrunler());

   object list = response.Value;

   //Diğer kodlar

}

Burada proxy sınıfıma ait “Execute” metodu parametre olarak gönderilen “Action”ı çalıştırıp “Response<T>” tipinde dönüş yapan bir metod.

Bahsettiğim gibi bu scripti dinamik olarak oluşturmam lazım. Lakin zorluk şu ki; buradakilerden “IUrunler” sınıfını da, “Response<List<Urunler>>” tipini de, “srv => srv.GetUrunler()” ifadesini de bilmiyorum ve çalışma zamanında alabiliyorum.

Burada ilk olarak “srv => srv.GetUrunler()” ifadesini oluşturmam gerekiyor. Bunun için yazdığım metod aşağıda.

 private Expression CreateExpression(Type interfaceType, MethodInfo method)

{

   ParameterExpression parameter = Expression.Parameter(interfaceType, "srv");

   Expression callExpression = Expression.Call(parameter, method.Name, null, null);

   Expression lambdaExpression = Expression.Lambda(callExpression, new ParameterExpression[] { parameter });

   return lambdaExpression;

}

Metodun parametrelerini yukarıdaki “IUrunler” örneği üzerinden açıklayacak olursam:

interfaceType: “IUrunler” vb. sözkonusu interface in tipi.

method: “GetUrunler()” metoduna ait Reflection ile elde edilmiş “MethodInfo” nesnesi.

İlk önce lambda ifademizde parametre olarak “srv” kullanacağımı belirtiyorum. Daha sonra “call” edilecek metodun ismini(GetUrunler) vererek callExpression ifadesini elde ediyorum.  Burada Expression sınıfına ait tam 14 adet “Call” metodundan parametre olarak methodName alan Call metodunu kullandım (public static MethodCallExpression Call(Type type, string methodName, Type[] typeArguments, params Expression[] arguments);).

“Expression”ımda kullanacağım elemanlarımı elde ettikten sonra bu alemanlarla Lambda Expression’ı oluşturmaya geldi sıra. Bunun için de yine Expression sınıfına ait 18 adet Lambda metodundan parametre olarak yalnız “Expression Body” ve “ParameterExpression” dizisi alan metodu kullanıyorum(public static LambdaExpression Lambda(Expression body, params ParameterExpression[] parameters);)

Şu ana kadar en üstte verdiğim örnekteki Lambda expression(“srv => srv.GetUrunler()”) ifadesini oluturmuş olduk. Scriptin kalan kısmını elde etmek için bu lambda expression ı da parametre olarak alan yeni bir metod yazıyorum:

private object GetResponseValue(Type interfaceType, Type methodReturnType, Expression expression)

{

   var proxyType = typeof(Proxy<>);

   var service = proxyType.MakeGenericType(interfaceType);

   string methodSignature = "TResponse Execute[TResponse](System.Linq.Expressions.Expression`1[System.Func`2[" + interfaceType.FullName + ",TResponse]])";

   MethodInfo executeMethodInfo = service.GetMethods().Single(mi => mi.ToString() == methodSignature);

   MethodInfo executeGeneric = executeMethodInfo.MakeGenericMethod(methodReturnType);

   var instanceClass = Activator.CreateInstance(service);

   var response = executeGeneric.Invoke(instanceClass, new object[] { expression });

   object list = response.GetType().GetProperty("Value").GetValue(response, null);

   return list;

}

 

Bu metodda ekstra parametre olarak “methodReturnType” var ve bu “GetUrunler” metodunun dönüş tipini yani “Response<List<Urunler>>” değerini ifade ediyor.

Oluşturmamız gereken proxy nesnesi ve çağıracağımız “Execute” metodu generic yapılar olduğu için bunları reflection ile oluştumamız gerekiyor.

İlk etapta “service” instanceını oluşturabilmek için “Proxy<>” nesnesinin tipini alarak bundan yine reflection ile generic “service” tipini elde ediyorum.

Execute metodunun farklı overload ları olduğu için direkt kullanacağım metodun imzasını belirterek MethodInfo’sunu elde ediyorum. Kullanacağım metod da generic olduğu için metodun dönüş tipini belirterek ve “MakeGenericMethod” kullanarak “Execute” metodunun generic versiyonunu oluşturuyorum(MethodInfo executeGeneric = doActionMethodInfo.MakeGenericMethod(methodReturnType)).

Sonrasında “Activator.CreateInstance“ yardımıyla “service” instance ımı oluşturuyorum.

..ve oluşturduğum instance yardımıyla, expression parametresini de göndererek metodu invoke ediyorum, sonucu response değişkenine atıyorum.

Son olarak da “respons”un “Value” propertysini list değişkenime atıyorum.

Umarım açıklayıcı ve faydalı bir anlatım olmuştur.

Kendinize çook iyi davranın…

 

 Bu sayfa 664 kez görüntülendi

It's only fair to share...Share on LinkedInShare on FacebookShare on Google+Tweet about this on TwitterShare on Tumblr