在ASP.NET MVP应用程序中在Presenter中注入较低层依赖关系

时间:2010-01-27 19:17:48

标签: asp.net dependency-injection inversion-of-control mvp

我最近阅读了Phil Haack's post,其中给出了一个实现Model View Presenter for 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 here上提出了同样的问题。

其中一个答案建议使用服务定位器获取BlogDataService的实例并将其传递给演示者的构造函数。但是,此解决方案无法解决查看BlogDataService并需要明确获取引用的视图问题它。

是否有办法使用IoC或DI容器工具自动构建presenter对象,以便视图不必处理显式创建BlogDataService对象,还将视图和服务实例注入到演示者的构造函数中。我更喜欢尽可能使用构造函数注入模式。

或者有更好的设计可以解决问题吗?有没有更好的方法来实现这个如果我正在构建一个WinForms应用程序而不是ASP.NET WebForms应用程序?

感谢您的反馈。

2 个答案:

答案 0 :(得分:2)

是的。例如,在webform构造函数中使用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。

答案 1 :(得分:2)

我做到了这一点。该解决方案基于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());
    }
}

这会通过IMvpRequest将控制树连接到MVP系统,您现在必须实现它并在应用程序级容器中注册。 ASP.NET集成应该注意将实现注入页面。这使页面与演示者创建完全分离,依靠IMvpRequest进行映射。

IMvpRequest的实施将特定于容器。演示者将像其他类型一样在容器中注册,这意味着他们的构造函数将自动解析。

您将拥有从视图类型到演示者类型的某种地图:

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

这些是您将从容器中解析的类型。

(这里的问题是视图已经存在,这意味着容器不会创建实例或者不知道它。您必须将其作为解析参数传递,这是大多数容器支持的另一个概念。)

一个不错的默认映射可能如下所示:

[Presenter(typeof(LogOnPresenter))]
public class LogOnPage : PageView, ILogOnView
{
    // ...
}