With all of the current talk about ASP.NET MVC it seem dated to bring up the model view presenter architecture but I still believe it’s a very strong pattern in separating UI logic from application design. This post is meant to offer a successor to the Web Client Software Factory (WCSF) framework provided by Microsoft. In last usage of WCSF I encountered numerous errors stemming from ObjectBuilder during the dependency injection of WCSF which is what lead me down the road of creating UnityWeb which has evolved into StructuredWeb. The rest of this post will include no specific dependencies except 1 to StructureMap to handle the actual resolution of objects through dependency injection. This could be very easily replaced by Unity or any other inversion of control framework that supports the BuildUp() method. For a framework that does not support a BuildUp operation it will make things more complicated and that will need to be solved by your case basis.
Firstly, what is the model-view-presenter (MVP) pattern? The View is defined as an interface that the Presenter will use for getting and setting data to and from the Model. The View implementation will instantiate the Presenter object and provide a reference to itself (the formal constructor parameter is the View interface while the actual parameter is a concrete View class). When the event methods of the View are triggered, they will do nothing but invoke a method of the Presenter which has no parameters and no return value. The Presenter will then get data from the View, through the View interface variable that the Presenter stored when the constructor was called. The Presenter then invokes methods of the Model, and sets data from the Model into the View through the View interface.
From a layering point of view, the Presenter class might be considered as belonging to the application layer in a multilayered architectured object-oriented system with common layers but it can also be seen as a Presenter layer of its own between the Application layer and the User Interface layer.
[/source]
Now if you’re at all like me you will have almost no more of an idea what the MVP pattern is supposed to do after that explanation. To generalize on that statement it’s basically taking the interaction of the UI and the application logic which usually is coupled together and separating it into 3 areas.
The model – this is the application logic which will be housed in controller classes.
The view – which is an interface which abstracts the UI away, all data sent and retrieved from the UI is done through this interface
The presenter – this is a mediator that negotiates how the UI and application logic interact. The view is also a composition of this class.
Starting with the view:
public interface IView
{
}
|
This is the most basic of the basic of interfaces, for my needs it’s purely a marker interface. If every single one of your pages shares a common property perhaps an event message collection this would be a good place to define a property for it.
The presenter class:
public abstract class Presenter<TView> where TView : IView
{
public TView View { get; set; }
public virtual void OnViewInitialized()
{
}
public virtual void OnViewLoaded()
{
}
}
|
The OnViewInitialized method is to represent any operations you would execute in Page_Load if(Page.IsPostback == false) so only on the initial page load. The OnViewLoaded method represents all page loads whether or not they are post backs. Currently I have no global page operations since I have no global view properties. This class is also constrained that each TView for a presenter is also an IView, this will be important later.
Next creating a view:
public interface IEmployeeView: IView
{
IList<Employee> Employees { get; set; }
IList<Employee> EmployeesListDatasource { set; }
}
|
As you can see my view will have 2 properties one for getting or setting a collection and another for setting only the same type. The view also implements the marker interface IView.
Creating the presenter:
public class EmployeePresenter : Presenter<IEmployeeView>
{
private readonly IEmployeeController _controller;
public EmployeePresenter(IEmployeeController controller)
{
_controller = controller;
}
public override void OnViewInitialized()
{
View.Employees = _controller.GetEmployees();
}
public override void OnViewLoaded()
{
View.EmployeesListDatasource = View.Employees;
}
}
|
I’m not going to go into the details of the IEmployeeController this is the “model” where actual application code is ran there is nothing special about this class or it’s interface and only contains logic specific to getting my set of employees. As you can see the EmployeePresenter implements Presenter. I overrided both of the methods provided by the Presenter base where the Initialize method gets list from the model and the loaded method binds the list to a datasource.
Now before I move onto implementing the IEmployeeView inside a page I’m going to create an abstract base page that will handle the dependency injection build up for my MVP framework.
public abstract class ViewBasePage<TPresenter, TView> : Page
where TPresenter : Presenter<TView>
where TView : IView
{
protected TPresenter _presenter;
public TPresenter Presenter
{
set
{
_presenter = value;
_presenter.View = GetView();
}
}
/// <summary>
/// Gets the view. This will get the page during the ASP.NET
/// life cycle where the physical page inherits the view
/// </summary>
/// <returns></returns>
private static TView GetView()
{
return (TView) HttpContext.Current.Handler;
}
protected override void OnPreInit(EventArgs e)
{
ObjectFactory.BuildUp(this);
base.OnPreInit(e);
}
}
|
This class might look quite a bit strange, the majority of it is from the class definition it’s setup to only allow you to inherit it in a valid way such as: ViewBasePage<EmployeePresenter,IEmployeeView>. On the Presenter set method this is where the page actually becomes part of the composition of the Presenter (the description from Wikipedia said this).
The GetView method also probably looks strange what this is doing is getting the physical page that’s being instantiated by ASP.NET. The reason I’m doing this is it makes it easier to reference the page generically otherwise I would be required to do something smilar to
return (TView) (object) this
Which is borderline wrong since THIS technically refers to the ViewBasePage which does not implement the interface that TView consists of but at runtime eventually resolves correctly since a Page will implement the interface that makes up TView and not cause a runtime error.
This class overrides Page’s OnPreInit method to call ObjectFactory.BuildUp() which will cause all the dependency resolution to take place. In my specific usage of StructureMap I have it identify all Presenter properties as targets for dependency injection, in your case you may be required to use the [Setter] attribute from SturctureMap or the [Dependency] attribute from Unity or some other similar convention in other inversion of control frameworks.
Now finally onto the actual page.
public partial class _Default :
ViewBasePage<EmployeePresenter, IEmployeeView>, IEmployeeView
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
_presenter.OnViewInitialized();
}
_presenter.OnViewLoaded();
Page.DataBind();
}
#region Implementation of IEmployeeView
IList<Employee> IEmployeeView.Employees
{
get { return ViewState["IEmployeeView.Employees"] as IList<Employee>; }
set { ViewState["IEmployeeView.Employees"] = value; }
}
IList<Employee> IEmployeeView.EmployeesListDatasource
{
set { lstEmployees.DataSource = value; }
}
#endregion
}
|
This class inherits the ViewBasePage class and implements the IEmployeeView. The _presenter field is in the ViewBasePage and contains the fully resolved dependency to EmployeePresenter. As I stated in the portion of the presenter code you can see where OnViewInitialized() and OnViewLoaded() are called inside the page life cycle.
Implementing the IEmployeeView you have the properties target actual controls on the page or the ViewState / QueryString etc that is available to the page. Now if you remember the code in my actual EmployeePresenter it should make sense why in ViewInitialized the Employees property is set by the controller and on the ViewLoaded the Employees property is assigned to the EmployeesListDatasource since it’s just refreshing the data source to the view state collection on subsequent page post backs.
BloggingContext.ApplicationInstance.CompleteRequest();