Thoughts on MVP and WebForms

I got an email from a friend asking about MVP in WebForms and how the view should do databinding.  There was talk of using a FormView and various *DataSource objects, so I thought I’d do a brain-dump of MVP and Presentation Model.  I’ve had a few other people ask me about this recently, so I hope it benefits all of you.

Also, I’d like to credit Jeremy with most of my knowledge on this. NOTE: Don’t blame him for any inaccuracies or misunderstandings I may have, they are my own :)

So, here goes:

First, there’s a few points of clarification/context that you should know about my advice:

  1. It’s fairly well established that there is no good pattern for dealing with ASP.NET WebForms (i.e. <form runat=server>) that won’t break down after significant use.
    • If you’re writing internal/intranet line-of-business type apps and you can get away with lower quality and poor usability, WebForms are good enough because you can bang out apps pretty fast without as much emphasis on quality as you might need to have with a public-facing or shipping premise product  
    • The alternative would be ASP.NET MVC (all the good stuff about ASP.NET without the form runat=server), MonoRail, or even a home-grown MVC framework. Just about anything is better than WebForms at this point.
  2. Given #1, MVP is about the best you can do for yourself if you’re stuck in WebForms land, but keep in mind that it’s not a great solution and it’ll wear thin and cause problems at various points. 
    • … but less problems than you’d have if you DIDN’T use it
    • … assuming that you’re using MVP to afford you some testability also
  3. IMHO, views should NEVER hit a database, ever. Period. They shouldn’t even have a concept of a database, nor that the data comes from one.
    • Unless you’re building a strict forms-over-data app where there’s almost no logic and you need a dirt simple UI for putting stuff into a DB and editing stuff already in there
    • In which case, you should consider the ASP.NET Dynamic Data Services (Astoria) in .NET 3.5 and be done with the whole thing

Ok, now, on to the meat!

There are generally two major flavors of MVP: Supervising Controller and Passive View. In short:

  • Supervising Controller (aka Supervising Presenter) has the presenter doing very little, merely “supervising” but not actually doing a lot of legwork. In this case, the View and the various Services take care of the heavy lifting. The Controller/presenter just responds to events and shuffles things between the View and Services 
     
  • Passive View has the view doing almost nothing. The view is just a dumb data displayer and user-entered data-and-events catcher + dispatcher. The controller/presenter does all heavy lifting and grabs things from the view and drives services and such.

After looking at these, I hope you picked Passive View, because in my experience, it’s the only one that makes Web Forms manageable and testable to any sufficient level J

Jeremy Miller taught me, when using Passive View, another concept known as “Presentation Model.” It goes together with Passive View very effectively to achieve highly testable, loosely coupled, highly cohesive designs – even with Web Forms! Unfortunately testing the views is still a major pain, but at least the entire rest of the system is easily decoupled and tested in isolation.

When using “Presentation Model”, you essentially define a class which is a simple, anemic DTO-style class (that is, just a bunch of getters/setters and not a lot of functionality). It will usually have a few tricks it can do in the getters and setters (i.e. if the FooName value is null, then the XYZ Panel should be hidden, etc – more on this later). Your view makes the Presenter aware that something has happened (i.e. “Hey boss, the user clicked the ‘OK’ button”). The presenter will do whatever is necessary to respond to this event and then call a method back on the view to say “Here’s what you should do.” Let’s call this “Here’s what you should do” method the UpdateYourself(presModel) method. The presModel is our Presentation Model class which has EVERYTHING the view needs present itself, including the visibility of various controls, maybe the text of labels, the enabled/disabled-ness of buttons and fields, even the HTML Title of the page if necessary. The view knows only how to raise events and, when UpdateYourself is called, redraw itself to match the state of things. In essence, the Presenter tells the view: “Here is how you should look. Now make it happen.”

Your view interface might look like:

public interface IUserPreferencesView
{
    // first page, or maybe ever page_load, regardless of postback
    event EventHandler ViewInitialized; 
    event ResetPasswordHandler ResetPasswordClicked;
    event SavePreferencesHandler SavePreferencesClicked;
    event CancelHandler CancelClicked;
    void UpdateYourself( UserPreferencesPresentationModel model );
}

The event handlers would have only the information that the view gained from the user action (such as the current password, the new password, and the new-password-entered-twice-for-comparison values).

