Страницы с тегами : AOP

Статьи про IoC, AOP и контейнер Unity

Первые посты про IoC я написал более двух лет назад, но судя по статистике блога люди часто к ним обращаются.

В этом после приведу индекс статей в порядке чтения для облегчения поиска и навигации

  1. Введение в IoC
  2. Основная задача IoC-контейнеров
  3. Первые шаги с Unity
  4. Рефакторинг legacy-кода для использования Unity
  5. Использование generic-классов с Unity
  6. Инъекция массивов в Unity
  7. Unity LifetimeManager
  8. Конфигурация Unity
  9. Инъекция самого контейнера Unity в объекты
  10. AOP в Unity
  11. Фабрики в Unity
  12. Практика AOP: аудит изменений данных в EF с учетом котнекста операций
  13. Unity 2.0
  14. AOP в Unity 2.0


Unity 2.0 Interception

Ранее я писал про механизмы AOP в Unity 1.0 (или 1.2 на тот момент). Недавно увидел этот пост, с примером для Unity 2.0.

В первой версии было фактически две возможности для задания перехватчиков вызовов: это атрибуты на классах или сложный код (или xml), задающий условия по которым будут срабатывать обработчики вызовов.

Оба варианта довольно плохие, первый заставляет править существующий код, второй просто неподъемный. В Unity 2.0 исправили ситуацию с помощью так называемых InterceptionBehavior.

Пример кода из поста в новом стиле.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using System.Transactions;


                
                
                
                
            


Аудит изменений с учетом контекста операций

В предыдущем посте я описал способ, который позволяет проводить аудит изменений данных в Entity Framework, не затрагивая сами классы сущностей.

Аудит изменений данных вполне возможно делать в самой БД с помощью триггеров, но в базу не попадают сведения о том какой пользователь системы произвел изменения (в случае приложения, построенного по принципу trusted subsystem) и с какой целью были проведены эти изменения.

Для каждой операции работы изменения данных существует некоторый контекст, который включает в себя как некоторые явные параметры, как имя пользователя или URL, так и неявные, например намерения, с которыми были выполнены изменения данных (частично их можно восстановить по стеку вызовов).

Для задач логирования было бы удобно иметь доступ к некоторым параметрам контекста, особенно неявным. Эти параметры должны прозрачно передаваться по цепочке вызовов. Для решения таких задач можно применить монады, но тогда придется переписать весь код под использование монад, что очень проблематично. Можно воспользоваться возможностями AOP и IoC чтобы обеспечить неявную передачу явного контекста.

Для начала нам понадобится класс для хранения текущего контекста. Как и в случае со стеком вызовов, контексты тоже могут составлять некоторый стек.

public interface IContextManager<T>
{
    void Push(T value);
    void Revert();
    IEnumerable<T> GetValues();
}


AOP времени исполнения в Unity

Для тех кто не знает – 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();
    }
}