Merhaba arkadaşlar, bu makalemde Aspect Oriented Programming (AOP) nedir ve birbirleri ile kesişen ilgiler (Cross-Cutting) nelerdir gibi kavramları açıklayarak AOP serisine bir giriş yapmak istiyorum.
Uzun zamandır giriş yapmayı düşündüğüm bir konuydu aslında AOP fakat fırsat bulup bir türlü başlayamamıştım. Umarım keyifli bir makale serisi çıkar. 🙂
Aspect Oriented Programming yaklaşımı, temelinde birbirleri ile kesişen ilgilerin (Cross-Cutting Concerns) ayrılması üzerinedir. Peki nedir bu birbirleri ile kesişen ilgiler diyecek olursak:
Geliştirdiğimiz modüllere baktığımızda aslında her business işleminde ortak olarak gerçekleştirdiğimiz bazı işlemler vardır. Örneğin kullanıcının yetkisi var mı?, Gelen isteği logla, Exception Handling işlemini yap gibi ve sonrasında ilgili modülün business kısmını gerçekleştirmekteyiz. İşte bu işlemler bizim birbirleri ile kesişen ilgilerimiz (Cross-Cutting-Concerns) oluyor.
Bu modüle birde kodsal açıdan örnek vermek gerekirse:
public Product GetProduct(int productId) { try { // Kullanıcı yetkilerini kontrol et. // Yapılan işlemi logla. var product = new Product(); // Product'ı cache'e ilgili productId ile ekle. return product; } catch (Exception ex) { // Exception handling işlemleri. throw; } }
Örnek metot üzerinde de gördüğümüz üzere genelde business işlemi sırasında yaptığımız temel kontrol ve işlemler. AOP ise bize birbirleri ile kesişen bu ilgilerin birbirlerinden farklı olsada ayrılmaları gerektiğini söylemektedir.
Aspect Oriented Programming’in odak noktası ise konuların ayrımıdır (Separation of Concerns). Object Oriented Programming’de de olduğu üzere oluşturmuş olduğumuz sınıfları, her sınıfın kendi sorumluluğunu yerine getirmesi (Single Responsibility) gibi prensiplere uyarak geliştirdiğimizde oluşan kodun okunabilirliğini (Readability), tekrar kullanabilirliğini (Reusability) ve genişletilebilirliğini (Extensibility) sağlamaktayız. AOP ise bize bu anlamda kodun okunabilirliğini ve tekrar kullanılabilirliğini en üst seviyede sağlamayı amaçlamaktadır konuların ayrımı (Separation of Concerns) altında.
Object Oriented Programming’de soyutlamaya (Abstraction) giderekte kodun tekrar kullanılabilirliğini, okunabilirliğini ve genişletilebilirliğini ve sınıfların birbirine sıkı sıkıya bağlı (Tightly Coupled) olmamasını sağlayabiliriz ama bir noktaya kadar. O nokta geldiğinde ise AOP kesişen ilgilerimizi ele alarak onları tamamen bağımsız (Independent) hale getirmektedir. Bu sayede her bir kesişen ilgimiz, kendi içerisinde bağımsız olarak geliştirilebilir ve tekrar kullanılabilir hale gelmektedir.
Yukarıdaki örnek üzerine AOP uygulayarak yeni bir örnek vermek gerekirse:
[ExceptionHandling] [CheckUserAuthorization] [LogProcess] [Cache(TimeOut = 10000)] public Product GetProduct(int productId) { var product = new Product(); return product; }
Kesişen ilgilerimizi (Cross-Cutting-Concerns) AOP ile ayırdıktan sonraki hali gerçekten şaka gibi değil mi? 🙂 Okunabilirlik ve tekrar kullanılabilirliği nasılda değiştiğini netçe görebiliyoruz. Açıkçası bu şekilde yapılar geliştirmek, insana kod yazarken de ayrı bir zevk veriyor doğrusu.
Evet, işin terminoloji kısımlarında her şey güzel. Fakat biz bu kesişen ilgilerimizi nasıl ayıracağız sorununa gelirsek, bu konuda yardımımıza Interceptor yapıları çıkmakta. Interceptor’ler bize metot çağrıldığında, öncesinde ve sonrasında araya girerek belirli işlemleri yapabilmemizi sağlayan yapılardır.
Interceptor yapılarını oluşturabilmek için Reflection sınıfından bolca yararlanacağız. 🙂 İşin aslı, bu yapıları kolaylıkla kullanabilmek için bir çok 3rd party uygulama bulunmakta. Örneğin: Castle Windsor, Enterprise Library gibi kütüphaneler. Ben en azından giriş makalemde işin mutfağında neler olup bitiyoru sizlere göstermeden hazır kütüphaneler aracılığı ile bu yapıları kullanmak istemiyorum. Bu yüzden işin eğlenceli kısmı olan kendi interceptor’ümüzü oluşturacağız. Bu mutfağın bir kokusunu alalım ve neler olup bitiyor bir bilelim en azından. 🙂
Şimdi işin en eğlenceli kısmına geçme vakti. Örneğimizde bizim kesişen ilgilerimiz aşağıdaki modüller olsun:
Bu modüllerimiz’i birbirlerinden bağımsız concern’lara ayırarak aspect’ler aracılığı ile metotların çağırımında, nasıl araya girerek işlemlerimizi gerçekleştirebileceğimizi görelim.
AOPGiris isimli bir konsol uygulaması oluşturuyorum. Konsol uygulamasının içerisine Aspect isimli bir klasör oluşturuyorum ve Aspect’lerimizi burada geliştireceğiz. IAspect isimli bir interface ekliyorum ve bu interface’i implemente ederek aspect modlarımızı oluşturacağız.
namespace AOPGiris.Aspect { public interface IAspect { } }
Şimdi ise IAspect interface’ini implemente ederek, aspect modlarımızı oluşturacağız. Bu modlar ise bize metot çağrımında öncesinde veya sonrasında araya girebilmemizi sağlayacaklar.
namespace AOPGiris.Aspect { public interface IAfterAspect : IAspect { object OnAfter(object value); } }
IAfterAspect metot işlendikten sonra çalışacak olan aspect modudur ve geriye herhangi bir işlem değeri dönmektedir.
namespace AOPGiris.Aspect { public interface IAfterVoidAspect : IAspect { void OnAfter(object value); } }
IAfterVoidAspect ise imzasından da anlaşılacağı üzere metot işlendikten sonra çalışıp geriye değer dönmemektedir.
namespace AOPGiris.Aspect { interface IBeforeAspect : IAspect { object OnBefore(); } }
IBeforeAspect ise metot işlenmeden önce çalışacak olan aspect modumuzdur.
namespace AOPGiris.Aspect { public interface IBeforeVoidAspect : IAspect { void OnBefore(); } }
IBeforeVoidAspect ise metot işlenmeden önce çalışıp geriye değer dönmeyecek olan aspect modumuzdur. Şimdi sıra geldi kesişen ilgilerimizin aspectlerini oluşturmak için türeceğimiz abstract sınıfımızı oluşturmaya.
using System; namespace AOPGiris.Aspect { public abstract class AspectBase : Attribute, IAspect { } }
AspectBase sınıfını, Attribute soyut sınıfından türetiyoruz ve oluşturmuş olduğumuz IAspect arayüzünüde implemente ediyoruz. Alt yapımız hazır olduğuna göre şimdi modüllerimiz’i oluşturmaya başlayabiliriz.
İlgili aspect’leri oluşturmadan önce modül işlemleri sırasında bize unique key’ler oluşturabilme gibi işlemler için ilgili metot’un ismini, çağırım sırasında gönderilen parametreleri verecek olan yardımcı sınıf amacı ile AspectContext isimli bir sınıf oluşturuyorum.
using System; namespace AOPGiris.Aspect { public class AspectContext { private readonly static Lazy<AspectContext> _Instance = new Lazy<AspectContext>(() => new AspectContext()); private AspectContext() { } public static AspectContext Instance { get { return _Instance.Value; } } public string MethodName { get; set; } public object[] Arguments { get; set; } } }
Lazy sınıfı aracılığı ile pratik olarak Singleton desenini uyguluyorum ve bize modül işlemleri sırasında gerekli olacak property’leri tanımlıyorum. Aspect klasörünün içerisine Attributes isimli yeni bir klasör oluşturuyorum ve ilk modülümüz olan caching işlemleri için kullanacak olduğumuz CacheAttribute sınıfını kodlamaya başlıyorum.
using System; namespace AOPGiris.Aspect.Attributes { public class CacheAttribute : AspectBase, IBeforeAspect, IAfterVoidAspect { public int DurationInMinute { get; set; } public object OnBefore() { string cacheKey = string.Format("{0}_{1}", AspectContext.Instance.MethodName, string.Join("_", AspectContext.Instance.Arguments)); if(true) { // gerekli cache key ile kontrol ederek varsa cache'de çağırım öncesi metot'u execute // etmeden cache üzerinden ilgili veriyi geri dön. Console.WriteLine("{0} isimli cache key ile cache üzerinden geliyorum!", cacheKey); return true; } } public void OnAfter(object value) { string cacheKey = string.Format("{0}_{1}", AspectContext.Instance.MethodName, string.Join("_", AspectContext.Instance.Arguments)); // cache key ile ilgili veriyi DurationInMinute kullanarak Cache'e ekle veya güncelle. } } }
IBeforeAspect ve IAfterVoidAspect kullanarak oluşturmuş olduğum CacheAttribute’ü çağırım öncesi metot execute edilmeden araya girerek cache kontrolü yapar ve varsa cache üzerinden getirerek geriye değeri döner. OnAfter’da ise ilgili işlem execute edildikten sonra veri cache’e eklenir veya güncellenir. Cache işlemimiz bu kadar 🙂 Şimdi sıra Loging işlemine. Attributes klasörü içerisinde şimdide LogAttribute sınıfı oluşturuyorum ve kodlamaya devam ediyoruz.
using System; using System.Text; namespace AOPGiris.Aspect.Attributes { public class LogAttribute : AspectBase, IBeforeVoidAspect, IAfterVoidAspect { public void OnBefore() { StringBuilder sbLogMessage = new StringBuilder(); sbLogMessage.AppendLine(string.Format("Method Name: {0}", AspectContext.Instance.MethodName)); sbLogMessage.AppendLine(string.Format("Arguments: {0}", string.Join(",", AspectContext.Instance.Arguments))); // log işlemini gerçekleştir. Console.WriteLine("Loging: {0}", sbLogMessage.ToString()); } public void OnAfter(object value) { // sonrasında log tutmak istenirse. } } }
IBeforeVoidAspect ve IAfterVoidAspect kullanarak oluşturmuş olduğum LogAttribute sınıfıda öncesinde loglama işlemini gerçekleştiriyoruz ve geriye herhangi bir değer dönmemize gerek olmadığı için IBeforeVoidAspect türünü implemente ettik. OnAfter’da ise ilgili veriyi işlem sonrası loglama yapabiliriz.
Aspectler bu kadar. Şimdi sıra geldi asıl sıkıntılı kısıma 🙂 Araya girmemizi sağlayacak olan proxy sınıfını tasarlayacağız. Transparent Proxy sınıfı bizim gerçek sınıfımızı temsil ediyor olacak ve ilgili metot’u invoke etmeden önce veya sonrasında istediğimiz işlemleri yapabiliyor olacağız. Bu işlemi yapan hazır kütüphaneler olduğundan bahsetmiştik, EL içindeki Unity veya Castle Windsor gibi.
Bu kütüphanelerde gerçek nesnelerimizin çalışma zamanında birer Transparent Proxy’lerini oluşturarak araya girme işlemlerini gerçekleştirmektedirler. Bu işlemide kendimiz handle edeceğimiz için başlayalım generic bir TransparentProxy sınıfını kodlamaya.
using AOPGiris.Aspect; using System; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Proxies; namespace AOPGiris { public class TransparentProxy<T, TI> : RealProxy where T : TI, new() // İlgili tipin örneğini alabilmek için new() constraint'ini ekliyoruz. { private TransparentProxy() : base(typeof(TI)) // RealProxy sınıfına, gerçek nesnemizi temsil edecek olan tipi veriyoruz, ilgili interface'imiz. { } // İlgili tipte RealProxy aracılığı ile proxy'mizi oluşturuyoruz. public static TI GenerateProxy() { var instance = new TransparentProxy<T,TI>(); return (TI)instance.GetTransparentProxy(); } // İlgili metot çağırıldığında çalışacak olan metotdur. public override IMessage Invoke(IMessage msg) { var methodCallMessage = msg as IMethodCallMessage; ReturnMessage returnMessage = null; try { // tipimiz üzerinden metot infoya erişerek ilgili attribute olarak eklenmiş // aspect'lerimizi buluyoruz. var realType = typeof(T); var mInfo = realType.GetMethod(methodCallMessage.MethodName); var aspects = mInfo.GetCustomAttributes(typeof(IAspect), true); // Hatırlarsanız aspect'lerimiz içerisinde kullanmıştık. Gelen parametreleri dolduruyoruz context'e. FillAspectContext(methodCallMessage); object response = null; // Before aspectlerimizi çalıştırıyoruz önce ve geriye değer dönen varsa respons'a eşitliyoruz. CheckBeforeAspect(response, aspects); object result = null; // Response boş değilse, buradaki veri cache üzerinden de geliyor olabilir ve tekrardan invoke etmeye // gerek yok, direkt olarak geriye response dönebiliriz bu durumda. if (response != null) { returnMessage = new ReturnMessage(response, null, 0, methodCallMessage.LogicalCallContext, methodCallMessage); } else { // Response boş ise ilgili metot'u artık invoke ederek çalıştırıyor ve sonucunu alıyoruz. result = methodCallMessage.MethodBase.Invoke(new T(), methodCallMessage.InArgs); returnMessage = new ReturnMessage(result, null, 0, methodCallMessage.LogicalCallContext, methodCallMessage); } CheckAfterAspect(result, aspects); // After aspectlerimizi'de çalıştırdıktan sonra artık geriye çıktıyı dönebiliriz. return returnMessage; } catch (Exception ex) { return new ReturnMessage(ex, methodCallMessage); } } private void FillAspectContext(IMethodCallMessage methodCallMessage) { AspectContext.Instance.MethodName = methodCallMessage.MethodName; AspectContext.Instance.Arguments = methodCallMessage.InArgs; } private void CheckBeforeAspect(object response, object[] aspects) { foreach (IAspect loopAttribute in aspects) { if (loopAttribute is IBeforeVoidAspect) { ((IBeforeVoidAspect)loopAttribute).OnBefore(); } else if (loopAttribute is IBeforeAspect) { response = ((IBeforeAspect)loopAttribute).OnBefore(); } } } private void CheckAfterAspect(object result, object[] aspects) { foreach (IAspect loopAttribute in aspects) { if (loopAttribute is IAfterVoidAspect) { ((IAfterVoidAspect)loopAttribute).OnAfter(result); } } } } }
TransparentProxy sınıfımız’da hazır durumda. İlgili bilgileri metot yorum satırlarında bulabilirsiniz. Artık ilgili sınıflarımız için RealProxy aracılığı ile temsilcilik yapacak olan Generic Transparent Proxy sınıfımız hazır ve kod içerisinde de görebileceğiniz gibi aspect’lerimizide implemente etmiş durumdayız.
Şimdi kodlamış olduğumuz aspect’leri test edebilmemiz için bir ProductService isminde sınıf oluşturuyorum ve içerisine GetProduct metodunu oluşturarak aspect’lerimizi işaretliyorum.
using System.Collections.Generic; using AOPGiris.Aspect.Attributes; namespace AOPGiris { public class Product { public int Id { get; set; } public string Name { get; set; } public double Price { get; set; } } public class ProductService { // In memory olarak bir kaç product ekliyorum. private static Dictionary<int, Product> _InMemDb = new Dictionary<int, Product>(); public ProductService() { _InMemDb.Add(1, new Product() { Id = 1, Name = "MacBook Air", Price = 3000 }); _InMemDb.Add(2, new Product() { Id = 2, Name = "Sony Xperia Z Ultra", Price = 1400 }); } [Cache(DurationInMinute = 10)] [Log] public Product GetProduct(int productId) { return _InMemDb[productId]; } } }
ProductService sınıfı için yeni bir örneği alındığında in memory olarak bir dictionary üzerinde Product nesnelerini tutuyorum. GetProduct metodunda ise gördüğümüz gibi sadece dictionary üzerinden nesneyi geri return ediyorum. Caching ve Loging işlemlerini bizim için aspect’lerimiz halledecek. Ne kadarda hoş duruyor değil mi? 🙂
Şimdi konsol uygulamamız üzerinde TransparentProxy sınıfımız aracılığı ile çağırarak bir bakalım aspect’lerimiz nasıl çalışacaklar?
using System; namespace AOPGiris { class Program { static void Main(string[] args) { // Servis örneğini oluşturuyoruz. var productService = TransparentProxy<ProductService, IProductService>.GenerateProxy(); // Servis üzerinden GetProduct metotunu çağırıyoruz. var product = productService.GetProduct(1); Console.WriteLine("Id: {0}, Name: {1}, Price: {2}", product.Id, product.Name, product.Price); Console.ReadLine(); } } }
ve sonuç…
Gördüğümüz gibi GetProduct metot’u için eklemiş olduğumuz Cache ve Log aspect’leri çalışmış bulunmaktadır. Özünde yapmış olduğumuz işlemde GetProduct için business kodlarının karmaşıklaşmasını kesişen ilgilerimizi ayırarak sağlamış bulunmaktayız. Kod satırları artık okunabilirliği yüksek bir seviyede ve kesişen ilgilerimiz tekrar kullanılabilir haldedir.
Aktaracaklarım şimdilik bu kadar. Bu başlık altında metotlar invoke olurken mutfakta neler olup bitiyor, Interceptor’ler bu işlemi nasıl işliyor gibi sorularıda görmüş olduk aslında. AOP serisinin ilerleyen makalelerinde ise 3rd party kütüphaneler aracılığı bu işlemleri nasıl gerçekleştirebiliriz konularınada değineceğim. Çünkü her zaman kendimiz custom olarak yazabilme ve işleyebilme olanaklarına sahip olamayabiliriz. Bunun en büyük sebeplerinden birisi ise: standardizasyon. Eğer büyük bir ekipte çalışıyorsanız bazı konuların önemli olduğu gibi en büyük unsurlardan biriside standardizasyondur.
Bunun için gelecek makalelerimde bu kütüphaneler nasıl kullanılıyor konularınada değiniyor olacağım. Örnek projeyi ekte bulabilirsiniz.
{:tr} Makalenin ilk bölümünde, Software Supply Chain güvenliğinin öneminden ve containerized uygulamaların güvenlik risklerini azaltabilmek…
{:tr}Bildiğimiz gibi modern yazılım geliştirme ortamında containerization'ın benimsenmesi, uygulamaların oluşturulma ve dağıtılma şekillerini oldukça değiştirdi.…
{:tr}Bildiğimiz gibi bir ürün geliştirirken olabildiğince farklı cloud çözümlerinden faydalanmak, harcanacak zaman ve karmaşıklığın yanı…
{:tr}Bazen bazı senaryolar vardır karmaşıklığını veya eksi yanlarını bildiğimiz halde implemente etmekten kaçamadığımız veya implemente…
{:tr}Bildiğimiz gibi microservice architecture'ına adapte olmanın bir çok artı noktası olduğu gibi, maalesef getirdiği bazı…
{:tr}Bir önceki makale serisinde Dapr projesinden ve faydalarından bahsedip, local ortamda self-hosted mode olarak .NET…
View Comments
Merhaba Gökhan,
Bu güzel makale için teşekkürler.
"public class ProductService" sınıfı IProductService sınıfından implement edilmediği için yeni başlayan arkadaşlar sorunu tespit edemeyebilir.
Ekte verdiğin kaynak kodlarda sorun yok. Orada implement..
Başarılar.
Teşekkürler. :)
Makale için teşekkürler .AOP konusunda çok detaylı makaleler bulamazken ilaç gibi geldi. 3rd party uygulamalara postsharp kullanımı konusunda uygulamalı anlatım olarak makalenin devamı gelir mi acaba ? :)
Merhaba teşekkür ederim öncelikle. Evet bir sonraki aop serisinde 3rd party uygulamalar ile nasıl intercept yapılabileceğini hakkında yazılar yazmayı düşünüyorum ama bu aralar biraz zaman sıkıntısı çekiyorum. :)
o zaman bol bol vakit diliyorum size. beklemedeyiz :)
Aop hakkında okuduğum en anlaşılır yazıydı. Teşekkürler
Teşekkür ederim.
Benim kullanacağım sınıf bir abstract sınıftan türediği için sizin verdiğiniz örnekteki Proxy sınıfında bir takım değişiklikler yapmam gerekti fakat çok hakim olmadığım için doğru mu yaptım ya da daha pratik bir değişiklik ile çözülür müydü emin değilim.
Değişiklikten sonra kod çalıştı ama production'a çıktığımızda gelecek çoklu isteklerde de doğru çalışır mı emin olamıyorum gerçi static bir metot kalmadı ama yine de göz atabilirseniz çok sevinirim.
public class TransparentProxy : RealProxy where T : BaseBusiness, TI // BaseBusiness abstract classını ekledim ve new constraintini kaldırdım.
{
private T _creator;
private MethodRequest _methodRequest; //Abstract classın zorunlu tuttuğu property bu. Bunu constructorda almak gerekli.
public TransparentProxy(MethodRequest methodRequest) : base(typeof(TI))
{
_methodRequest = methodRequest;
_creator = (T)Activator.CreateInstance(typeof(T), methodRequest);
}
public TI GenerateProxy() //Bu alan static tanımlanmıştı fakat constructorda alan aldığım için bunu değiştirdim.
{
var instance = new TransparentProxy(_methodRequest);
return (TI)instance.GetTransparentProxy();
}
......
//Bundan sonra geri kalan tüm kod aynı sadece sizin new ile çağırdığınız aşağıdaki satırda ben new yerine _creator kullandım
result = methodCallMessage.MethodBase.Invoke(_creator, methodCallMessage.InArgs);
}
Proxy'i çağırdığım kod;
public UserController()
{
_userBusiness = new TransparentProxy(methodRequest).GenerateProxy();
}
Merhaba şuan bir problem yaratacak durum görünmüyor fakat, performans testlerine bir bakmak gerek static constructor kullanılmadığı için herhangi bir etkisi olacak mı. Şuan herhangi bir test etmeden yorum yapamayacağım. Eğer performans concern'ünüz var ise de, runtime ao yerine compiletime ao tercih etmenizi önerebilirim. Bu noktada ise lightweight olarak Fody veya PostSharp'ı tavsiye edebilirim.
Merhaba makale çok güzel. Bu kodu transactionscope için nasıl kullanabiliriz. Postsharp dışarıdan parametre olarak transactionscope alarak işlemi devam ettiriyor. kodunuzda nasıl bir değişiklik yapmamız gerekiyor. Teşekkürler
Merhaba, tam yapmak istediğiniz nedir acaba? TransactionScope yerine, bir attribute kullanarak mı decorate etmek istiyorsunuz? Buradan anladığım bu sanırım. Eğer öyle ise, TransparentProxy içerisindeki Invoke method'una implemente edebilirsiniz veya constructor'ından inject edebilirsiniz. Teşekkürler.
Merhaba,
Güzel anlatım için teşekkürler. Bir şey sormak istiyorum. Benim product service constructor umda parametre olması durumunda nasıl bir yöntem izleyebilirim.
Teşekkürler
Merhaba, herhangi bir DI container ile, ITransparentProxy'leri de inject ettirebilirsiniz veya her ne kadar bir antipattern olsada service locator pattern'i ile.
Teşekkürler
Hocam merhaba, burada tek anlamadığım AspectContext neden singleton. Her aspect call edildiğinde bu context oluşturulup sonra silinmesi gerekmez mi? Aspect ve context transient bir mantık da çalışması gerekir diye düşünüyorum. Singleton olduğunda eş zamanlı işlemlerde context nesnesi nasıl davranacak?
Merhaba yorumunuz için çok teşekkür ederim. Evet haklısınız, normalde bir service locater ile ilgili objecnin yaşam döngüsünü kontrol etmek gerekir multi-threaded ortamlar için. Farklı konulara atlamamak için sadece AOP yaklaşımını göstermek amacıyla bu şekilde yapmıştım. :) Uzun zaman olmuş.
Merhaba,
Öncelikle makale için teşekkürler.
Aradan baya zaman geçmiş ama belki görürsünüz :)
Benim de dikkatimi çeken durum bu oldu. Singleton tanımlama.
Buradaki MethodName değerini Singleton olmadan nasıl kullanabiliriz?
Merhaba, teşekkür ederim yorumunuz için. Evet oldukça eski bir makale.
Eğer doğru anladıysam "AspectContext" e singleton olmadan erişmek istiyorsanız biraz kanlı bir yol diyebilirim. Kullanmış olduğunuz ortama göre (WebAPI/Console etc.) bir DI tool'u kullanabilir ve gerekli custom injection işlemlerini (service locating) gerçekleştirebilirsiniz. Böylelikle method detaylarını concreate aspect'leriniz arasında gezdirebilirsiniz.
Merhaba,
Çalışmanız benim için çok faydalı oldu. Exception handle etmek için kendi projemi yazmıştım. Bunu kendi projemezde nasıl kullanmalıyız static mi olmalı felandiye düşünürken AOP çok faydalı oldu benim için. Fakat hala karar veremedğim bir nokta var. Exception katmanımı, exception throw ederken criticalLevel yada usurLevel vs gibi throw etmeyi düşünerek yazdım. Şimdi Aspectlerle tek bir [ExceptionHandling] attribute eterli olmuyor. Çünkü bu hep Dot.Net exception'a düşecektir. Dolayısıyla benim her level için bir Exception Aspect mi yazmam gerekiyor diye kararsız kaldım.
Çalışmanız için tekrar tşk ederim.
Merhaba, güzel bir düşünce. Kusura bakmayın geç cevap için, bir kaç aydır oldukça yoğun bir dönemdeyim taşındığım için. Şuan ezbere cevap vermem ne derece doğru olur ama fikrim; Kendi exception nesnenizi oluşturabilir, içerisinde bir property'de tutabilirsiniz level'ını. Daha sonra ise ilgili aspect'in içinde handle edip, istediğiniz işlemi gerçekleştirebilirsiniz diye düşünüyorum.
Merhaba,
"Interceptor’ler bize metot çağrıldığında, öncesinde ve sonrasında araya girerek belirli işlemleri yapabilmemizi sağlayan yapılardır."
Ben metot çağrıldığında ActionFilterAttribute'ları kullanarak'da metot'un çalışma öncesine veya sonrasına müdahale edeibiliyorum. İkiside aynı işi yapıyor gibi ? Bunların farkı nedir ? Niye interceptor kullanmalıyım ? Niye Attribute kulanmamalıyım ?
Teşekkürler
Merhaba kullandığınız framework eğer destekliyorsa bir yerlerde araya girebilmenizi, tabiki de kullanmalısınız, ActionFilter attribute gibi. Herhangi bir fark yok. Sadece nerede, nasıl araya girmeniz gerektiği ile ilgili konu veya kullandığınız framework ile.