Model View Presenter with ASP.NET

This article describes using the Model-View-Presenter pattern within ASP.NET 2.0 to encourage proper separation of concerns between presentation and business logic

Author's note added October 15, 2007 - Suggestion of MVC over MVP

As we progress as developers, we strive to seek out the "best" way to perform our craft. The chosen methods to attain this lofty goal always bring with them a number of developmental trade-offs. Some techniques may simplify the code but lessen fine grained control while others enable greater power while introducing complexity. Model-View-Presenter with ASP.NET is a wonderful example of the latter. MVP aims to facilitate test-driven development of ASP.NET applications with the unfortunate introduction of added complexity. So while developers will be more confient in the quality of their product, MVP hinders their ability to easily maintain code. Yes, the tests will (hopefully) inform the developer that a bug has been introduced, but the inherent complexity of MVP makes it difficult for later team members to become comfortable with the code base and maintain it as project development continues.
Fortunately, as time progresses quickly in our field, resources and tools become available which enhance our ability to write powerful applications while simplifying the coding process itself. The introduction of NHibernate, for example, eliminated vast amounts of data-access code while still providing powerful facilities for managing transactions and dynamically querying data. Castle MonoRail (and Microsoft's upcoming spin-off of this framework) now does for writing testable and maintainable .NET web applications what NHibernate (and the upcoming LINQ to Entities) did for ADO.NET. This is not to say that the previous techniques were necessarily wrong, but that they were only applicable considering the developer's toolset that was available at the time of selection.
In adapting to the evolution of our field, it is important for developers to note when an accepted technique is no longer valuable in light of current alternatives. Specifically, MVP was a very powerful technique for writing ground-up, test-driven ASP.NET applications but is no longer a strong candidate for consideration when compared to the time saving benefits and simplicity of Castle MonoRail and Microsoft's upcoming MVC framework. Oddly, it is sometimes difficult to "give up" on something that worked perfectly fine before, but that's the nature of our business ... one tenet that's not likely to change anytime soon.
Of this article, I believe it to be of continued value to those maintaining legacy applications built upon MVP and for those interested in learning a solid domain driven architecture which is discussed further below and in detail in another post.
In summary, although I still believe that MVP is the best technique for developing ground-up ASP.NET solutions, I believe that there are off-the-shelf frameworks which make the entire job a heck of a lot simpler.

Introduction

After years of maintaining thousands of lines of ASP spaghetti code, Microsoft has finally given us a first class web development platform: ASP.NET. ASP.NET instantly brought a basic separation of concerns between presentation and business logic by introducing the code-behind page. Although introduced with good intentions and perfect for basic applications, the code-behind still lacks in a number of aspects when developing enterprise web applications:
  • The code-behind invites melding the layers of presentation, business logic and data-access code. This occurs because the code-behind page often serves the role of an event handler, a workflow controller, a mediator between presentation and business rules, and a mediator between presentation and data-access code. Giving the code-behind this many responsibilities often leads to unmanageable code. In an enterprise application, a principle of good design is to maintain proper separation of concerns among the tiers and to keep the code-behind as clean as possible. With Model-View-Presenter, we'll see that the code-behind is greatly simplified and kept strictly to managing presentation details.
  • Another drawback to the code-behind model is that it is difficult to reuse presentation logic between code-behind pages without enlisting helper/utility classes that consolidate the duplicated code. Obviously, there are times that this provides an adequate solution. However, it often leads to incohesive classes that act more like ASP includes than first class objects. With proper design, every class should be cohesive and have a clear purpose. A class named ContainsDuplicatePresentationCodeBetweenThisAndThat.cs usually doesn't qualify.
  • Finally, it becomes nearly prohibitive to properly unit test code-behind pages as they are inseparably bound to the presentation. Options such as NUnitAsp may be used, but they are time consuming to implement and difficult to maintain. They also slow down unit-test performance considerably, where unit tests should always be blazingly fast.
Various techniques may be employed to promote a better separation of concerns from the code-behind pages. For example, the Castle MonoRail project attempts to emulate some of the benefits of Ruby-On-Rails but abandons the ASP.NET event model in the process. Maverick.NET is a framework that optionally supports the ASP.NET event model but leaves the code-behind as the controller in the process. Ideally, a solution should be employed that leverages the ASP.NET event model while still allowing the code-behind to be as simple as possible. The Model-View-Presenter pattern does just that without relying on a third party framework to facilitate this goal.

Model-View-Presenter

Model-View-Presenter (MVP) is a variation of the Model-View-Controller (MVC) pattern but specifically geared towards a page event model such as ASP.NET. For a bit of history, MVP was originally used as the framework of choice behind Dolphin Smalltalk. The primary differentiator of MVP is that the Presenter implements an Observer design of MVC but the basic ideas of MVC remain the same: the model stores the data, the view shows a representation of the model, and the presenter coordinates communications between the layers. MVP implements an Observer approach with respect to the fact that the Presenter interprets events and performs logic necessary to map those events to the proper commands to manipulate the model. For more reading on MVC vs. MVP, take a look at Darron Schall's concise entry on the subject. What follows is a detailed examination of MVP in the form of three example projects.
Author's note: Martin Fowler has suggested that MVP be split between two "new" patterns called Supervising Controller and Passive View. Go here for a very short synopsis of the split. The content described herein is more consistent with Supervising Controller as the View is aware of the Model.

A most trivial example

In this example project, the client wants a page that shows the current time. Thank goodness they started us off with something easy! The ASPX page that will show the time is the "View." The "Presenter" is responsible for determining the current time -- i.e. the "Model" -- and giving the Model to the View. As always, we start with a unit test:
Collapse
[TestFixture]
public class CurrentTimePresenterTests
{
[Test]
public void TestInitView()
{
MockCurrentTimeView view = new MockCurrentTimeView();
CurrentTimePresenter presenter = new CurrentTimePresenter(view);
presenter.InitView();

Assert.IsTrue(view.CurrentTime > DateTime.MinValue);
}

private class MockCurrentTimeView : ICurrentTimeView
{
public DateTime CurrentTime
{
set { currentTime = value; }

// This getter won't be required by ICurrentTimeView,
// but it allows us to unit test its value.
get { return currentTime; }
}

private DateTime currentTime = DateTime.MinValue;
}
}
MVP Diagram
The above unit test, along with the diagram, describes the elements of the MVP relationship. The very first line creates an instance of MockCurrentTimeView. As seen with this unit test, all of the Presenter logic can be unit tested without having an ASPX page, i.e. the View. All that is needed is an object that implements the View interface; accordingly, a mock view is created that stands in place of the "real" view.
The next line creates an instance of the Presenter, passing an object that implements ICurrentTimeView via its constructor. In this way, the Presenter can now manipulate the View. As seen in the diagram, the Presenter only talks to a View interface. It does not work with a concrete implementation directly. This allows multiple Views, implementing the same View interface, to be used by the same Presenter.
Finally, the Presenter is asked to InitView(). This method will get the current time and pass it to the View via a public property exposed by ICurrentTimeView. A unit-test assertion is then made that the CurrentTime on the view should now be greater than its initial value. A more detailed assertion could certainly be made if needed.
All that needs to be done now is to get the unit test to compile and pass!

ICurrentTimeView.cs: the View interface

As a first step towards getting the unit test to compile, ICurrentTimeView.cs should be created. This View interface will provide the conduit of communication between the Presenter and the View. In the situation at hand, the View interface needs to expose a public property that the Presenter can use to pass the current time, the Model, to the View.
Collapse
public interface ICurrentTimeView 
{
DateTime CurrentTime { set; }
}
The View only needs a setter for the current time since it just needs to show the Model, but providing a getter allows CurrentTime to be checked within the unit test. So instead of adding a getter to the interface, it can be added to MockCurrentTimeView and need not be defined in the interface at all. In this way, the exposed properties of the View can be unit tested without forcing extraneous setters/getters to be defined in the View Interface. The described unit test above shows this technique.

CurrentTimePresenter.cs: the Presenter

The presenter will handle the logic of communicating with the Model and passing Model values to the View. The Presenter, needed to make the unit test compile and pass, is as follows.
Collapse
public class CurrentTimePresenter 
{
public CurrentTimePresenter(ICurrentTimeView view)
{
if (view == null)
throw new ArgumentNullException("view may not be null");

this.view = view;
}

public void InitView()
{
view.CurrentTime = DateTime.Now;
}

private ICurrentTimeView view;
}
Once the above items have been developed -- the unit test, the mock view, the view and the presenter -- the unit test will now compile and pass successfully. The next step is creating an ASPX page to act as the real View. As a quick side, take note of the ArgumentNullException check. This is a technique known as "Design by Contract." Putting checks like this all over your code will greatly cut down on tracking down bugs. For more information about Design by Contract, see this article and this article.

ShowMeTheTime.aspx: the View

The actual View needs to do the following:
  1. The ASPX page needs to provide a means for displaying the current time. As shown below, a simple label will be used for display.
  2. The code-behind must implement ICurrentTimeView.
  3. The code-behind needs to create the Presenter, passing itself to the Presenter's constructor.
  4. After creating the Presenter, InitView() needs to be called to complete the MVP cycle.

The ASPX page

Collapse
...
<asp:Label id="lblCurrentTime" runat="server" />
...

The ASPX code-behind page

Collapse
public partial class ShowMeTheTime : Page, ICurrentTimeView
{
protected void Page_Load(object sender, EventArgs e)
{
CurrentTimePresenter presenter = new CurrentTimePresenter(this);
presenter.InitView();
}

public DateTime CurrentTime
{
set { lblCurrentTime.Text = value.ToString(); }
}
}

Is that it?

In a word, yes. But there is much more to the story! A drawback to the above example is that MVP seems like a lot of work for such little gain. We've gone from having one ASPX page to having a Presenter class, a View interface and a unit testing class. The gain has been the ability to unit test the Presenter, i.e. the ability to conveniently unit test code that would normally be found in the code-behind page. As is the case with trivial examples, the advantages of MVP shine when developing and maintaining enterprise web applications, not when writing "hello world"-like samples. The following topics elaborate the usage of MVP within an enterprise, ASP.NET application.

MVP within enterprise ASP.NET applications

I. Encapsulating Views with user controls

In the previous, simple example, the ASPX page itself acted as the View. Treating the ASPX in this way was sufficient in that the page had only one simple purpose - to show the current time. But in more representative projects, it is often the case that a single page will have one or more sections of functionality whether they be WebParts, user controls, etc. In these more typical of enterprise applications, it is important to keep functionality logically separated and to make it easy to move/replicate functionality from one area to another. With MVP, user controls can be used to encapsulate Views while the ASPX pages act as "View Initializers" and page redirectors. Extending the previous example, we need only modify the ASPX page to implement the change. This is another benefit of MVP; many changes can be made to the View layer without having to modify the Presenter and Model layers.

ShowMeTheTime.aspx redux: the View initializer

With this new approach, using user controls as the view, ShowMeTheTime.aspx is now responsible for the following:
  1. The ASPX page needs to declare the user control which will implement ICurrentTimeView.
  2. The ASPX code-behind needs to create the Presenter, passing the user control to the Presenter's constructor.
  3. After giving the View to the Presenter, the ASPX needs to call InitView() to complete the MVP cycle.

The ASPX page

Collapse
...
<%@ Register TagPrefix="mvpProject"
TagName
="CurrentTimeView" Src="./Views/CurrentTimeView.ascx" %>

<mvpProject:CurrentTimeView id="currentTimeView" runat="server" />
...

The ASPX code-behind page

Collapse
public partial class ShowMeTheTime : Page  
// No longer implements ICurrentTimeView
{
protected void Page_Load(object sender, EventArgs e)
{
InitCurrentTimeView();
}

private void InitCurrentTimeView()
{
CurrentTimePresenter presenter =
new CurrentTimePresenter(currentTimeView);
presenter.InitView();
}
}

CurrentTimeView.ascx: the user control-as-view

The user control now represents the bare-bones View. It is as "dumb" as it can be - which is exactly how we want a View to be.
The ASCX page
Collapse
...
<asp:Label id="lblCurrentTime" runat="server" />
...

The ASCX code-behind page

Collapse
public partial class Views_CurrentTimeView : UserControl, ICurrentTimeView
{
public DateTime CurrentTime
{
set { lblCurrentTime.Text = value.ToString(); }
}
}

Pros and cons of the user Control-as-View approach

Obviously, the primary drawback to the User Control-as-View approach to MVP is that it adds yet another piece to the equation. The entire MVP relationship is now made up of: unit test, presenter, view interface, view implementation (the user control) and the view initializer (the ASPX page). Adding this additional layer of indirection adds to the overall complexity of the design. The benefits of the user control-as-View approach include:
  • The View can be easily moved from one ASPX page to another. This happens regularly in a mid to large sized web application.
  • The View can be easily reused by different ASPX pages without duplicating much code at all.
  • The View can be initialized differently by different ASPX pages. For example, a user control could be written that displays a listing of projects. From the reporting section of the site, the user may view and filter all the projects available. From another section of the site, the user may only view a subset of the projects and not have the ability to run filters. In implementation, the same View can be passed to the same presenter, but then each ASPX page, in its respective section of the site, would call a different method on the Presenter to initialize the View in a unique way.
  • Additional Views can be added to the ASPX page without adding much additional, coding overhead. Simply include the new user control-as-view into the ASPX page and link it to its Presenter in the code-behind. Placing multiple sections of functionality within the same ASPX page, without using user controls, quickly creates a maintenance headache.

II. Event handling with MVP

The previous example described, essentially, a one-way round of communications between a Presenter and its View. The Presenter communicated with the Model and delivered it to the View. In most situations, events occur which need to be handed off to the Presenter for action. Furthermore, some events depend on whether or not a form was valid and whether or not IsPostBack had occurred. For example, there are some actions, such as data-binding, that may not be done when IsPostBack.
Disclaimer: Page.IsPostBack and Page.IsValid are web specific keywords. Therefore the following may make the presenter layer, as described, slightly invalid in non-web environments. However, with minor modifications it will work fine for WebForms, WinForms or mobile applications. In any case, the theory is the same but I welcome suggestions for making the presenter layer transferable to any .NET environment.

A simple event handling sequence

Continuing with the earlier example, assume that requirements now dictate that the user may enter a number of days to be added to the current time. The time shown in the View should then be updated to show the current time plus the number of days supplied by the user, assuming the user provided valid inputs. When not IsPostBack, the current time should be displayed. When IsPostBack, the Presenter should respond to the event accordingly. The sequence diagram below shows what occurs upon the user's initial request (top half of diagram) and what happens when the user clicks the "Add Days" button (bottom half of diagram). A more thorough review of the sequence follows the diagram.
Screenshot - BasicEventHandling.GIF

A) User Control-as-View created

