Шаблон MVP с использованием веб-форм и создания объектов DI - PullRequest
6 голосов
/ 15 мая 2009

Я использую общий шаблон репозитория для сохранения моих данных. В PageLoad я создаю новый объект Repository (из IRepository), а в PageUnload я его удаляю.

Должна ли MasterPage / Page отвечать за создание экземпляров объектов, передаваемых докладчику, или докладчик должен отвечать за это? Меня больше интересует тестирование докладчика, чем страницы (просмотр), так как имитировать интерфейсы, передаваемые докладчику, проще.

Пример страницы

public partial class _Default : System.Web.UI.Page
{
    private IRepository _repo;
    protected void Page_Load(object sender, EventArgs e)
    {
        if (_repo == null)
            _repo = new Repository();
        ConnectPresenter();
    }

    private void ConnectPresenter()
    {
        _DefaultPresenter presenter = new _DefaultPresenter(_repo);
    }

    private void Page_Unload(object sender, EventArgs e)
    {
        if (_repo != null)
            _repo.Dispose();
    }
}

Поможет ли в этом случае DI Framework, например StructureMap или Ninject? Будет ли он отвечать за удаление таких объектов, как этот?

Ответы [ 2 ]

6 голосов
/ 07 ноября 2009

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

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
    }

    public _DefaultPresenter Presenter { get; set; }
}

Страница не должна нуждаться в какой-либо ссылке на хранилище, поскольку она будет вставлена ​​в докладчик.

Остальная часть этого ответа относится к StructureMap - детали могут отличаться для других контейнеров.

Чтобы включить установку сеттера, вам нужно указать StructureMap, какие свойства заполнять. Одним из способов является применение атрибута [SetterProperty] к самому свойству. Тем не менее, это может показаться немного инвазивным, чтобы иметь детали StructureMap в ваших классах. Другой способ - настроить StructureMap так, чтобы он знал, какие типы свойств вводить. Например:

protected void Application_Start(object sender, EventArgs e)
{
    ObjectFactory.Initialize(x =>
    {
        x.Scan(scan =>
        {
            scan.TheCallingAssembly();
            scan.WithDefaultConventions();
        });
        x.ForRequestedType<IRepository>().TheDefaultIsConcreteType<Repository>().CacheBy(InstanceScope.Hybrid);
        x.SetAllProperties(set => set.WithAnyTypeFromNamespaceContainingType<IRepository>());
    });
}

Метод SetAllProperties позволяет вам сообщать StructureMap, как распознать свойства, которые он должен заполнить. В этом случае я говорю, что StructureMap внедряет всех докладчиков (при условии, что они все находятся в одном пространстве имен).

Вам по-прежнему необходимо вводить сеттер для каждого запроса. В StructureMap вы используете метод BuildUp () для внедрения зависимостей в существующий экземпляр. Вы можете сделать это в событиях Init или Load каждой страницы или базового класса страницы, но опять же, это кажется инвазивным. Чтобы полностью исключить контейнер из ваших классов страниц, вы можете использовать событие PreRequestHandlerExecute приложения (в global.asax или IHttpModule):

protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
    var application = (HttpApplication)sender;
    var page = application.Context.CurrentHandler as Page;
    if (page == null) return;
    ObjectFactory.BuildUp(page);
}

Наконец, если вы хотите явно удалить свой IRepository, вы можете обработать это в событии EndRequest:

protected void Application_EndRequest(object sender, EventArgs e)
{
    var disposable = ObjectFactory.GetInstance<IRepository>() as IDisposable;
    if (disposable != null) disposable.Dispose();
}

Обратите внимание, что это работает правильно, потому что при инициализации мы указали StructureMap кэшировать IRepository с помощью Hybrid, что означает «дать мне один и тот же экземпляр для каждого HTTP-запроса (или потока, если он не работает на веб-сайте)». Когда вы получаете IRepository в EndRequest, вы получите тот же, который использовался в запросе, и вы можете его утилизировать.

2 голосов
/ 05 ноября 2009

Да, вам стоило бы изучить один из примеров использования DI с ASP.NET .

Да, удаление объектов поведения по запросу в соответствующей точке обычно управляется интеграцией контейнера с ASP.NET.

Типичная схема состоит в том, что создание объекта начинается со страницы и Application / Module s внутрь. Обычно вы отмечаете свойства [Inject] в своем классе Page, но это зависит от того, как вы организовали свою триаду. Обычно докладчик может использовать Constructo Injection для объявления того, что ему нужно, независимо от того, является ли он тестовым или ASP.NET cotext. Затем во время выполнения зависимости будут удовлетворены DI. Во время тестирования вы все еще можете использовать DI, хотя в других случаях может быть более естественным просто создать несколько подделок вместе с SUT и передать их докладчику.

Что касается договоренностей о тестировании, я нашел эту статью MSDN Mag об использовании Ninject с xUnit.net Джастином Этереджем очень полезной, хотя она и предназначена для ASP.NET MVC.

...