Click here to Skip to main content
15,881,089 members
Articles / Web Development / ASP.NET

Model View Presenter via ASP.NET

Rate me:
Please Sign up or sign in to vote.
4.38/5 (10 votes)
11 Oct 2009CPOL6 min read 39.3K   291   24   7
Following on from articles on adding generics support to ASP.NET, how to pass through arbitrary types to data sources, and implementing MVP in a type safe fashion, today Steve Gray takes readers through how to implement the various elements of a modular, re-usable MVP framework for ASP.NET.

Introduction

This article introduces a mechanism for implementing the Model View Presenter pattern in ASP.NET in a generic, declarative fashion. The resulting implementation does not require code-behind in the page to deal with the connection of the various elements (though some is still used in the View implementations). The article hereafter leverages several tutorials I've published here on The Code Project in past months:

As a result, this is something of an advanced implementation of Model View Presenter for ASP.NET, but once the initial support libraries are in place, is very lightweight to implement and support.

Why Model View Presenter for ASP .NET 'Felt Wrong'?

The more time-pressed implementations of Model View Presenter will take User Controls (The .ascx files) and use these template files as the basis for views, attaching view logic to the 'Code Behind' elements of the view. This, in and of itself, is not particularly worrisome. Time-allowing it's usually possible to wrap View implementations into actual ASP.NET Server Controls, allowing them to be toolboxed much more easily than User Controls, without the need to apply code-behind. This is a minor problem though, compared to some other issues.

Where a lot of abstraction and robustness falls away tends to be the code-behind on the pages themselves, which seem over-burdened with duties. The typical pattern seems to be:

Image 1

This leads us to a situation where the page is coded against concrete implementations of multiple aspects. It's not possible to switch the view, presenter or even control how the model gets passed to the presenter without having a developer fire up Visual Studio. It seems the page is acting as a fourth role within the paradigm, acting to weave the various elements together. This duty of care is typically referred to as Orchestration.

What Does an Orchestrator Do?

Taking our ASP.NET example. It seems that any component that would be built to Orchestrate the various associations in the Model View Presenter scenario would need to supply instances of each of the appropriate elements and connect them all together.

Image 2

The duties of this orchestrator would be:

  • Activate an instance of a type implementing the view contract.
  • Activate an instance of a type implementing the presenter contract
    • Optional: passing through any parameters (the model instance, for example)

An additional special duty for ASP.NET would be:

  • Insert the control at some pre-defined point on the page.

The reason for this requirement is that we may have multiple views to create against a single presenter. In that scenario, it's clear that inserting the controls at the location of the Orchestrator in the markup would not be a useful proposition.

So there we have it, the definition of what we're after. But how do we define such an entity? What would it look like? How can we make it work like the rest of the ASP.NET Framework?

Case Study: ObjectDataSource

A mainstay of ASP.NET WebForms development, the ObjectDataSource control allows for easy connection of methods to ASP.NET server controls, such as the Repeater. Let's look at some typical syntax for using this component:

ASP.NET
<%--  Define our data source --%>
<asp:ObjectDataSource ID="SomeDataSource" runat="server"
    TypeName="SomeNamespace.SomeType, SomeAssembly"
    SelectMethod="GetData">
    <SelectParameters>
        <asp:ControlParameter ControlID="SomeTextbox" />
    </SelectParameters>    
</asp:ObjectDataSource>
        
<%--  Define our target, linking ito the data source --%>
<asp:Repeater ID="RepeaterDisplay" runat="server" DataSourceID="SomeDataSource">
    <ItemTemplate>
        Item display template.
    </ItemTemplate>
</asp:Repeater>

With little/no work a page can be made to use some other sources for components. The underlying method needs only have the right definition, return the right types and you're there. This ability to declaratively wire-up components leads to some great gains:

  1. Designers can re-use a library of data sources with standard components and re-design the site entirely.
  2. The data-source itself could be a mock/dummy data source (often useful during design time, or when very early in the development cycle).
  3. The elements can be composed together on new pages without requiring any new coding be done (not possible if the wire-up was done in the page codebehind).

Keeping this in mind, it's time to turn back to our MVP orchestration issues.

An Orchestrator Control for ASP .NET

Having seen the way other ASP.NET wireup assistants work, it seems logical that any orchestrator component should be entirely declarative in style. There should be no code involved in connecting pieces together, and nothing that ties a specific group of elements to a specific page or location.

Using the Generics Framework I introduced here on Code Project a few months ago, I was able to produce an # Orchestrator component that is able to load up user-controls dynamically, creating a presenter, connecting the two and using a complex-parameter input, constructing the model to pass through to the presenter.

Image 3

In this model, the page itself knows nothing of the design pattern being used. There's no rewrite of ASP.NET's fundamentals (a-la the MVC Framework) and the page is more or less a simple, standard, familiar web-form. The Orchestrator component, during page initialization performs the following tasks:

  • Activate any dependencies of the presenter (The Model)
  • Activate the presenter, passing through the dependencies
  • Activate any views:
    • Create user control instances
    • Insert user controls within placeholders on the page
    • Attach the user control the presenter