This step simply represents the inline user control declaration found in the ASPX page. During page initialization, the user control gets created. It's included on the diagram to emphasize the fact that the user control implements ICurrentTimeView. During Page_Load, the ASPX code-behind then creates an instance of the Presenter, passing the User Control-as-View via its constructor. So far, everything looks identical to what was described in the section "Encapsulating Views with User Controls."

B) Presenter attached to View

In order for an event to be passed from the user control, the View, to the Presenter, it must have a reference to an instance of CurrentTimePresenter. To do this, the View Initializer, ShowMeTheTime.aspx, passes the Presenter to the View for later use. Contrary to initial reaction, this does not cause a bi-directional dependency between the Presenter and the View. Instead, the Presenter depends on the View interface and the View implementation depends on the Presenter to pass events off to. To see how it all works, let's take a step back to look at how all the pieces are now implemented.

ICurrentTimeView.cs: the View interface

Collapse
public interface ICurrentTimeView 
{
DateTime CurrentTime { set; }
string Message { set; }
void AttachPresenter(CurrentTimePresenter presenter);
}

CurrentTimePresenter.cs: the Presenter

Collapse
public class CurrentTimePresenter 
{
public CurrentTimePresenter(ICurrentTimeView view)
{
if (view == null)
throw new ArgumentNullException("view may not be null");

this.view = view;
}

public void InitView(bool isPostBack)
{
if (! isPostBack)
{
view.CurrentTime = DateTime.Now;
}
}

public void AddDays(string daysUnparsed, bool isPageValid)
{
if (isPageValid)
{
view.CurrentTime =
DateTime.Now.AddDays(double.Parse(daysUnparsed));
}
else
{
view.Message = "Bad inputs...no updated date for you!";
}
}

private ICurrentTimeView view;
}