You could also make all these things properties on the IUserPreferencesView interface and just use basic EventHandlers, too. That’s OK, but it might clutter your interface a lot. Try it both ways and see how it works out for you.

Your presentation model class might look like:

public class UserPreferencesPresentationModel
{
    // maybe not all users are allowed to reset their password? These are contrived examples, bear with me
    public bool ResetPasswordAllowed{ get; set; }                    

    public string Username{ get; set; }
    public string EmailAddress{ get; set; }

    // Note that you can do some formatting (a form of logic) here instead of in the view
    public string CurrentDateFormatted{ get; set; }

    public bool ChangeManagerAllowed{ get; set; }
    public string ManagerName{ get; set; }
    public int ManagerID{ get; set; }    

    public bool UserWantsToBeNotifiedViewEmail { get; set; }

    // HTML, plain text, etc
    private EmailType _emailType = EmailType.Default;

    // Note that you can have some logic here
    public EmailType PreferredEmailType 
    {
        get
        { 
            return UserWantsToBeNotifiedViaEmail 
                    ? _emailType
                    :  EmailType.None;
        }
        set{ _emailType = value; }
    }

    // etc, etc, etc.
}

Now, the Controller is responsible for assembling all the data together to fill in all the properties (i.e. ManagerName which may come from the Manager table, Username which comes from the user table, etc).

Also, note that the PresentationModel can have some logic inside of itself. These are then easily testable via state-based testing, like so:

[TestFixture]
public class UserPreferencesPresentationModelTester
{
    [Test]
    public void  email_type_should_be_none_when_email_notify_preference_is_false()
    {
        var model = UserPreferencesPresentationModel
        {
            UserWantsToBeNotifiedViewEmail = false;
        };

        Assert.That( model.PreferredEmailType, Is.EqualTo( EmailType.None ) );
    }
}

Also, using the PresentationModel object allows your View to databind directly to these properties without having a lot of “this.that.something.or.another” databind properties. Binding the view is very simple, now.

// Meanwhile, in the view…
public void  UpdateYourself(UserPreferencesPresentationModel model )
{
    ResetPasswordTab.Visible = model. ResetPasswordAllowed;
    
    EmailNotifyCheckbox.Checked = model. UserWantsToBeNotifiedViewEmail;
    
    UsernameTextBox.Text = model.Username;

    // etc, etc, etc
}

Also, testing the views can get a lot simpler now because all you have to do is pass in a PresentationModel set up the way you expect and you can use something like WatiN to verify that the HTML comes out the way you expect.

Hope this helps.