This sequence of events occurs during the Init stage of the page-life cycle, allowing standard ASP.NET WebForms events to fire during the ProcessPostData/Raise Events stages without any further work. This gives the advantage of meaning that postbacks and other essentials work as-is, even though we're using Model-View-Presenter as our paradigm de-jour.

Revising the Model-View-Presenter Clock Example

The same project this time around is an extension of the previously mentioned MVP implementation of a clock with time-zone support. For reasons of practicality, the time is not pushed every second into the rendered page via AJAX, unlike the WinForms example code, which updates each second.

The implementation this time around involves a web application project, with a User Control that implements our view. All the other elements are dealt with by the exact same code that powers the WinForms application, same model, same presenters, literally just a different view. Once that happens, of course, to be an ASP.NET user control.

The syntax for the use of the Orchestrator is pretty straightforward and looks like:

ASP.NET
<form id="form1" runat="server">
    <div>
        <asp:PlaceHolder ID="ClockViewPlaceHolder" runat="server" />
    </div>
        
    <CMVP:Orchestrator ID="Orchestrator1" runat="server"
        ViewContract="ExampleProject.Contracts.IClockView, ExampleProject.Contracts"
        PresenterContract="ExampleProject.Contracts.IClockPresenter, 
			ExampleProject.Contracts">
        <Presenter>  
            <CMVP:SimplePresenterLoader TypeToActivate=
		"ExampleProject.Presenters.ClockPresenter, ExampleProject.Presenters">
                <ConstructorParameters>
                    <CMVP:ObjectParameter TypeName="ExampleProject.Model.TimeModel, 
							ExampleProject.Model" />
                </ConstructorParameters>
                <PropertyValues />
            </CMVP:SimplePresenterLoader>
        </Presenter>
        <Views>
            <CMVP:UserControlViewLoader src="mvp_for_asp/~/Views/ClockView.ascx" 
					PlaceHolderID="ClockViewPlaceHolder" />
        </Views>
    </CMVP:Orchestrator>
</form>

Notice that the mechanism by-which views and presenters are loaded is completely pluggable. This is achieved through implementation of the IViewLoader/IPresenterLoader interfaces. When you spin up the web application, you'll see a screen like this:

Image 4

Changing the selected time zone in the drop-down will refresh the time, and for the impatient there is a button that will cause the page to reload (bringing with it an updated view of the time!). Once the framework is in place, building new views, presenters and so forth is trivial.

Under the Hood: Generic Controls in ASP.NET

As per my article on ASP.NET Generics, the Orchestrator is subject to some trickery. When ASP.NET loads the page and starts preparing the various controls for display, the Orchestrator is swapped for an instance of OrchestratorImplementation with the correct view and presenter contracts specified as generic parameters, an implementation that leverages the coupled contracts support for bi-directional strong typing. This allows the bulk of the code to be strictly type-safe, as in the following snippet:

C#
// Create the presenter
TPresenterContract presenter = Presenter.GetPresenterInstance<tpresentercontract, />(this);

// Wireup each view.
foreach (IViewLoader viewLoader in Views)
{
    viewLoader.Connect<tviewcontract, />(this, presenter);
}

The only point at which any reflection trickery is actually used is the standard ASP.NET data-source parameters. Rewriting those for a tiny performance gain seemed fruitless, especially since the only 'reflection' usage in this case is to load the presenter instance and pass through its constructor parameters.

Finishing Thoughts

Today we've more or less introduced enough material to implement a declarative MVP framework for ASP.NET. Pages have been liberated of their duties and knowledge of the underlying types, strong typing of presenter/view operations behind the scenes is guaranteed.

Stay abstract!

Revision History

  • 11th October, 2009 - Initial draft

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Insurance Industry
United Kingdom United Kingdom
Steve Gray is a Senior Developer at a British insurance company, working on a popular aggregator. When he's not writing ASP .NET, it's because there's SQL or WCF to write instead.

Comments and Discussions

 
GeneralMy vote of 5 Pin
dp.sistemas17-Sep-13 16:44
dp.sistemas17-Sep-13 16:44 
QuestionAjax call in MVP Pin
Member 306141721-May-12 1:22
Member 306141721-May-12 1:22 
QuestionModel View Presenter with javascript Pin
Glenn197923-Oct-11 2:33
Glenn197923-Oct-11 2:33 
GeneralVery interesting solution Pin
oleg@rh13-Nov-09 0:54
oleg@rh13-Nov-09 0:54 
GeneralRe: Very interesting solution Pin
Steven James Gray16-Nov-09 10:24
Steven James Gray16-Nov-09 10:24 
GeneralFairly new to MVP Pin
Enriarg14-Oct-09 7:22
Enriarg14-Oct-09 7:22 
GeneralRe: Fairly new to MVP Pin
Steven James Gray14-Oct-09 18:04
Steven James Gray14-Oct-09 18:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.