Introduction to MVP in ASP .NET

04.08.2010 13:23Comments
First we’ll remember a few things about the pattern Model View Presenter. The model represents your business rules, or business model. The View is the form that the user will be using, and the Presenter will be the connection between the Model and the View. To simplify things, we are going to say that the View’s responsibilities are two: to expose the data to the user and the presenter, and to notify of events to the presenter. The presenter will act upon this events based on the View state, and consume the model in order to update the state of the view. Let’s look at the first example, we need a page where I can see my account settings. We’ll need the UserName and the roles of our account. We’ll start by building our View:
<form id="form1" runat="server">
 <label>User name</label>
 <asp:Label runat="server" ID="lblUserName"></asp:Label>
 <br />
 <label>Roles</label>
 <ul>
  <%= RenderRoles() %>
 </ul>
</form>
Now we need a way to expose the UserName and Roles to the presenter. In this case we’ll create some properties in the code behind:
public string UserName
 {
 get { return lblUserName.Text; }
 set { lblUserName.Text = value; }
 }

 public string[] Roles
 {
 get { return (string[])ViewState["Roles"]; }
 set { ViewState["Roles"] = value; }
 }
This is what we want to expose to the presenter. So we’ll create an IAccountView like that looks like this:
 interface IAccountView
 {
  string UserName { get; set; }
  string[] Roles { get; set; }
 }
In order to render the Roles we’ll add the following method:
 protected string RenderRoles()
 {
  if (Roles != null)
 {
  var sb = new StringBuilder();
  foreach (var role in Roles)
   sb.AppendLine(string.Format("<li>{0}</li>", role));
  return sb.ToString();
 }
 else
  return string.Empty;
 }
Now we need a Presenter for our view. The only thing we need to notify to the presenter is that we are rendering for the first time, and we need to load the form. So we’ll need a presenter that conforms to this interface:
 interface IAccountPresenter
 {
  void OnInitialize();
 }
There’s a very good reason for naming the method OnInitialize and not Load, or Initialize. We’ll make a design choice here and whenever it’s possible (and convenient) we’ll start our presenter interface methods with “On” to illustrate that it’s an event. This will make the presenter code cleaner by dividing notification methods from other helper methods. So here is how the Page_Load might look like:
 IAccountPresenter presenter;
 protected void Page_Load(object sender, EventArgs e)
 {
  presenter = new AccountPresenter(this);
  if (!IsPostBack)
  {
   presenter.OnInitialize();
  }
 }
And that’s it for our View for now. Now let’s build this AccountPresenter. Notice that the Presenter depends on the View, and that’s why it receives the view in the constructor. As for the OnInitialize, that’s where the presenter has to go over to the model to get that information and manipulate the view in order to update the status of the View. Here’s our presenter:
 public class AccountPresenter : IAccountPresenter
 {
  IAccountView view;
  AccountModel model;
  public AccountPresenter(IAccountView account)
  {
   view = account;
   model = new AccountModel();
  }

  internal void OnInitialize()
  {
   view.UserName = model.GetUserName();
   view.Roles = model.GetUserRoles(view.UserName);
  }
 }
How the model retrieves the user name and the roles doesn’t matter, that’s your business rules. You might even have a return “Gustavo”; hardcoded in the GetUserName as far as I’m concerned ;) Up to here, we have the same MVP example you’ll find everywhere. The advantages so far are: that you have your model out of your code behind; you have your presenter and view loosely coupled so you could even mock or IoC these; and you divided the code and the classes responsibilities thus minimizing the classes complexity. But there are a lot of other reasons to use MVP. Let’s see how we can start building up this MVP example a little.

Base Interfaces and Base Classes

I usually consider it a good practice to have both base interfaces and base classes in my code. It adds extensibility and reusability points in the code. I think we could use some base interfaces in here.

IView

IView will contain whatever we want to expose to every presenter. The only thing that comes to my mind right now is the Title of the page, but we can get anything like stuff you would usually keep in the session or take from the MasterPage.
 public interface IView
 {
  string Title { get; set; }
 }

IPresenter

The view depends on the presenter, and from the view’s perspective the first thing we need from a presenter is to be able to set its view. So our IPresenter will have a method for doing this (here is our first exception to our “On” prefix rule):
 public interface IPresenter
 {
  void SetView(Views.IView view);
  void OnInitialize();
 }

BaseView

BaseView will be our base class for all our views. This class will implement IView, and will have some of the things we do in every page. Such as the Page_Load method. There’s a high chance that for every form we do using MVP we’ll want to create our presenter in the Page_Load, and set the View pretty much like we did in our AccountView. So let’s refactor our code so we don’t have to handle this in every codebehind.
 public abstract class BaseView<T> : Page, IView where T : IPresenter, new()
 {

  public virtual T Presenter { get; set; }
  protected void Page_Load(object sender, EventArgs e)
  {
   Presenter = new T();
   Presenter.SetView(this);
   if (!IsPostBack)
   {
    Initialize();
    Presenter.OnInitialize();
   }
  }
  public virtual void Initialize(){}
 }
We could easily get rid of the “new()” constraint over T if we had an IoC container, but in this case for the sake of simplicity we’ll omit that part.

BasePresenter

Our BasePresenter will implement the IPresenter and solve the SetView so we don’t have to take of this in every presenter.
 public class BasePresenter<T> : IPresenter where T : IView 
 {
  public T View { get; set; }
  public virtual void SetView(Views.IView view)
  {
   if (view == null)
   throw new ArgumentNullException("view");
   if (!view is T)
    throw new ArgumentException("view parameter must be of type: " + typeof(T).Name, "view");

   View = (T)view;
  }
  public virtual void OnInitialize(){}
 }

The result

Next we should inherit IAccountView from IView, IAccountPresenter from IPresenter, Account from BaseView and AccountPresenter from BasePresenter. And here’s how Account looks like:
 public partial class Account : BaseView<AccountPresenter>
 {

  public string UserName
  {
   get { return lblUserName.Text; }
   set { lblUserName.Text = value; }
  }

  public string[] Roles
  {
   get { return (string[])ViewState["Roles"]; }
   set { ViewState["Roles"] = value; }
  }

  #region render methods
  protected string RenderRoles()
  {
   if (Roles != null)
   {
    var sb = new StringBuilder();
    foreach (var role in Roles)
     sb.AppendLine(string.Format("<li>{0}</li>", role));
    return sb.ToString();
   }
   else
    return string.Empty;
  }
  #endregion

 }
As you can see, it only has the specific implementation of IAccountView, and no Page_Load method! Now let’s look at our AccountPresenter:
 public class AccountPresenter : BasePresenter<IAccountView>
 {
  AccountModel model;
  public AccountPresenter()
  {
   model = new AccountModel();
  }

  public override void OnInitialize()
  {
   View.UserName = model.GetUserName();
   View.Roles = model.GetUserRoles(View.UserName);
  }
 }
As you can see we can take care of our Model and our events only without worrying about the SetView plumbing. In this post we have showed how we could take advantage of MVP and base classes in order to create extensibility and reusability points for our web pages. As an example of this, we have centralized the Presenter instantiation and initialization.

comments powered by Disqus