Для тех кто не знает – 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();Аналогичные настройки можно задать в конфигурационном файле.