CurrentTimeView.ascx: the View

The ASCX page

Collapse
...
<asp:Label id="lblMessage" runat="server" /><br />
<asp:Label id="lblCurrentTime" runat="server" /><br />
<br />
<asp:TextBox id="txtNumberOfDays" runat="server" />
<asp:RequiredFieldValidator ControlToValidate="txtNumberOfDays" runat="server"
ErrorMessage="Number of days is required" ValidationGroup="AddDays" />
<asp:CompareValidator
ControlToValidate="txtNumberOfDays" runat="server"
Operator="DataTypeCheck" Type="Double" ValidationGroup="AddDays"
ErrorMessage="Number of days must be numeric" /><br />
<br />
<asp:Button id="btnAddDays" Text="Add Days" runat="server"
OnClick="btnAddDays_OnClick" ValidationGroup="AddDays" />
...

The ASCX code-behind page

Collapse
public partial class Views_CurrentTimeView : UserControl, ICurrentTimeView 
{
public void AttachPresenter(CurrentTimePresenter presenter)
{
if (presenter == null)
throw new ArgumentNullException("presenter may not be null");

this.presenter = presenter;
}

public string Message
{
set { lblMessage.Text = value; }
}

public DateTime CurrentTime
{
set { lblCurrentTime.Text = value.ToString(); }
}

protected void btnAddDays_OnClick(object sender, EventArgs e)
{
if (presenter == null)
throw new FieldAccessException("presenter has" +
" not yet been initialized");

presenter.AddDays(txtNumberOfDays.Text, Page.IsValid);
}

private CurrentTimePresenter presenter;
}

