Daha önceki IoC ve AOP yazılarında çeşitli konulara değinerek örnek projeler üzerinde anlatımlarda bulunduk. Bu yazımızda da Castle Windsor'dan yararlanarak projelerimizde sıklıkla kullanacağımız bir ExceptionAspect veya Interceptor oluşturacağız.
Interceptor'lar veya Dynamic Proxies Aspect Oriented Programming'in bir implementasyonudur. Bu bize oluşturduğu proxy ile metodu intercept ederek kendi kodlarımızın arasına inject etmemizi sağlar.
IL kodlarına baktığımızda aşağıdaki resimde olduğu gibi geliştirmiş olduğumuz kodları try catch finally blokları arasına alınır.
Daha önceki Castle Windsor Kullanarak Cache Interceptor Oluşturma yazısındaki örneğimiz üzerinden ilerleyelim. O yazıda il kodu alarak geriye ilçeleri dönen bir case üzerinden caching işlemi yapan aspect geliştirmiştik. Bu yazımızda da aynı örnek üzerinden ilerleyerek projemiz için bir ExceptionHandling aspect oluşturalım.
İlk olarak ExceptionHandlingInterceptor adında aspect'imizi oluşturalım.
public class ExceptionHandlingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
try
{
invocation.Proceed();
}
catch (Exception ex)
{
LogManager.Log("Exception in : " + invocation.Method.Name + " method." + ex);
invocation.ReturnValue = new BaseResponse { IsSuccess = false };
}
}
}
Yukarıdaki kodu incelediğimizde özetle şunu söylüyor ;
- Invoke edilecek metodu try-catch-finally bloğu arasına al
- Invoke edilen metot içerisinde hata alırsan catch bloğuna gir ve önce alınan hatayı log'a yaz
- Sonrasında BaseResponse modelini initialize ederek client'a clear bir response model dön.
Tabi ki de çok daha farklı ayrıntıları log'a yazmanız gerekir ancak sample olduğundan şimdilik bunları yazalım.
İkinci adım olarak yazmış olduğumuz Exception aspect'ini container'a register etmek var.
Castle Windsor Kullanarak Cache Interceptor Oluşturma örneğinde ServiceInstaller class'ını olduğu gibi alıp ExceptionHandlingInterceptor'ını register işlemimizi yapalım.
public class ServiceInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<CacheInterceptor>().LifestyleSingleton());
container.Register(Component.For<ExceptionHandlingInterceptor>().LifestyleSingleton());
container.Register(Component.For(typeof(ILocationService))
.ImplementedBy(typeof(LocationService))
.Interceptors(typeof(CacheInterceptor)
.Interceptors(typeof(ExceptionHandlingInterceptor))));
}
}
Yukarıda ki register işlemi şunu söylüyor; "ILocationService interface'inin implementasyonu LocationService class'ı dır. Bu implementasyona ait 2 adet Interceptor var "CacheInterceptor" ve "ExceptionHandlingInterceptor" adında ve bu interceptor'lar LifeStyleSingleton olarak container'a register edilmişlerdir."
Uygulamamız hazır diyebiliriz. LocationService içerisinde bulunan GetDistrictsByCityCode adlı metodumuzda test etmek için aşağıdaki gibi kendimiz bir exception throw edelim.
public class LocationService : ILocationService
{
public GetDistrictsByCityCodeResponse GetDistrictsByCityCode(int cityCode)
{
throw new NullReferenceException();
}
}
Projemizi run edip ilgili endpoint'e istekte bulunduğumuzda exception fırlatılıp Interceptor içerisinde bulunan catch bloğuna düşecektir ve loglama işlemini yaptıktan sonra client'a BaseResponse modelini dönecektir.
Peki bunları yaparak ne sağladık ?
- Exception Handling işini eski usül projede yüzlerce try-catch bloğu oluşturmak yerine AOP'in faydalarından yararlanarak tek bir yerden yönetebilir duruma getirdik.
- Çok daha reusable olan ve başka yerlerde de kullanabileceğimiz bir yapı tasarladık.
- Uygulama exception fırlattığında client'a saçma sapan StackTrace mesajı yerine her response da aldığı BaseResponse modelini dönerek response'larımızı daha tutarlı bir hale getirdik.