Для тех кто не знает – AOP это Aspect-Oriented Programming, Аспектно-ориентированное программирование.
При написании любой программы программист производит функциональную декомпозицию, то есть разбивает большие блоки функциональности на более маленькие. Но всегда существуют так называемые cross-cutting concerns или сквозная функциональность, которая используется всеми остальными частями программы, которую невозможно выделить в отдельный модуль\класс\метод,
Чаще всего такой функциональностью является логгирование, разграничение доступа, управление транзакциями.
Концепция AOP заключается в том что сквозная функциональность выделяется в отдельные сущности , называемые аспектами, и декларативно задается использование аспектов в коде.
AOP для .NET может быть реализован двумя способами: изменение кода при компиляции инструментами типа PostSharp или макросами языка Nemerle, или перехват вызовов на стадии выполнения.
В составе Unity есть сборка Microsoft.Practices.Unity.Interception, которая содержит расширение контейнера Unity для перехвата вызовов объектов собираемых контейнером.
Чтобы перехватывать вызовы надо контейнеру сообщить что перехватывать, как перехватывать, и зачем перехватывать.
Что перехватывать задается политиками (Policy), как перехватывать определяют перехватчики (Interceptor), зачем перехватывать определяют обработчики вызовов (CallHandlers).
Эти три части механизма перехвата не зависят друг от друга.
Перехватчики – это классы, реализующие интерфейс IInterceptor. В библиотеке есть классы InterfaceInterceptor для перехвата методов интерфейса, VirtualMethodInterceptor – для перехвата виртуальных методов класса, TransparentProxyInterceptor – для перехвата с помощью прокси-классов, используемых для .NET Remoting.
Обработчики вызовов – это классы, которые реализуют интерфейс ICallHandler, в котором только один нужный метод – Invoke.
Политики бывают двух видов – управляемая атрибутами (AttributeDrivenPolicy) и управляемая правилами (RuleDrivenPolicy).
По-умолчанию используется AttributeDrivenPolicy, которая заключается в том что обработчики вызовов задаются атрибутами, унаследованными от HandlerAttribute, и перехватываются только те методы, для которых заданы эти атрибуты (или атрибуты заданы для классов).
RuleDrivenPolicy позволяет задавать какие методы будут перехватываться с помощью набора правил (IMatchingRule) и какие обработчики будут при этом вызываться.
Подробнее по этой ссылке http://msdn.microsoft.com/en-us/library/dd140045.aspx
Если вы сами пишете код, для которого нужен перехват, то атрибутов и стандартной политики вам будет достаточно. Если вы не можете менять код классов, но хотите их перехватывать, то это можно сделать с помощью задания политик, основанных на правилах заданных в коде или конфигурационном файле.
Пример
Сначала создадим обработчик, который просто выводит Hello, world на консоль
public sealed class HelloWorldAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new HelloWorldCallHandler();
}
}
public class HelloWorldCallHandler: ICallHandler
{
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("Hello, world");
return getNext()(input, getNext);
}
public int Order { get; set; }
}
Теперь создадим класс, унаследованный от MarshalByRefObject, чтобы его можно было перехватывать с помощью TransparentProxyInterceptor. Атрибут HelloWorld объявлен выше.
[HelloWorld]
public class SomeService1: MarshalByRefObject
{
public void Method1()
{
Console.WriteLine("SomeService1.Method1");
}
}
В Main напишем код
container
.AddNewExtension<Interception>()
.Configure<Interception>()
.SetDefaultInterceptorFor<SomeService1>(new TransparentProxyInterceptor());
var s = container.Resolve<SomeService1>();
s.Method1();
Console.ReadLine();
При выполнении будет выведено
Hello, world SomeService1.Method1
Предположим что SomeService1 нам недоступен и уберем атрибут HelloWorld. Чтобы получить такую же функциональность программы надо дописать несколько строк кода
container
.AddNewExtension<Interception>()
.Configure<Interception>()
.SetDefaultInterceptorFor<SomeService1>(new TransparentProxyInterceptor())
.AddPolicy("Policy")
.AddMatchingRule(new TypeMatchingRule(typeof(SomeService1)))
.AddCallHandler<HelloWorldCallHandler>();
var s = container.Resolve<SomeService1>();
s.Method1();
Console.ReadLine();
Аналогичные настройки можно задать в конфигурационном файле.