ShowMeTheTime.aspx: the View initializer

The ASPX page

Collapse
...
<%@ Register TagPrefix="mvpProject"
TagName
="CurrentTimeView" Src="./Views/CurrentTimeView.ascx" %>

<mvpProject:CurrentTimeView id="currentTimeView" runat="server" />
...

The ASPX code-behind page

Collapse
public partial class ShowMeTheTime : Page  
// No longer implements ICurrentTimeView
{
protected void Page_Load(object sender, EventArgs e)
{
InitCurrentTimeView();
}

private void InitCurrentTimeView()
{
CurrentTimePresenter presenter =
new CurrentTimePresenter(currentTimeView);
currentTimeView.AttachPresenter(presenter);
presenter.InitView(Page.IsPostBack);
}
}

C) Presenter InitView

As defined in the requirements, the Presenter should only show the current time if not IsPostBack. The important action to note is that the Presenter should decide what to do according to IsPostBack. It should not be the job of the ASPX code-behind to make this decision. As seen in the code above, the ASPX code-behind does no check for IsPostBack. It simply passes the value to the Presenter to determine what action to take.
This may lead to the question, "But what happens if another user control-as-view caused the post-back to occur?" In the scenario at hand, the current time would remain in the view state of the label and be displayed again after post back. This may be OK depending on the needs of the client. In general, it's a good question to ask of any Presenter: what impact will a post-back from another user control have on the View? In fact, it's a good question to ask even if you're not using MVP. There may be actions that should always occur, regardless of IsPostBack, while other initialization steps may be bypassed. View state settings obviously have a large impact on this decision, as well.
When not IsPostBack, as shown in the diagram, the Presenter then sets the CurrentTime of the view via its interface. Sequence diagram purists may raise the point that the diagram implies two messages are being sent -- one from CurrentTimePresenter to ICurrentTimeView and then one from ICurrentTimeView to CurrentTimeView.ascx -- when in fact only one is being sent from CurrentTimePresenter to CurrentTimeView.ascx, polymorphically. The interface "middleman" is included to emphasize that the Presenter does not depend on the concrete View directly.

