Friday, February 4, 2011

Asp.net MVP URL Routing with WebForms. Part2 - The Hello World MVP


DARE TO SHARE?
In the previous post we've discussed the background of Asp.net MVP and how it evolved. In this post we would try to understand the basics of MVP via a "Hello World" MVP example in Asp.net. 

Just to recall, MVP stands for Model-View-Presenter, and, following is what they actually are:  

Model is the domain or business object which encapsulates data and behavior. Model knows nothing about the two other components View and Presenter.

View is the Aspx and CodeBehind which knows about the Models, and, which doesn't contain any application logic, except displaying data from Models.

Presenter is the glue between the Model and the View, with very loose coupling. It accepts user inputs from the View, determines which business logic to execute and finally sets model to the View so that View can do the rests. 

So, if we like to see a figure to demonstrate the idea, it would be something like the following:

Interactions between Model - View - Controller in MVP

Figure : Interactions between Model - View - Presenter in MVP
The picture is pretty self-explanatory, and, I am not describing the sequence of interactions between the different conceptual components in MVP. 

Perhaps the most important thing about MVP is, Model, View and Presenter all are VERY loosely coupled. The Presenter is usually a concrete implementation, but, it doesn't directly know about the concreteView and the Model, because, it simply interacts with these two via their interfaces. This allows to change the View and the Model if required without much effect. 

The "Hello World" Model View Presenter example  

Following are the steps required to develop a very basic MVP example. In this example, we would implement an Asp.net WebForm application which allows user to view a User's detailed information in the output (May be by selecting a User Name from a DropDownList in a page) 

Step1 : Define the Model 

As has been mentioned already, Presenter is very loosely coupled with the Model via Models Interfaces. So, an interface has to be defined for the Model. 

In our case, our Model is User. So, here goes the Interface:
public interface IUserModel
{
    User GetUserById(long Id);
}
And then, a concrete Model would implement this Model as follows:
public class UserModel
{
    public User GetUserById(long Id)
    {
        return db.GetUserById(Id);
    }
}

Step2 : Define the View  

Like the Model, Presenter is also loosely coupled with the View, and hence, there will be an interface for the View also. 

Now, an important question is, what this interface should contain? 

Ideally, a View shouldn't contain any functionality or business logic. A View should only capable to get Inputs from user and show Outputs. 

So, an Interface for a View should only contain properties for the Inputs and Outputs it needs to care about. 

In our case, the View needs to get the User ID from user input (It gets it somehow when user selects a user in the DropDownList), and, show user's detailed information using the User object. So, the Interface should only contain the following two properties:
public interface IUserView
{
    long UserId; //For Input
    User UserDetails; //For Output
}
Who will implement this View? The codebehind class ofcourse, right? 

Yes. In Asp.net WebForm application, either a Page or a User Control is a View element. So, their corresponding CodeBehind classes should implement the IUserView. 

In our case, we will have a simple aspx page to display user's detailed information. So, the CodeBehind class would be something like as follows:
public class UserDetails : System.Web.UI.Page, IUserView
{
    public long UserId
    {
        //Presenter will Get UserId via this property
        get
        {
            return Convert.ToInt64(ddlUser.SelectedItem.Value);
        }
    }

    public User UserDetails;
    {
        //Presenter will set User Object via this property
        set
        {
             PopulateUserInfo(this);
        }
    }

    //Page_Load and other methods
    protected void Page_Load(object sender, EventArgs e)
    {
    }

    protected void PopulateUserInfo(User userDetails)
    {
         //Display user info in GUI
    }
}
Note that, the UserId property has only the "get" method which returns the DropDownList's selected value (After converting to long). On the other hand, the UserDetails property has only the "set" method, which is used by the Presenter to set result and thus displays user detail via the PopulateUserInfo() method. 

Step3 : Define the Presenter  

Before jumping into creating the Presenter class, let us ask ourselves, what should the Presenter class needs to know about? 

Should the Presenter class have any property/method for setting Model(s)?  

From the figure above, it is obvious that, a Presenter should know about the Models, simply because it needs to execute business logic method on those. In our case, it should know about the IUserModel, but, in reality a Presenter may need to know about multiple Models to carry out a desired operation from user inputs. 

So, the Presenter should be able to access any number of Models (Well, it should access only the models it is logically related), and hence, it wouldn't be correct if the Presenter exposes some properties for setting Models (Because, it would limit the Models it can interact with). Rather, it should simply be able to instantiate any desired Model object and invoke business logic on it. 

Should the Presenter class have any property/method for setting View? 

Yes it should! Why? Simply because, the result has to be displayed via one single View component only, be it a User Control or an aspx page. If the Presenter knows about the View it needs to interact with, it can gather user input from it's corresponding property, and, set the resultant output to the corresponding property. 

So, there will be a Constructor inside the presenter class to accept a View. The Presenter will have a method to execute business method on the Model to get the result and set it to Views result property as follows:
public class UserPresenter
{
    IUserView view;
    public UserPresenter(IUserView view)
    {
        this.view = view;
    }

    public void ObtainUserDetails()
    {
        //Get input from User
        long userId = view.UserId;
        //Execute business method on Model to obtain result and set to View's property
        IUserModel model = new UserModel();
        view.User = model.GetUserById(userId );
    }
}

Step4 : Let the play begin!  

Once you carried out the above steps, you are ready for the show. In our case, we would ask the Presenter to carry out what it should do when user wants to see the detailed information of a User by selecting the target user's Name inside a DropDownList. Following is the modified CodeBehind code:

public class UserDetails : System.Web.UI.Page, IUserView
{
    public long UserId
    {
        get
        {
            //Presenter will Get UserId via this property
            return Convert.ToInt64(ddlUser.SelectedItem.Value);
        }
    }

    public User UserDetails;
    {
        set
        {
             //Presenter will set User Object via this property
             PopulateUserInfo(this);
        }
    }

    //Page_Load and other methods
    protected void Page_Load(object sender, EventArgs e)
    {
    }

    protected void PopulateUserInfo(User userDetails)
    {
         //Display user info in GUI
    }

    void ddlUser_SelectedIndexChanged(Object sender, EventArgs e)
    {
        //Instantiate Presenter object with setting this Page as it's view
        UserPresenter presenter = new UserPresenter(this);
        //Ask the Presenter to do what it should do
        presenter.ObtainUserDetails();
    }
}

That's it! You just learned the simplest Model View Presenter Pattern implementation in Asp.net WebForm.
 
In the next part of this article, we would learn the basics of URL Routing with Asp.net WebForm with some routing examples and other related things.

Stay tuned.

1 comments:

Anonymous said...

I am learning MVP for web. Could you upload a compete code for a simple MVP web project?
Thanks a lot.

Post a Comment