пятница, 23 сентября 2011 г.

АОП на .NET без Unity

Очередная попытка вернуться к повседневному использованию аспектно-ориентированного проектирования в проектах на .NET не увенчалась успехом. Причина проста: языковые средства слабо приспособлены для этого. Как не придумывай красивое и казалось бы надежное решение, но хоть в одном месте оно дает слабину - а значит для серьезных задач не подходит.

Рассматривались три варианта решения:

1. Транспарентные прокси
Суть метода в том, что посредством Reflection мы создаем новый тип, который наследуется от нужного интерфейса и реализует прозрачный прокси-класс, выполняющий аспекты перед вызовом.
Этот метод посредством контейнера Unity я попробовал применить на живом проекте и у меня осталась масса впечатлений :)
http://fullfeedbag.blogspot.com/2010/09/aop-application-unity.html


Плюсы: после некоторых мучений АОП все же работает

Минусы: как по мне, то их оочень много
Самая большая беда: если что то сделать не так - аспекты могут отвалиться. Это не сильно страшно скажем для логирования. Но вот если на аспектах построен контроль синхронизации потоков, механизмы валидации или уровни доступа при авторизации, то отвалившиеся аспекты сделают целевую систему жутко уязвимой.
А сделать что то не так, к сожалению, очень даже легко, поскольку транспарентный прокси генерируется внешним компонентом, и если случайно его создать скажем с помощью оператора new - то аспекты не работают.
Вторая проблема: упавшая производительность. Падает она действительно очень сильно
Третья проблема: отладка кода сильно затрудняется. Отладчик Visual Studio неохотно ест сгенерированные в runtime типы данных
Четвертая проблема: багоемкость и высокий порог понимания для программистов. Объяснить принципы работы транспарентных прокси новому человеку, пришедшему на проект, ох как непросто. В совокупности с отваливающимися аспектами и диковатой отладкой это превращается в сущий ад


2. Модификация сборок в рантайме
Вообще, эта идея мне пришла сразу как только я начал применять АОП :) Мы просто берем и перехватываем метод класса. Без всяких прокси и прочей мути.
Один хороший человек разработал библиотечку для перехвата методов
http://www.codeproject.com/KB/dotnet/CLRMethodInjection.aspx
Если чуть пораскинуть мозгами - то ее можно применить для реализации АОП

Плюсы: решается проблема с быстродействием. Отваливания аспектов при грамотной реализации интерцептора не будет

Минусы:
1. Отладка еще хуже чем в случае с транспарентными прокси
2. Из-за того что метод использует грязные хаки чуть менее чем полностью, он очень сильно завязывается на версии .net framework и на его внутренних структурах данных. Проблема при переносе кода x32/x64. Если что то не так - программа падает
В общем для коммерческого проекта такой метод применять не очень хочется. Но сам по себе он очень интересен. Вот еще годные линки по теме покопаться в недрах дотнета:
http://www.codeproject.com/KB/dotnet/DotnetInternals_Injection.aspx
http://msdn.microsoft.com/en-us/magazine/cc163791.aspx
http://ntcore.com/files/disasmsil.htm


3. Модификация сборок до загрузки с помощью Mono.Cecil
Ахтунг! Не смотря на название либы Mono.Cecil, к самому Mono она, по видимому, не имеет отношения


Замысел в том, чтобы пофиксать сборочку один раз на диске и прикрутить к ней почти полноценное АОП. Библиотека Mono.Cecil позволит нам модифицировать сборки не копаясь в памяти и во внутренней структуре.


Плюсы: такие же как у всех - работающее АОП в результате
По поводу отладки затрудняюсь сказать (не пробовал), но думаю что с ней такие же проблемы как и у предыдущих методов


Минусы:
1. Фиксап подписанной (signed) сборки приводит к слетанию ЭЦП и неработоспособности сборки (кстати предыдущий метод был лишен такой проблемы).
Лечится тем, что нужно запустить процедуру фиксапа сразу после компиляции, ДО операции подписывания DLL. Это подразумевает то, что DLL своего производства. С чужими DLL такое не прокатывает

2. Операцию инжекции аспектов в сборку нужно запускать отдельно от кода. Если ее случайно не запустить - ошибка не выдается, аспекты не работают.
Введение этапа посткомпиляции - достаточно некрасивое решение. Хотя что то подобное сделали для прекомпиляции в Qt (MOC), так что может не все так плохо?

У этого метода есть приличные готовые реализации:
http://habrahabr.ru/blogs/net/95211/


Мораль сей басни состоит в том, что таки-нету идеальной реализации АОП на .NET
Так что ждем поддержки средств АОП от Microsoft на уровне компилятора. Ну или хотя бы stable версию АОП-фреймверка с Mono.Cecil


P.S. Я забыл упомянуть о PostSharp. Он использует и транспарентные прокси, и модификацию IL кода. Но, блин, стОит он дороговато...

P.P.S Литература по теме применения аспектно-ориентированного программирования:
http://www.javable.com/columns/aop/workshop/01/
http://www.javable.com/columns/aop/workshop/02/
http://citforum.ru/internet/javascript/aop/

Комментариев нет:

Отправить комментарий