D) Presenter InitView after IsPostBack

In the preceding steps, the user made the HTTP request, the Presenter set the current time on the View, and the HTTP response was delivered to the user. Now, the user clicks the "Add Days" button, which causes a post-back. Everything occurs as before until InitView is called on the Presenter. At this point, the Presenter tests for IsPostBack and does not set the CurrentTime on the View.

E) Button click handled by user control

After the Page_Load of the ASPX page has occurred, the OnClick event is then raised to the user control. The View should not handle the event itself; it should immediately pass the event on to the Presenter for action. By looking at the code-behind of the user control, you can see that it makes sure it has been given a valid presenter -- more "Design by Contract" -- and then hands the command off to the Presenter. The Presenter then verifies that the page was valid and sets the time or error message accordingly.
The above has been an exhaustive analysis of a complete MVP cycle with event handling. Once you get the hang of MVP, it takes very little time to get all the pieces in place. Remember to always begin with a unit test and let the unit tests drive the development. The unit tests not only help ensure that the MVP pieces are working correctly, they also serve as the point for defining the communications protocol among the pieces. A Visual Studio code snippet for an MVP unit test can be found in Appendix B. We'll now take a look at look at handling page redirection.

III. Page redirects with MVP & PageMethods

In developing enterprise application, application flow is always a concern. Who's going to take care of page redirects? Should action redirects be stored in a configurable XML file? Should a third party tool such as Maverick.NET or Spring.NET handle page flow? Personally, I like to keep the page redirects as close to the action as possible. In other words, I feel that storing action/redirects in an external XML file leads to further indirection that can be tedious to understand and maintain. As if we don't have enough to worry about already! On the other hand, hard-coded redirects in the ASPX code-behind are fragile, tedious to parse and not strongly typed. To solve this problem, the free download PageMethods allows you to have strongly typed redirects. So instead of writing Response.Redirect("../Project/ShowProjectSummary?projectId=" + projectId.ToString() + "&userId=" + userId.ToString()), PageMethods provides a strongly typed redirect that would look more like Response.Redirect(MyPageMethods.ShowProjectSummary.ShowSummaryFor(projectId, userId)). The redirect is strongly typed and, therefore, checked at compile time.
An MVP related question concerning page redirects remains: who should be responsible for making a redirect and how should the redirect be initiated? I believe there are a number of valid answers to this question but will propose a solution that I've found to be rather successful. Add one event to the Presenter for each outcome that is possible. For example, assume a website is made up of two pages. The first page lists a number of projects; the second page, reached by clicking "Edit" next to one of the project names, allows the user to update the project's name. After updating the project name, the user should be redirected to the project listing page again. To implement this, the Presenter should raise an event showing that the project name was successfully changed and then the View Initializer, the ASPX page, should execute the appropriate redirect. Note that the following is illustrative and not associated with the "current time" example discussed thus far.

Presenter

Collapse
...
public event EventHandler ProjectUpdated;

public void UpdateProjectNameWith(string newName)
{
...

if (everythingWentSuccessfully)
{
ProjectUpdated(this, null);
}
else
{
view.Message = "That name already exists. Please provide a new one!";
}
}
...

ASPX code-behind

Collapse
...
protected void Page_Load(object sender, EventArgs e)
{
EditProjectPresenter presenter =
new EditProjectPresenter(editProjectView);
presenter.ProjectUpdated += new EventHandler(HandleProjectUpdated);
presenter.InitView();
}

