Внедрение зависимостей нижнего уровня в Presenter в приложении ASP.NET MVP - PullRequest
2 голосов
/ 27 января 2010

Я недавно прочитал пост Фила Хаака , где он приводит пример реализации Model View Presenter для ASP.NET. Один из фрагментов кода показывает, как код для класса представления.

public partial class _Default : System.Web.UI.Page, IPostEditView
{    
    PostEditController controller;
    public _Default()
    {
         this.controller = new PostEditController(this, new BlogDataService());
    }
}

Однако здесь представление создает экземпляр BlogDataService и передает его докладчику. В идеале представление не должно знать ни о BlogDataService, ни о каких-либо зависимостях нижнего уровня докладчика. Но я также предпочитаю сохранять BlogDataService как инжекторную зависимость презентатора, поскольку это делает зависимости презентатора явными.

Этот же вопрос был задан для stackoverflow здесь .

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

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

Или есть лучший дизайн для решения проблемы? Может ли быть лучший способ реализовать это, если я создаю приложение WinForms вместо приложения ASP.NET WebForms?

Спасибо за любые отзывы.

Ответы [ 2 ]

2 голосов
/ 01 февраля 2010

Я сделал именно это. Решение основано на Autofac, но может быть реализовано поверх любого контейнера.

Сначала определите интерфейс, представляющий полномочия для представления представлений в запросе к системе MVP:

public interface IMvpRequest
{
    void Present(object view);
}

Затем создайте базовую страницу со свойством этого типа:

public abstract class PageView : Page
{
    public IMvpRequest MvpRequest { get; set; }
}

На этом этапе настройте внедрение зависимостей для страниц. Большинство контейнеров имеют интеграцию ASP.NET, обычно в форме модулей HTTP. Поскольку мы не создаем экземпляр страницы, мы не можем использовать инъекцию конструктора и должны использовать только инъекцию свойства здесь.

После этого создайте аргументы события, представляющие представление, готовое к представлению:

public class PresentableEventArgs : EventArgs
{}

Теперь перехватите события в PageView и передайте их запросу (также представьте страницу):

protected override bool OnBubbleEvent(object source, EventArgs args)
{
    var cancel = false;

    if(args is PresentableEventArgs)
    {
        cancel = true;

        Present(source);
    }
    else
    {
        cancel = base.OnBubbleEvent(source, args);
    }

    return cancel;
}

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    Present(this);
}

private void Present(object view)
{
    if(MvpRequest != null && view != null)
    {
        MvpRequest.Present(view);
    }
}

Наконец, создайте базовые классы для каждого типа элемента управления, который вы хотите использовать в качестве представления (главные страницы, составные элементы управления и т. Д.):

public abstract class UserControlView : UserControl
{
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        EnsureChildControls();

        RaiseBubbleEvent(this, new PresentableEventArgs());
    }
}

Это соединяет дерево управления с системой MVP через IMvpRequest, которое вы теперь должны будете реализовать и зарегистрировать в контейнере уровня приложения. Интеграция ASP.NET должна позаботиться о внедрении реализации на страницу. Это полностью отделяет страницу от создания докладчика, полагаясь на IMvpRequest для сопоставления.

Реализация IMvpRequest будет зависеть от контейнера. Представители будут зарегистрированы в контейнере, как и другие типы, то есть их конструкторы будут автоматически разрешены.

У вас будет какая-то карта от типов просмотра к типам презентаторов:

public interface IPresenterMap
{
    Type GetPresenterType(Type viewType);
}

Это типы, которые вы разрешите из контейнера.

(Единственное, что здесь понятно, это то, что представление уже существует, то есть контейнер не создает экземпляр или вообще не знает о нем. Вам нужно будет передать его как параметр разрешения, еще одну концепцию, поддерживаемую большинством контейнеров.)

Приличное отображение по умолчанию может выглядеть так:

[Presenter(typeof(LogOnPresenter))]
public class LogOnPage : PageView, ILogOnView
{
    // ...
}
2 голосов
/ 29 января 2010

Да, есть. Например, используя StructureMap в конструкторе веб-формы:

 public partial class AttributeDetails : EntityDetailView<AttributeDetailPresenter>, IAttributeDetailView
    {
 public AttributeDetails()
        {
            _presenter = ObjectFactory.With<IAttributeDetailView>(this).GetInstance<AttributeDetailPresenter>();
        }

....
}

и, как вы можете видеть здесь, докладчику необходимо просмотреть и внедрить сервис

 public AttributeDetailPresenter(IAttributeDetailView view, IAttributeService attributeService)
        {
            MyForm = view;
            AppService = attributeService;
        }

Вы также можете использовать функцию StructureMap BuildUp для веб-форм, чтобы вы могли избежать использования ObjectFactory непосредственно в своем представлении.

...