Почему все говорят, что инъекция зависимостей в веб-формах ASP.NET затруднена, когда существуют PageHandlerFactory и IHttpHandlerFactory?

Итак, у меня есть сайт устаревших веб-форм, и я работаю над тем, чтобы упростить его поддержку. Отбросить его и переписать это не вариант.

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

Различные люди, с которыми я разговаривал в режиме онлайн и вне, рассказывали мне, что я мог бы сделать инъекцию свойств с помощью HttpModule, который сканирует класс страницы для свойств, украшенных атрибутом Inject или аналогичным, но это звучит как удар отражения (кешированный, но все же) по каждому запросу. Не привлекателен.

Итак, я смотрел другие варианты и наткнулся на System.Web.IHttpHandlerFactory, который, по-видимому, был в рамках с версии v2. Можно удалить обработчик *.aspx по умолчанию и заменить его на тот, который использует пользовательскую реализацию, в разделе httpHandlers web.config.

Итак, люди, с которыми я разговаривал, не тупые; Я думал, что попрошу здесь. Есть ли какие-либо проблемы с заменой веб-форм PageHandlerFactory на реализацию на основе IoC...?

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

Ответы

Ответ 1

Из-за того, как разработан ASP.NET, классы Page должны иметь конструктор по умолчанию. Когда вы хотите использовать инъекцию конструктора, есть способ обойти это. Вы можете сделать конструктор по умолчанию защищенным и добавить один открытый конструктор, который принимает зависимости следующим образом:

public partial class _Default : System.Web.UI.Page
{
    private IUserService service;

    protected _Default()
    {
    }

    public _Default(IUserService service)
    {
        this.service = service;
    }
}

Это позволяет создать пользовательский PageHandlerFactory и вставить зависимости в конструкторе.

Итак, это работает, но есть уловка. Класс _Default, который вы определяете, не соответствует используемому классу ASP.NET. ASP.NET создает новый класс, который наследует от _Default. Этот новый класс создает иерархию управления на основе разметки в файле .aspx. Этот класс выглядит примерно так:

public class ASPGeneratedDefault : _Default
{
    public ASPGeneratedDefault() : base()
    {
    }

     protected override void OnPreInit(object s, EventArgs e)
     {
          // Building up control hierarchy.
     }
}

Как вы можете видеть, пользовательский конструктор не был переопределен в ASPGeneratedDefault ASP.NET. Из-за этого не существует способа позволить DI framework создать для нас этот тип. Путь к этому заключается в том, чтобы позволить ASP.NET создать этот тип для нас и вызывать конструктор нестандартного класса базового класса _Default для этого существующего экземпляра. Поскольку этот экземпляр уже существует, мы должны сделать это с помощью рефлексии, и это не сработает при выполнении частичного доверия.

Кроме того, это работает для классов страниц, но не для пользовательских элементов управления на странице. Генератор кода ASP.NET сообщает о том, что эти элементы управления со своим конструктором по умолчанию во время процесса иерархии управления создают процесс. Если вы хотите, чтобы это сработало для них, вам понадобится ваш PageHandlerFactory для привязки к событию PreInit для этих элементов управления, потому что за время, когда был создан класс страницы, связанные элементы управления и пользовательские элементы управления еще не созданы. Однако, чтобы зарегистрировать событие PreInit на них, вам нужно найти эти элементы управления в классе страницы, и снова нам нужно задуматься над классом страницы. Поскольку элементы управления хранятся в непубличных экземплярах, снова это не будет работать в частичном доверии.

Независимо от того, является ли это проблемой ваше приложение не может работать в частичном доверии, зависит от вас, но поскольку модель безопасности .NET 4 значительно упрощена, очень просто запустить веб-приложения в частичном доверии, и это это то, к чему я стремлюсь.

TL;DR; Поэтому в заключение можно сделать это (см., Например, этот пример), но из-за ограничений ASP.NET Web Forms, вам нужно полностью доверять, чтобы заставить его работать.

ОБНОВЛЕНИЕ Microsoft устарела частичное доверие для ASP.NET, начиная с NET 4.0 (читайте здесь). Таким образом, с этой точки зрения, избегать полного доверия может быть не так полезно (в любом случае вам это понадобится).