private void HandleProjectUpdated(object sender, EventArgs e)
{
Response.Redirect(
MyPageMethods.ShowProjectSummary.Show(projectId, userId));
}
...
Taking this approach keeps page redirection out of the Presenter and out of the View. As a rule of thumb, the Presenter should never require a reference to System.Web. Furthermore, disassociating redirects from the View -- i.e. the user control -- and allows the View to be used again by other View Initializers, i.e. other ASPX pages. At the same time, it leaves application flow up to each individual View Initializer. This is the greatest benefit of using an event based model of redirection with User Control-as-View MVP.

IV. Presentation security with MVP

Oftentimes, a column, button, table or whatever should be shown/hidden based on the permissions of the user viewing the website. Likewise, an item may be hidden when a View is included in one View Initializer vs. being included in different View Initializer. The security should be determined by the Presenter but the View should handle how that decision should be implemented. Picking up again with the "current time" example, assume that the client only wants the "Add Days" section to be available for users on even days, e.g. 2, 4, 6. The client likes to keep the users guessing! The View could encapsulate this area within a panel, as follows:
Collapse
...
<asp:Panel id="pnlAddDays" runat="server" visible="false">
<asp:TextBox id="txtNumberOfDays" runat="server" />
<asp:RequiredFieldValidator
ControlToValidate="txtNumberOfDays" runat="server"
ErrorMessage="Number of days is required" ValidationGroup="AddDays" />
<asp:CompareValidator ControlToValidate="txtNumberOfDays" runat="server"
Operator="DataTypeCheck" Type="Double" ValidationGroup="AddDays"
ErrorMessage="Number of days must be numeric" /><br />
<br />
<asp:Button id="btnAddDays" Text="Add Days" runat="server"
OnClick="btnAddDays_OnClick" ValidationGroup="AddDays" />
</asp:Panel>
...
Note that the panel's visibility is pessimistically set to false. Although it would not make much difference in this case, it is better to be pessimistic about showing secure elements than the other way around. The code-behind of the View would then expose a setter to show/hide the panel:
Collapse
...
public bool EnableAddDaysCapabilities
{
set { pnlAddDays.Visible = value; }
}
...
Note that the View does not expose the panel directly. This is intentionally done for two reasons: 1) exposing the panel directly would require that the Presenter have a reference to System.Web, something we want to avoid, and 2) exposing the panel ties the Presenter to an "implementation detail" of the View. The more a Presenter is tied to how a View is implemented, the less likely it will be reusable with other Views. As with other OOP scenarios, the pros and cons of exposing implementation details of the View need to be weighed against looser coupling to the Presenter.
Finally, during InitView, the Presenter checks if the user should be allowed to use the add-days functionality and sets the permission on the View accordingly:
Collapse
...
public void InitView()
{
view.EnableAddDaysCapabilities = (DateTime.Now.Day % 2 == 0);
}
...
This simple example can be extended to a varied number of scenarios including security checks. Note that this is not a replacement for built-in .NET security, but it serves to augment it for finer control.

V. Application architecture with MVP

Finally! How does all of this fit together in a data-driven, enterprise application? "Enterprise application," in this instance, is an application that has logically separated tiers including presentation, domain and data-access layers. The following graph shows an overview of a fully architected solution with discussion following.

Each raised box represents a distinct specialization of the application. Each gray box then represents a separate physical assembly, e.g. MyProject.Web.dll, MyProject.Presenters.dll, MyProject.Core.dll. The arrows represent dependencies. For example, the .Web assembly depends on the .Presenters and .Core assemblies. The assemblies avoid bi-directional dependency using the techniques Dependency Inversion and Dependency Injection. My preferred means of Dependency Injection -- "DI" in the above graph -- to the View Initializers is via the Castle Windsor project. The data layer then uses the ORM framework, NHibernate, for communicating with the database.
For a primer on Dependency Injection, read the CodeProject article entitled "Dependency Injection for Loose Coupling." Additionally, for a complete overview of this architecture, sans the .Presenters layer and Castle Windsor integration, read the CodeProject article entitled "NHibernate Best Practices with ASP.NET." This article also describes how to set up and run the sample application. Yes, these are both shameless plugs for other articles I have written, but both are required reading to fully appreciate the sample solution. Please feel free to raise any questions concerning the architecture.

In summary

At first glance, implementing MVP looks like a lot of extra work. In fact, it will slow development a bit during the initial stages of development. However, after using it in all stages of enterprise application development, the long-term benefits of using the approach far outweigh the initial feelings of discomfort with the pattern. MVP will greatly extend your ability to unit test and keep code more maintainable throughout the lifetime of the project, especially during the maintenance phase. When it comes right down to it, I'm not suggesting that you use MVP on all your enterprise ASP.NET projects, just the projects that you want to work! ;) In all seriousness, MVP is not appropriate in all situations. An application's architecture should fit the task at hand and complexity should not be added unless warranted. Obviously, MVP and User-Control-as-View MVP are just two architectural options among many. However, if used appropriately, MVP allows you to be confident in your presentation logic by making most of the code that would have been in a code-behind, testable and maintainable.

