Я прошу прощения за длину, и я знаю, что есть некоторые ответы на этот вопрос, но я много искал и не нашел правильного решения, поэтому, пожалуйста, потерпите меня.
Я пытаюсь создатьфреймворк для унаследованных приложений для использования DI в веб-формах ASP.NET.Вероятно, я буду использовать Castle Windsor в качестве основы.
Эти устаревшие приложения в некоторых местах будут частично использовать шаблон MVP.
Докладчик будет выглядеть примерно так:
class Presenter1
{
public Presenter1(IView1 view,
IRepository<User> userRepository)
{
}
}
Теперь страница ASP.NET будет выглядеть примерно так:
public partial class MyPage1 : System.Web.UI.Page, IView1
{
private Presenter1 _presenter;
}
Перед использованием DI я создаю экземпляр Presenter следующим образом в OnInit страницы:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
_presenter = new Presenter1(this, new UserRepository(new SqlDataContext()));
}
Итак, теперь я хочу использовать DI.
Сначала я должен создать фабрику обработчиков, чтобы переопределить конструкцию моей страницы.Я нашел ЭТО действительно хороший ответ, чтобы помочь: Как использовать Dependency Injection с веб-формами ASP.NET
Теперь я могу легко настроить свои контейнеры в корне моей композиции, как предложил Марк Симан.Global.asax (хотя это означает создание статического контейнера, который должен быть потокобезопасным и запечатанным, чтобы не было возможности добавлять дополнительные регистрации)
Теперь я могу пойти и объявить внедрение конструктора на странице
public MyPage1() : base()
{
}
public MyPage1(Presenter1 presenter) : this()
{
this._presenter = presenter;
}
Теперь мы столкнулись с первой проблемой, у меня круговая зависимость.Presenter1 зависит от IView1, но страница зависит от докладчика.
Теперь я знаю, что некоторые скажут, что дизайн, вероятно, неверен, когда у вас есть циклические зависимости.Во-первых, я не думаю, что дизайн Presenter является неправильным, поскольку он переносит зависимость в конструкторе к представлению, и я могу сказать это, рассматривая множество реализаций MVP.
Некоторые могут предложить изменить страницу на дизайнгде Presenter1 становится свойством, а затем использует свойство инъекции
public partial class MyPage1 : System.Web.UI.Page, IView1
{
[Dependency]
public Presenter1 Presenter
{
get; set;
}
}
Некоторые могут даже предложить полностью удалить зависимость от Presenter, а затем просто подключиться через кучу событий, но это не тот дизайн, который я хотел, и, честно говоря,Не понимаю, почему мне нужно внести это изменение, чтобы приспособить его.
В любом случае, независимо от предложения, существует другая проблема:
Когда фабрика обработчиков получает запрос страницы, доступен только тип (НЕИНТЕРФЕЙС ПРОСМОТРА):
Type pageType = page.GetType().BaseType;
теперь используя этот тип, вы можете разрешить страницу через IoC и его зависимости:
container.Resolve(pageType)
После этого будет известно, что существует свойство с именем Presenter1 ибыть в состоянии ввести это.Но Presenter1 нужен IView1, но мы никогда не разрешали IView1 через контейнер, поэтому контейнер не будет знать, чтобы предоставить конкретный экземпляр фабрике обработчиков, только что созданной, как она была создана вне контейнера.
Так что нам нужно взломатьнаша фабрика обработчиков и замените интерфейс представления: так, где фабрика обработчиков разрешает страницу:
private void InjectDependencies(object page)
{
Type pageType = page.GetType().BaseType;
// hack
foreach (var intf in pageType.GetInterfaces())
{
if (typeof(IView).IsAssignableFrom(intf))
{
_container.Bind(intf, () => page);
}
}
// injectDependencies to page...
}
Это создает другую проблему, большинство контейнеров, таких как Castle Windsor, не позволят вам перерегистрировать этот интерфейс в том экземпляре, в котором он находится.указывая на сейчас.Кроме того, поскольку контейнер зарегистрирован в Global.asax, это не является потокобезопасным, поскольку контейнер должен читаться только в этой точке.
Другое решение состоит в создании функции для перестройки контейнера накаждый веб-запрос, а затем проверьте, содержит ли контейнер компонент IView, если не установлен экземпляр.Но это кажется расточительным и идет вразрез с предлагаемым использованием.
Другое решение - создать специальную фабрику под названием IPresenterFactory и поместить зависимость в конструктор страницы:
public MyPage1(IPresenter1Factory factory) : this()
{
this._presenter = factory.Create(this);
}
Проблема в том, что выТеперь нужно создать фабрику для каждого докладчика, а затем вызвать контейнер для разрешения других зависимостей:
class Presenter1Factory : IPresenter1Factory
{
public Presenter1Factory(Container container)
{
this._container = container;
}
public Presenter1 Create(IView1 view)
{
return new Presenter1(view, _container.Resolve<IUserRepository>,...)
}
}
Этот дизайн также кажется громоздким и сложным, есть ли у кого-нибудь идеи для более элегантного решения?