Что на самом деле делают перехватчики с моим классом c #? - PullRequest
10 голосов
/ 07 апреля 2010

Меня попросили реализовать динамический прокси-замок в моем веб-приложении asp.net, и я просматривал пару статей, которые я получил от Проекта замка и Кодовый проект о динамике замка прокси в веб-приложении asp.net ....

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

Ответы [ 2 ]

51 голосов
/ 07 апреля 2010

Допустим, вашему классу нужно выполнить 3 действия для определенной операции:

  1. Выполнить проверку безопасности;
  2. Записать вызов метода;
  3. Кэшировать результат.

Предположим также, что ваш класс ничего не знает о специфическом способе настройки безопасности, ведения журнала или кэширования. Вы должны зависеть от абстракций этих вещей.

Есть несколько способов сделать это. Один из способов - установить несколько интерфейсов и использовать инжектор конструктора:

public class OrderService : IOrderService
{
    private readonly IAuthorizationService auth;
    private readonly ILogger logger;
    private readonly ICache cache;

    public OrderService(IAuthorizationService auth, ILogger logger,
        ICache cache)
    {
        if (auth == null)
            throw new ArgumentNullException("auth");
        if (logger == null)
            throw new ArgumentNullException("logger");
        if (cache == null)
            throw new ArgumentNullException("cache");
        this.auth = auth;
        this.logger = logger;
        this.cache = cache;
    }

    public Order GetOrder(int orderID)
    {
        auth.AssertPermission("GetOrder");
        logger.LogInfo("GetOrder:{0}", orderID);
        string cacheKey = string.Format("GetOrder-{0}", orderID);
        if (cache.Contains(cacheKey))
            return (Order)cache[cacheKey];
        Order order = LookupOrderInDatabase(orderID);
        cache[cacheKey] = order;
        return order;
    }
}

Это не ужасный код, но подумайте о проблемах, которые мы вводим:

  • Класс OrderService не может функционировать без всех трех зависимостей. Если мы хотим сделать так, чтобы это было возможно, нам нужно начинать заполнять код с нулевых проверок везде.

  • Мы пишем тонну дополнительного кода для выполнения относительно простой операции (поиска заказа).

  • Весь этот шаблонный код должен повторяться в каждом методе, что приводит к очень большой, уродливой, подверженной ошибкам реализации.

Вот класс, который гораздо легче поддерживать:

public class OrderService : IOrderService
{
    [Authorize]
    [Log]
    [Cache("GetOrder-{0}")]
    public virtual Order GetOrder(int orderID)
    {
        return LookupOrderInDatabase(orderID);
    }
}

В Аспектно-ориентированном программировании эти атрибуты называются Точки соединения , полный набор которых называется Срез точки .

Вместо того, чтобы фактически писать код зависимости снова и снова, мы оставляем «подсказки», что некоторые дополнительные операции должны выполняться для этого метода.

Конечно, эти атрибуты нужно превратить в код иногда , но вы можете отложить это вплоть до кода вашего основного приложения, создав прокси для OrderService (обратите внимание, что метод GetOrder был сделан virtual, поскольку он должен быть переопределен для службы), и перехват метод GetOrder.

Запись перехватчика может быть такой простой:

public class LoggingInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        if (Attribute.IsDefined(invocation.Method, typeof(LogAttribute))
        {
            Console.Writeline("Method called: "+ invocation.Method.Name);
        }
        invocation.Proceed();
    }
}

И создание прокси будет:

var generator = new ProxyGenerator();
var orderService = (IOrderService)generator.CreateClassProxy(typeof(OrderService),
    new LoggingInterceptor());

Это не только намного менее повторяющийся код, но полностью устраняет фактическую зависимость , потому что посмотрите, что мы сделали - у нас даже нет авторизации или Система кеширования пока нет, но система все еще работает. Мы можем просто вставить логику авторизации и кэширования позже, зарегистрировав другой перехватчик и проверив на AuthorizeAttribute или CacheAttribute.

Надеюсь, это объясняет "почему".

Боковая панель: Как комментирует Кшиштоф Козьмич, использование DP-перехватчика не является «лучшей практикой» для DP. В рабочем коде вы не хотите, чтобы перехватчик работал для ненужных методов, поэтому используйте взамен IInterceptorSelector .

3 голосов
/ 07 апреля 2010

Причина, по которой вы будете использовать Castle-DynamicProxy, заключается в том, что называется аспектно-ориентированным программированием. Это позволяет вставлять код в стандартный поток операций вашего кода без необходимости зависеть от самого кода.

Простой пример - регистрация. Чтобы вы создали DynamicProxy вокруг класса, в котором у вас есть ошибки, он регистрирует данные, поступающие в метод, и перехватывает любые исключения, а затем регистрирует исключение.

Используя перехватчик, ваш текущий код даже не подозревает, что он существует (при условии, что ваше программное обеспечение построено несвязанным образом с интерфейсами правильно), и вы можете изменить регистрацию ваших классов с помощью инвертирования контейнера управления, чтобы использовать вместо этого прокси-класс без необходимости менять ни одной строки в другом месте кода. Затем, когда вы исправите ошибку, вы можете отключить проксирование.

Более расширенное использование прокси можно увидеть в NHibernate, где вся ленивая загрузка выполняется через прокси.

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