Appendix A: additional references

Appendix B: MVP unit test code-snippet for Visual Studio 2005

With a default VS 2005 install location, copy the following contents to "MVP Test Init.snippet" under "C:\Program Files\Microsoft Visual Studio 8\VC#\Snippets\1033\Visual C#".
Collapse
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets
xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>MVP Test Init</Title>
<Shortcut>mvpTestInit</Shortcut>
<Description>Code snippet for creating an initial
unit test for a new MVP setup.</Description>
<Author>Billy McCafferty</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>viewInterface</ID>
<ToolTip>Name of the view interface</ToolTip>
<Default>IView</Default>
</Literal>
<Literal>
<ID>presenter</ID>
<ToolTip>Name of the presenter class</ToolTip>
<Default>Presenter</Default>
</Literal>
<Literal>
<ID>mockView</ID>
<ToolTip>Name of the mock view
used in the unit test</ToolTip>
<Default>MockView</Default>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[ [Test]
public void TestInitView()
{
$viewInterface$ view = new $mockView$();
$presenter$ presenter = new $presenter$(view);
view.AttachPresenter(presenter);
presenter.InitView();
}

private class $mockView$ : $viewInterface$
{
public void AttachPresenter($presenter$ presenter)
{
}
}
$end$
]]>

</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>

History

  • 2006.07.02 - Initial posting
  • 2007.04.18 - Added lessons learned over the past year to top of article
  • 2007.07.10 - Article edited and moved to the main CodeProject.com article base
 ...............................................................................................................................................................

 Model View Presenter, is a network of objects where each collection co-ordinate within them among to achieve solution in such away that the application becomes very testable.

Here model is domain-specific representation of the data, View is nothing but the win form or Web forms and the Presenter are the components which take the user input from the user and communicate with the models via the interfaces.
• View contains the Presenter instance.
• Presenter is the only class knowing how to reach to model and retrieve the data needed for performing business logic.
• Presenter talks to the View through the view interface (abstracted representation of the View without UI specific attributes).
• View doesn't know nothing about the Model.


..................................................................................................................................................................




Building an MVP Framework for .NET. Part 1: The Basics of MVC and MVP

By Oleg Zhukov | 11 Feb 2008
This article starts a series addressing the development of an MVP Framework under the .NET platform.

Table of Contents

Introduction

Clear separation of responsibilities along with a low coupling level is a sign of a well-designed application. Whereas design patterns are proven solutions to reduce coupling between small sets of objects, architectural patterns help to improve a system's design on the whole. One popular architectural pattern is Model-View-Controller. It originally came from Smalltalk and now it has implementations in various languages. In Java for example such Frameworks as Spring and Struts have gained high popularity and are widely used. However in the .NET world, the existing implementations aren't much spread, being mainly inspired by Java implementations they fit well only for Web applications and are not suitable for Windows thick client applications.
Another architectural pattern that in fact is an evolution of MVC is Model-View-Presenter. The underlying ideas of MVP are very similar to those in MVC however MVP is designed to be more usable and understandable. MVP has much less implementations than MVC does, one known MVP implementation for .NET is the Microsoft User Interface Process application block (UIPAB). In spite of numerous problems, and hence low usability of UIPAB, its reasonable key concepts inspired us to create something new.
This article starts a series covering the development of a native Model-View-Presenter Framework for .NET platform. Although it will be an MVP Framework, we will still use the term "Controller" as it seems to be more pertinent than the "Presenter" notation (UIPAB follows the same manner describing itself as MVC, though in fact it is closer to MVP). Let us start with clarifying the basic concepts of an application's structure. After that we will proceed to the discussion of the existing architectural solutions (including MVC and MVP).

Basic Concepts

Every application can be split into a number of tasks. Here by task we mean a sequence of actions which accomplish a job, problem or assignment. There are different ways of breaking an application into tasks, for example, tasks may be associated with use cases. Each task involves interaction with a user done via so called interaction points. An interaction point is represented by one or more classes which serve the following needs:
  • Receive and process an input from a user
  • Make proper requests to the business objects
  • Request/modify the task's state
  • Send feedback to the user
The following figure illustrates the described relationships:
As we can see, the interaction point responsibilities are rather vast. That is why architectural solutions such as MVC make their aim to split interaction points into simpler parts. Next we will consider various ways of splitting the interaction point (including the MVC paradigm). Though the division of the interaction point into smaller elements is preferred, we will start with a case when an interaction point is represented by a single view class.

Interaction Point as a Single View

