Веб-формы и инъекции зависимостей
Сейчас я внедряю инфраструктуру Injection Dependency в существующее приложение WebForms (используя Castle Windsor).
У меня довольно глубокий опыт работы с DI, и я очень сильно рекомендую впрыск конструктора над инъекцией сеттера. Если вы знакомы с Webforms, вы знаете, что инфраструктура ASP.Net обрабатывает конструкцию страниц и объектов управления, что делает невозможным истинное вложение конструктора.
Мое текущее решение - зарегистрировать контейнер в событии Application_Start для Global.asax и сохранить контейнер как общедоступную статическую переменную в Global. Затем я просто разрешаю каждую услугу, которая мне нужна непосредственно на странице, или управление, когда она мне нужна. Поэтому в верхней части каждой страницы я получаю код следующим образом:
private readonly IMyService _exposureManager = Global.IoC.Resolve<IMyService>();
private readonly IMyOtherService _tenCustomersExposureManager = Global.IoC.Resolve<IMyOtherService>();
Очевидно, мне не нравится, что все эти ссылки на контейнер, разбросанные по моему приложению, или мои зависимости на странице/контроле не явные, но я не смог найти лучший способ.
Есть ли более элегантное решение для использования DI с Webforms?
Ответы
Ответ 1
Я согласен с @DarinDimitrov, что MVP - интересный вариант. Однако при работе с устаревшим приложением переписывание существующей страницы в шаблон MVP - это чертовски работа. В этом случае лучше начать с шаблона Locator (но только в ваших классах пользовательского интерфейса), как вы уже делаете. Однако измените одно. Не выставляйте выбранный контейнер DI в приложение, так как я ожидаю, что вы выполняете его с свойством Global.IoC
.
Вместо этого создайте статический метод Resolve<T>
в классе Global
. Это полностью скрывает контейнер и позволяет выполнять операции замены, не изменяя ничего на ваших веб-страницах. Когда вы это делаете, нет никакой пользы в использовании Common Service Locator, как предлагает @Wiktor. Локатор общих служб - это еще одна абстракция для чего-то, что не нужно абстрагировать (поскольку вы уже отбросили контейнер с помощью Global.Resolve<T>
).
К сожалению, с веб-формами, на самом деле нет хорошего способа сделать это. Для Simple Injector я написал руководство по интеграции для веб-форм, который в основном описывает использование метода Global.Resolve<T>
, но также показывает способ тестирования, если классы страницы могут быть созданы. Руководство также может использоваться для других контейнеров DI.
Кстати, пожалуйста, имейте в виду, что с Castle Windsor все, что вы запрашиваете, должно быть открыто выпущено (Register Resolve Release pattern). Это немного неприятно (IMO) и отличается от того, как работают другие контейнеры, и может быть источником утечек памяти, если вы этого не сделаете правильно.
Последняя записка. Можно выполнить инъекцию конструктора с помощью Web Forms. Ну... вроде, так как это вызовет перегруженный конструктор, используя отражение после того, как Form
был создан с использованием конструктора по умолчанию, поэтому это вызывает Temporal Coupling.
Ответ 2
Есть ли более элегантное решение для использования DI с Webforms?
Yeap, шаблон MVP позволяет вам иметь четкое разделение проблем в приложении WebForms. И когда у вас есть разделение проблем и слабая связь, DI легко.
И в ASP.NET MVC, который встроен.
Ответ 3
ASP.NET MVC имеет IDependencyResolver и класс статического менеджера, который позволяет вам установить и установить преобразователь. Мне не понравилась идея ссылки на System.Web.Mvc в проекте веб-форм, поэтому я пошел с IServiceLocator, который делает примерно то же самое вещь:
public static class Bootstrapper
{
private static readonly IUnityContainer _container = new UnityContainer();
public static void Initialize()
{
ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(_container));
_container.RegisterType<IDriverService, DriverService>();
}
public static void TearDown()
{
_container.Dispose();
}
}
public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
Bootstrapper.Initialize();
}
protected void Application_End(object sender, EventArgs e)
{
Bootstrapper.TearDown();
}
}
Затем в классе страницы...
IDriverService service = ServiceLocator.Current.GetInstance<IDriverService>();
Или подключите DI через впрыск конструктора. Я еще не пошел по этой дороге с веб-формами, поэтому кому-то еще нужно заполнить для меня:) (Я живу в основном на землях MVC около года).
В моем примере используется Unity, но вы можете легко адаптировать его к любой другой реализации DI.
Ответ 4
Как @DarinDimitrov говорит, что шаблон MVP - это способ использовать DI/IOC с Webforms.
Либо вы можете выполнить собственную реализацию, либо использовать существующую инфраструктуру. Я хорошо слышал о Webforms MVP, но я его на самом деле не использовал.
Согласно документам, он встроил поддержку DI через Castle Windsor, Autofac и Unity. Он также имеет автоматическое обнаружение на основе конвенций для докладчиков.
Ответ 5
На самом деле то, что вы только что создали, - это ваша собственная реализация Locator. Но, почти наверняка, реализация уже существует для рамки IoC по вашему выбору.
http://commonservicelocator.codeplex.com/