Платформа: NET Standard 2.0 |
На данный момент документация в Wiki неактуальна. Обновление в скором времени.
Библиотека предоставляет набор компонентов, реализующих некоторые возможности парадигмы АОП в языке C#. Если точнее, то библиотека позволяет вынести сквозную функциональность в отдельные компоненты и декларативно применять их к методам бизнес логики, используя атрибуты.
Как уже отмечалось, применений идей парадигмы позволяет избавиться от дублирующегося кода, разбросанного по всем слоям приложения. Примером может стать обработка ошибок. Допустим, есть ряд репозиториев, расположенных на уровне доступа к данным.
interface IUserRepository
{
int Add(User user);
int Update(User user);
}
В каждом методе репозитория находится одинаковая обработка ошибок вида
class UserRepository : IUserRepository
{
int Add(User user)
{
try {
// Добавление в базу пользователя
} catch (Exception e) {
Logger.Error(e.Message, e);
throw new WrappedException(e);
}
}
}
Рано или поздно обилие однообразных фрагментов кода приводит к появлению обобщенных обработчиков вида
class ExceptionHandlers {
public static T HandleExceptions<T>(Func<T> action)
{ ... }
}
И репозиторий преображается в следующий вид
class UserRepository : IUserRepository
{
int Add(User user)
{
return ExceptionHandlers.HandleExceptions(() =>
{
// Добавление пользователя
});
}
}
Однако, это не решает проблему и так же нарушает принцип DRY, так как для отличающихся по сигнатуре делегатов придется писать свой дублирующийся фрагмент кода. При этом комбинирование такого рода обработчиков сводит читаемость и удобство поддержки функциональности на нет. В варианте с применением АОП репозиторий преобразуется следующим образом
interface IUserRepository
{
[HandleExceptionAspect]
int Add(User user);
}
class UserRepository : IUserRepository {
int Add(User user) {
// Добавление пользователя
}
}
[AttributeUsage(AttributeTargets.Method)]
class HandleExceptionAspect : MethodBoundaryAspect
{
public override void OnException(IInvocationPipeline pipeline)
{
var exception = pipeline.CurrentException;
Logger.Error(exception.Message, exception);
// Выбросит исключение, сохранив стек-трейс
pipeline.Throw(new WrapperException(exception));
}
}
При этом возможно применение нескольких аспектов, в том числе аспектов верхнего уровня на интерфейсах без потери читаемости и поддерживаемости бизнес логики. Детализированные примеры аспектов и их использования представлены тут.
Делали само собой. Наиболее популярная библиотека реализующая АОП - это PostSharp. Его функциональность гораздо шире этой библиотеки, однако есть один минус - он платный. Есть и бесплатные альтернативы, например Fody и Mr.Advice, однако у них другой принцип работы. Данная библиотека выполняет динамическое проксирование вызовов через стандартные механизмы платформы (DispatchProxy), в то время как PostSharp и Fody выполняют инъекцию IL кода во время компиляции, что улучшает общую производительность, однако модифицирует код сборки. Наиболее близкое по принципу работы к представленной библиотеке - механизм динамического перехвата вызова в Castle.DynamicProxy. Для того чтобы лучше понять детали реализации библиотеки советую ознакомиться с этой статьей.
При разработке основной упор делается на производительность, так что сейчас вызов скомпонованного с аспектами метода немногим уступает по скорости, чем вызов метода через рефлексию. Так же есть возможность использования нескольких аспектов на методе и гибкий механизм управления пайплайном выполнения метода. Доступны плагины для интеграции со сторонними фреймворками по внедрению зависимостей и реализован механизм внедрения зависимостей в аспекты.