When the interaction point is made of a single view class it is the view which is responsible for all intermediate data processing. Such view class handles user input, makes proper calls to the business objects, analyses the data received from them, decides what to display to the user and actually displays it.
In order to demonstrate this and the other approaches, let us now introduce the example we will refer to throughout the article. Consider the encyclopedia application where a user enters some term and the system gives back the explanation for it. In most cases, the explanation is unambiguously found for a term. However sometimes several explanations of a term (especially if the term is an abbreviation) may apply and the system then asks the user which one to display.
With a single view approach, the sequence diagram for our encyclopedia application would look as follows:
This figure reveals the drawbacks of combining all intermediate logic into a view class. The view then becomes too bloated and difficult for understanding. Moreover such approach violates the single responsibility principle by uniting in the view two conceptually different responsibilities. First is making requests to the model and deciding what to display (application logic) and the second is actually displaying (presentation mechanism). As a result if we want to make our encyclopedia work both as a Windows and a Web application, we will have to duplicate the application logic in two view classes: one for Windows and the other for Web environment.

Model-View-Controller

We have seen the downsides of the solution when the interaction point is represented by a single view class. On the contrary to the single view technique MVC does break the interaction point into three parts. These parts are: Controller, View and Presentation model.
Controller handles user input, makes subsequent calls to the business objects and manages the application flow. In particular the controller decides what to display to the user. However it is not allowed in MVC to access the view directly, instead the underlying model should be altered and the changes will be propagated to the view through the observer mechanism. Thus in order to make the view update itself, the controller should change the presentation model object.
In our encyclopedia example, the controller class asks the model for the proper explanations (1.1 in the figure below) and passes the returned explanations to the presentation model (1.2). Depending on how many explanations were found (one or more) the controller sets a flag in the presentation model indicating whether the user should choose the desired variant (1.3). The view then reflects the changes in the presentation model (1.3.1) and either displays dialog with several variants (1.3.1.3) or displays the only found explanation (1.3.1.4).
The main advantage of MVC is a clear distribution of responsibilities between parties. The controller drives the application flow specifying what and when it should be done. The view only renders the underlying business and presentation models and presents them to the user. Since views in MVC do not contain any application logic, they can be harmlessly substituted, for example there may be different view classes for Windows and Web interfaces.
Nevertheless there are two major drawbacks in the traditional MVC approach. First is a higher complexity because of the observer mechanism: in order to update the view the controller must make changes to the presentation model, which will trigger the view update. Such indirect relationship may be difficult to understand. Instead the controller could simply send a message to the view, however such direct linking is not allowed in MVC.
The other shortcoming is that MVC does not conform to the modern UI programming environments where widgets themselves handle user gestures. For example form classes (either Web or Windows) in .NET applications by default contain handlers for user input. Thus it would be difficult to break the common paradigm and make controllers receive the user input instead of views.
The next architectural pattern we consider was designed to eliminate the drawbacks of MVC, while preserving its separation of application logic and presentation mechanism.

Model-View-Presenter

Model-View-Presenter approach appeared in the late 1990’s and was an evolution of MVC pattern. Above we have described two typical shortcomings of the MVC pattern. Now let us look at how MVP eliminates these two.
Firstly according to MVP, direct requests from the controller to the view become possible. Thus the controller itself may trigger the view updates instead of performing a round trip through the presentation model. In this sense the controller in MVP contains the presentation model from MVC. That is probably the reason why the controller in MVP is referred to as presenter (however we will continue naming it controller).
Here we must note that although the controller has a link to the view object it does not depend on the concrete view class. Instead the controller treats the view in an abstracted way by the means of a separated interface implemented by the view (see the figure above). For example, the encyclopedia controller will communicate the view via the IEncyclopediaView interface with chooseExplFrom(…) and displayExpl(…) operations. Since such separated interface is a part of the application logic, the dependency runs from the presentation to the application logic but not vice versa.
Next thing that makes MVP more convenient (in contrast to MVC) is that it allows views to receive user input. Such behavior fits well modern UI environments. For instance in Windows keyboard and mouse events are handled by UI controls and forms, in ASP.NET user requests are processed by Web page classes. Though the view in MVP receives the user input it should merely delegate all the processing to the controller.
The next figure demonstrates the interactions in MVP by the example of the encyclopedia application. As we can see direct calls from the controller to the view (via the IEncyclopediaView interface) make the overall picture less complicated than that in MVC. Yet the clear separation of responsibilities between the controller (application logic) and the view (presentation mechanism) still remains, in particular allowing the developer to easily support interchangeable views for Web and Windows environments.

Summary

Let us sum up what we have discussed so far. Among the architectural patterns we have considered, MVP seems to be the most attractive one. It allows building flexible systems without overheads peculiar to the classic MVC approach. That is why it is MVP that we are going to base our Framework on.
Proceed to Part 2: Implementing Core MVP Functionality
 ...........................................................................................................

0 comments:

Post a Comment