Почему все говорят, что внедрение зависимостей в веб-формах ASP.NET сложно, когда существуют PageHandlerFactory и IHttpHandlerFactory? - PullRequest
16 голосов
/ 28 июня 2011

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

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

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

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

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

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

1 Ответ

28 голосов
/ 28 июня 2011

Из-за способа разработки 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 создать этот тип для нас.Обходной путь - позволить ASP.NET создать этот тип для нас и вызвать конструктор не по умолчанию базового класса _Default для этого существующего экземпляра.Поскольку этот экземпляр уже существует, мы должны сделать это с отражением, и это не удастся выполнить при частичном доверии.

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

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

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

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

...