Related Articles:

    Post Footer automatically generated by Add Post Footer Plugin for wordpress.

    About Chad Myers

    Chad Myers is the Director of Development for Dovetail Software, in Austin, TX, where he leads a premiere software team building complex enterprise software products. Chad is a .NET software developer specializing in enterprise software designs and architectures. He has over 12 years of software development experience and a proven track record of Agile, test-driven project leadership using both Microsoft and open source tools. He is a community leader who speaks at the Austin .NET User's Group, the ADNUG Code Camp, and participates in various development communities and open source projects.
    This entry was posted in ASP.NET, MVP. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
    • http://anydiem.com Sean Scally

      I’ve been using a passive view approach for webforms for the past half year or so and it’s been serving well; at least, better than stuffing everything into the view.

      The PresentationModel bit is new to me though, it will be interesting to see how it could improve some of the controllers I already have.

    • Stephen Smith

      What are the most appropriate patterns for implementing separation of concerns while considering data binding for WPF? Are they the same as for WinForms or are there influential characteristics of WPF?

    • http://chadmyers.lostechies.com Chad Myers

      We were doing WPF at the last place I worked and using a very similar pattern to what I described above (using PresentationModel). In fact, we were able to transition from WinForms to WPF in the View with hardly any change to our presenters or presentation model at all.

      WPF does have some good databinding support, but I’m not comfortable with exposing full domain objects to the view. It won’t be long before you start seeing logic creeping up in the view (i.e. “I need to format ThisProp this way for this user and that way for that user”, etc) and the temptation will be to just put it in the view because WPF might make it seem easy. Now you have opaque business logic hidden in the view which is much more difficult and laborious to test.

    • Gary Brunton

      Thanks for your input Chad. I’ve been using MVP for quite some time now and I think I’ve been slowly changing my style to use the PresentationModel although I hadn’t quite put a name on it or really described it as you have. I like it.

      My question is would you also have a method on the view called UpdateUserPreferencesPresentationModel(UserPreferencesPresentationModel model)? This method would populate values based on text boxes and other controls.

    • http://creedcultcode.blogspot.com Dale Smith

      Good stuff, Chad. I love the Passive View / Presentation Model flavor of MVP, but I don’t always start there when working with legacy code.

      We’re working with very brownfield stuff here, I’m talking dark-chocolate-85%-cacao-brown brownfield (I could have chosen a different metaphor for the color of our legacy code, but my mother taught me not to be so crass). In moving a 4000-line code-behind file over to MVP, I have been starting by just creating the structural pieces of MVP for the page, a simple view and presenter. Moving along from there I extract small methods from code-behind stuff and dump it into the presenter, where it can be called from the view as well as from a unit test suite. As time goes on, we drive toward a model that has the Presenter doing as much of the heavy lifting as possible and has the View becoming more and more naive.

      Are you aware of a less risky technique for addressing legacy ASP.NET pages? One that gets you to Passive View / Presentation Model more quickly than what I described?

    • http://chadmyers.lostechies.com Chad Myers

      Dale,

      I don’t know if there’s a way to do it faster, but you may want to start with tests and then refactor into the tests. Starting with refactoring and adding tests could produce more bugs.

      You might start by using TypeMock and Ivonna to get good coverage on your big, nasty codebehinds. Then start adding new tests for your code and refactoring into tests. Little by little you can either pull out your TypeMock tests, or leave them in, your choice.

      If your employers complain about having to spend money on TypeMock, you should let them know that to do it any other way would be irresponsible and introduce instability into the system which would cost far more than a few licenses of TypeMock.

    • http://creedcultcode.blogspot.com Dale Smith

      Do you mean 1) creating a suite of Fit tests to serve as a vice around the code behind? or 2) test-driving the presenter as we move code into it?

      If 1), good call, we definitely need to do that, and we don’t have that kind of coverage yet. We do have a pretty good set of regression tests from our QA team we can run on those pages, but they aren’t part of our CI process, and we can’t readily run them on our own machines as we are developing. Not trying to sweep this under the rug at all, this is definitely an issue we have to address.

      If 2), we’re already there. As we move code from code-behind into the presenter we create tests for the presenter code.

      Granted, we should be doing both if we truly intend to move beyond “Edit and Pray”.

    • http://chadmyers.lostechies.com Chad Myers

      @Dale: No, not Fit testing (though, that would be helpful). I’m talking the stranglehold approach (Feathers). Do not make any changes to the code until you have good unit test coverage over the original code. Once you have good coverage on the original/legacy code, start writing tests for the new code, then start making small, measured changes from the old code into the new code+tests.

      Changing existing legacy code (where ‘legacy code’ is ‘code with no tests’) is not a safe thing to do.

    • http://creedcultcode.blogspot.com Dale Smith

      Yep, I understand where you’re going. I’m totally on board for developer testing, including testing to detect changes. My problem is that I effectively don’t know how to unit test code that lives in 800-line methods in code-behind classes. I suppose this is where your suggestion of TypeMock fits in?

    • http://chadmyers.lostechies.com Chad Myers

      @Dale:

      Yes, TypeMock Isolator actually hooks into the profiling API of the CLR and can do all sorts of crazy scary things like when you say “new SomeClass()” you can actually have it return a mock for SomeClass.

      It can mock static classes, seal classes, etc (I think, don’t quote me on that, but it’s still pretty crazy what it can do).

      It’s perfect in this type of situation where you have a big ball of mud that is otherwise untestable.

    • http://creedcultcode.blogspot.com Dale Smith

      Excellent stuff, Chad. Very helpful. Thanks man.

    • http://igorbrejc.net Igor Brejc

      A bit late in this discussion, but just some points about the PresentationModel:
      1. How do you distinguish the exact data that is changed in the PresentationModel from the previous view states? (let’s say we used it for WinForms – do you have to refill all the view data and states every time you call UpdateYourself())?
      2. It’s harder to test when using mocked views – the expectation now becomes the whole PresentationModel object, not just a single property.

      I’ve been using MVP PassiveView for some time now, but without the PresentationModel – at least not the one covering the whole view in a single class. I find it’s easier to work with view interfaces which have properties and methods for individual view controls (input boxes etc). But then again, I’m not using the automatic data binding.

    • http://chadmyers.lostechies.com Chad Myers

      @Igor:

      (1): If you’re using WinForms, Can’t you use some of the various data binding options? Aside from that, I don’t think it’s too big of a deal to re-set the values on all the textboxes and such. You might have to be judicious about when lists or grids are updated in which case you could have a flag or something on the presentation model that lets the view know that there’s new data available and it should rebind. There are a few options here. No, you don’t HAVE to rebind everything all the time if that’s not appropriate. There are a few options: Having multiple UpdateYourself() methods that take smaller subsets of the presentation model (PM), Have the ability to compare the previous PM state to the new PM state and produce a difference set of changes.

      (2) The point of PM is to make it easier. The PM does not need to be mocked since it is purely state. State-based testing is much easier than mocking/interaction testing and it makes it easier to test the state coming out of the presenter as well as how the view reacts when to new state coming in.

    • http://igorbrejc.net Igor Brejc

      Chad, thanks for the answer. Regarding the state-based testing of presenters: can you please paste some short code snipped that demonstrates it?
      BTW when I referred to mocking, I wasn’t talking about mocking PM, but mocking the presenter’s view interface. PM is not mocked, of course, since it’s supposed to be a pure DTO class.

    • http://chadmyers.lostechies.com Chad Myers

      @Igor
      State-based testing is just regular, non-mocking testing. I.e.:

      Assert.IsTrue(_presentationModel.PasswordDisabled);

      or (using Scott Bellware’s SpecUnit.NET testing extension methods)

      _presentationModel.EditingAllowed.ShouldBeTrue();

      A full test might be like this:

      [Test]
      public void editing_should_be_allowed_after_entering_edit_mode()
      {
      var theView = MockRepository.CreateStub();
      var presenter = new SomePresenter(theView);

      presenter.EnterEditMode();

      presenter.Model.EditingAllowed.ShouldBeTrue();
      }

    • http://igorbrejc.net Igor Brejc

      Chad, thanks. So you’re basically just checking the state of PM all the time and not worry about the interaction with the view. Nice, maybe I’ll try it in the future :)

    • http://chadmyers.lostechies.com Chad Myers

      @Igor:

      You might also have a few integration tests between the Presenter and the View, but the idea of PM is to limit the interaction between the two. The view might just expose a bunch of properties (state) so you could just stub it and the view just calls methods on the presenter (Hey! this button was clicked!)

    • http://danlash.com Dan Lash

      Do you think you could extend the example code to show the page handling a post back (like changing the value of something that would have implications on another field). I think you were getting at it with the preferred email type and user wants to be notified, but controller and page code would help complete the picture.

      Also could you extend the code to show how the controller would be implemented in terms of business state. I’m curious about state with webforms and what you think a good way to load, maintain/calculate, and save the data. Is it in the controller, is in a business service, is in business entities, etc.

      I like the practical example so far!

    • shmotter

      Chad, thanks for the article!
      One question, how do you get data back from view? Do you have a method in the view
      void UpdatePresentationModel(UserPreferencesPresentationModel model)
      {
      model.Username = UsernameTextBox.Text;
      etc…
      }

      and call this method inside event handlers, like ResetPasswordClicked?

      Gary Brunton asked the same thing much earlier in the comments.

    • http://chadmyers.lostechies.com Chad Myers

      @shmotter:

      There are a few ways, but the one I’ve usually ended up using was to expose properties on the IView interface (i.e. Username{ get; set;}) that the Presenter can grab off its IView reference.

    • shmotter

      @Chad: Username{ get; set;}

      Do you really expose a setter for View’s fields like UserName? From the article I understood that view updates itself in UpdateYourself(UserPreferencesPresentationModel model) method. Keeping in mind that PresentationModal could have some logic, exposing a setter for View’s element could bypass this [ tested ] logic.

    • http://chadmyers.lostechies.com Chad Myers

      @shmotter: Ah, yes, good catch. It would indeed be

      interface ISomeView : IView
      Username{ get; }

      -c