2015-04-26 4 views
4

Я искал конкретное решение для регистрации АОП. Мне нужен перехват, что делает возможным сделать что-то вроде этого:Autofac Interception с пользовательскими атрибутами

[MyCustomLogging("someParameter")] 

Дело в том, я видел примеры в других структур DI, что делает это возможным. Но мой проект уже использует Autofac для DI, и я не знаю, насколько хорошая идея сочетается с Unity (например). В Autofac.extras.dynamiclibrary2 класс InterceptAttribute запечатан.

У кого-нибудь есть идея по этой проблеме?

Ps .: Я бы доволен этим:

[Intercept(typeof(MyLoggingClass), "anotherParameter"] 

ответ

9

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

Маркировка код с атрибутом, как вы показанное имеет некоторые серьезные недостатки:

  1. Это делает ваш код зависит от используемой библиотеки перехвата, что делает код труднее изменить и делает его более трудным для замены внешних библиотек. Количество зависимостей, которые ядро ​​вашего приложения имеет с внешними библиотеками, должно быть сведено к абсолютному минимуму. Было бы иронично, если бы ваш код был завален зависимыми от библиотеки Injection Dependency; инструмент, который используется для минимизации внешних зависимостей и увеличения свободной связи.
  2. Чтобы применять сквозные проблемы для широкого круга классов (это то, что вы обычно хотите делать), вам нужно будет пройти полную базу кода, чтобы добавлять или удалять атрибуты из методов. Это требует много времени и ошибок. Но еще хуже, чтобы убедиться, что аспекты выполняются в определенном порядке, сложно с атрибутами. В некоторых фреймворках вы можете указать порядок для атрибута (используя какое-то свойство Order), но изменение порядка означает внесение радикальных изменений в код для изменения атрибутов Order. Забытие одного приведет к ошибкам. Это является нарушением Open/closed principle.
  3. Поскольку атрибут ссылается на класс аспект (в вашем примере typeof(MyLoggingClass)), это делает ваш код все еще статически зависимым от сквозной проблемы. Замена класса на другой снова приведет к радикальным изменениям в вашей базе кода, и сохранение жесткой зависимости значительно усложняет повторное использование кода или решение во время выполнения или время развертывания, должен ли применяться аспект или нет. Во многих случаях вы не можете иметь эту зависимость от вашего кода к аспекту, потому что код живет в базовой библиотеке, в то время как этот аспект относится к структуре приложения. Например, у вас может быть одна и та же бизнес-логика, которая работает как в веб-приложении, так и в службе Windows. Когда вы запускаете веб-приложение, вы хотите войти в систему по-другому. Другими словами, вы нарушаете Dependency inversion principle.

Я поэтому считаю применение атрибутов таким образом плохая практика. Вместо использования таких атрибутов прозрачно использовать сквозные проблемы, используя перехваты или декораторы. Декораторы - мой предпочтительный подход, потому что их использование намного чище, проще и, следовательно, более удобно. Декораторы могут быть написаны без необходимости зависеть от любой внешней библиотеки, и поэтому их можно разместить в любом подходящем месте в вашем приложении.Однако недостатком декораторов является то, что очень сложно писать и применять их, если ваш дизайн не SOLID, DRY, и вы не следуете за Reused abstraction principle.

Но если вы используете right application design с использованием SOLID и message based patterns, вы узнаете, что применение сквозных проблем, таких, как лесозаготовки является лишь вопросом написания очень простой декоратор, такие как:

public class LoggingCommandHandlerDecorator<T> : ICommandHandler<T> 
{ 
    private readonly ILogger logger; 
    private readonly ICommandHandler<T> decoratee; 
    public LoggingCommandHandlerDecorator(ILogger logger, ICommandHandler<T> decoratee) { 
     this.logger = logger; 
     this.decoratee = decoratee; 
    } 

    public void Handle(T command) { 
     this.logger.Log("Handling {0}. Data: {1}", typeof(T).Name, 
      JsonConvert.SerializeObject(command)); 
     this.decoratee.Handle(command); 
    } 
} 

Без надлежащий дизайн, вы все равно можете использовать перехват (без атрибутов), поскольку перехват позволяет вам «украшать» любые типы, которые, похоже, не имеют отношения к коду (не имеют общего интерфейса). Определение того, какие типы перехватывать и которые не могут быть громоздкими, но вы, как правило, все еще сможете определить это в одном месте приложения, таким образом, без необходимости делать радикальные изменения на всей базе кода.

Боковой узел. Как я уже сказал, использование атрибутов для описания pure metadata в порядке и preferable. Например, возьмите код, который разрешен только для пользователей с определенными разрешениями. Вы можете отметить, что код следующим образом:

[Permission(Permissions.Crm.ManageCompanies)] 
public class BlockCompany : ICommand { 
    public Guid CompanyId; 
} 

Этот атрибут не описывает, какие аспекты выполняются, и не ссылаться на любые виды из внешней библиотеки (PermissionAttribute является то, что вы можете (и должны) определить себя), или любых типов, специфичных для АОП. Он только обогащает код метаданными.

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

public class PermissionCommandHandlerDecorator<T> : ICommandHandler<T> 
{ 
    private static readonly Guid requiredPermissionId = 
     typeof(T).GetCustomAttribute<PermissionAttribute>().PermissionId; 

    private readonly IUserPermissionChecker checker; 
    private readonly ICommandHandler<T> decoratee; 

    public PermissionCommandHandlerDecorator(IUserPermissionChecker checker, 
     ICommandHandler<T> decoratee) { 
     this.checker = checker; 
     this.decoratee = decoratee; 
    } 

    public void Handle(T command) { 
     this.checker.CheckPermission(requiredPermissionId); 
     this.decoratee.Handle(command); 
    } 
} 
+1

Спасибо, Стивен за ваш замечательный ответ. Это заставило меня задуматься. –

+0

Я рад, что смогу помочь, хотя я не ответил на ваш вопрос :-) – Steven

+1

@Steven ваш ответ был действительно потрясающим, вы предоставили мне несколько замечательных идей. Однако, когда дело доходит до применения инструментального в общем смысле, я думаю, что лучший способ - механизм перехвата (а не использование атрибутов). Для того чтобы украсить каждый интерфейс приложения для реализации подобной логики приборов, это нарушило бы принцип СУХОЙ. На самом деле, Mark Seemann имеет отличную запись в блоге по этому вопросу: http://blog.ploeh.dk/2010/09/20/InstrumentationwithDecoratorsandInterceptors/. – LunaticSoul

Смежные вопросы