Istnieje wiele sytuacji podczas programowania które są żmudne i nudne. Wiele razy powtarzamy ten sam schemat działania: trzeba zalogować wyjątki, wywołania metody wraz z argumentami. Nie ma problemu możemy to zrobić wprost z wywołania w kodzie – najprostszy i najczęściej używany wzorzec : „Copy Paste”, czyli po prostu try catch w kodzie, czy Logger.Log i sprawa załatwiona. Robimy tak od zawsze 🙂

Chciałbym przestawić inne podejście, takie w którym możemy zastosować proxy, gdzie wywołanie danej metody będzie opakowane. Iterceptor przy użyciu biblioteki Ninject jest idealnym i prostym rozwiązaniem, a dzięki niemu nie będziemy mieć piękny i czysty kod!

Przyklady zastosowania interceptorów w naszym kodzie:

Wstrzyknąć do kontekstu użytkownika przechowywanego w kontenerze IoC, Ograniczenie zwiazane z uprawnieniami do obiektów – obcinanie na poziomie IQuerable danych zwracanych przez serwisy, Logowanie danych wejściowych, Przechwytywanie i logowanie wyjątków. Potrzebne biblioteki – nugety: Instancje interceptorów bazują na DynamicProxy przy pomocy implementacji LinFu lub Castle ja używam Casle więc potrzebujemy nugety Więc możemy zaczynać. Poniżej prosty przykład użycia interceptora dla bindingu. Dzięki takiemu użyciu każde wywołanie metody klasy będzie opakowane naszym interceptorem.

Ninject.Extensions.Interception
Ninject.Extensions.Interception.DynamicProxy
Castle.Core

Więc możemy zaczynać. Poniżej prosty przykład użycia interceptora dla bindingu. Dzięki takiemu użyciu każde wywołanie metody klasy będzie opakowane naszym interceptorem.

kernel.Bind<ITaskService>().To<TaskService>().Intercept().With<ServiceInterceptor>();

Przykładowy kod inerceptora do logowania:

public class LoggerInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var logger = invocation.Request.Context.Kernel.Get<Logger>();

            logger.Trace(string.Format("Start method {0} with {1}", invocation.Request.Method.Name, ArgumentsToString(invocation.Request.Arguments)));
            invocation.Proceed();
            logger.Trace(string.Format("End method {0} with {1}", invocation.Request.Method.Name, invocation.ReturnValue.ToString()));
        }

        private string ArgumentsToString(object[] arguments)
        {
            var builder = new StringBuilder();
            for (int i=0; i<arguments.Length; i++)
            {
                builder.Append(string.Format("Arg[{1}]={0},", arguments[i].ToString(), i));
            }
            return builder.ToString();
        }
    }

zawiera on dostęp do nazwy metody, argumentów oraz wyciągnięcie z kontenera IoC instancji loggera

Takie najprostsze użycie możemy rozwinąć o interceptowanie metod, czy klas udekorowanych przez atrybut lub wywołać wiele interceptorów na danej metodzie. Przykłady poniżej pokazują jak użyć wielu interceptorów wraz z oznaczeniem kolejności wywołania:

W atrybutach

[InterceptLogger(1)]
public class TaskFakeService : ITaskService
{

        public int GetById(int id)
        {
            return 1;
        }
        [InterceptExceptions(2)]
        public IList<int> GetAll()
        {
            throw new AggregateException();
        }
    }

w bindingu

Użycie interceptorów jako atrybuty:

public class InterceptLoggerAttribute : InterceptAttribute
{

        public InterceptLoggerAttribute(int order)
        {
            this.Order = order;
        }

        public override IInterceptor CreateInterceptor(IProxyRequest request)
        {
            return request.Kernel.Get<LoggerInterceptor>();
        }
    }