我最近阅读了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应用程序?
感谢您的反馈。
答案 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
{
// ...
}