Thursday, January 17, 2008

MVC (Model View Controller) For ASP.NET

MVC (Model View Controller) For ASP.NET

Historical Background

For those unfamiliar with MVC, MVC stands for Model, View, Controller. The Model is the representation of the thing the user is trying to interact with. It typically holds the data and business rules.

The View pulls information from the Model and renders that information in a way the user will understand.

The Controller interprets actions by the user, usually mouse and keyboard events, and sends the information to the model or the view.

The model knows nothing about the view or the controller other than that they exist. The view and the controller know as much as possible about the model.

The advantage to this type of architecture is that the view and the controller may be changed without affecting the model. For web applications that need to render themselves on various browsers and devices this is an ideal architecture that allows the application to be written originally for a desktop browser and later written for a PDA by changing only the view layer.

mvc.gif

Typical UML of MVC

JSP's Model II architecture is an attempt by those working in this environment to coerce MVC into a web based world. In this case the controller can no longer access the view directly, so we end up with the controller sending information to the model and the view always pulling the information out of the model.

webmvc.gif

UML of MVC on the web

In a Model 2 architecture, a central controller is used which then delegates to other controllers and/or the appropriate resulting view. In terms of physical components of a JSP environment, MVC is typically implemented by posting to a servlet that processes the form information and then forwards to a JSP page to render the resulting page.

This combined with EJB or JavaBeans keeps the view separate from the controller and the model.

jspmvc.gif

This translates rather easily to ASP. The Servlet becomes an ISAPI.DLL, the JSP page becomes an ASP page, and the model becomes ActiveX controls.

aspmvc.gif

Enter ASP.NET

If your only experience with MVC is through Model 2 architecture, the ability to implement MVC architecture in ASP.NET is not immediately apparent. However, ASP.NET not only allows MVC, it encourages it in the way the pattern was originally conceived.

Since the definition of MVC states that the View renders the data from the Model and that the Controller sends user events to the Model, generally from the elements rendered on the view, most could agree that there is a very high natural coupling between the View and the Controller. So, a natural real world implementation of MVC, and indeed what you normally find outside of web applications, is a one to one correspondence between the View and the Controller. However, in a web application, there is a need to not only send data to the model, there is also a need to control which view should render based on the data the model currently contains. A kind of a "View Controller." In some Model 2 implementations, there is a central Controller which we will call the "View Controller." This View Controller delegates form processing to other programming entities and eventually redirects to the appropriate view. JavaBeans in the case of JSP, ActiveX controls in the case of ASP. If you've ever worked with the Struts framework for JSP you are familiar with this model.

Since ASPX pages post back to themselves, we obviously can't use a "View Controller." So, how does one implement the same functionality in .NET? Furthermore, those familiar with Struts will recognize that the ability to loosely couple the views (or controllers) from each other is not available in ASP.NET "out of the box."

The following architecture solves both of these problems.

In a Model 2 architecture using a Central Controller, a delegate pattern is used to control access to the different services, or sub-controllers. You ask for a service and the central controller determines if you should be allowed access to the service and how to implement the service.

In ASP.NET, you generally ask for the service directly. Instead of a service being a sub-controller, it is the main point of entry. What this doesn't offer is a central location for determining if you should have access to the service in the first place, or, any other common processing that should happen in all of the services. However, by having all of your code-behind pages inherit from a common class that is a subclass of System.Web.UI.Page, you can place all of this pre-processing code in the OnLoad() method that you override from the Page class. OnLoad() is call by the framework every time the page is loaded. But before the Page_Load() method is called in your code-behind page. This allows you to verify that the service is allowed and do any redirecting you need to do in one central location, using Server.Transfer(), similar to what a Central Controller would do. I named my View Controller class ViewController.

The second requirement, making the views loosely coupled from each other, requires a bit more work. First, you will need to create an enum in your ViewController class that has an entry that corresponds to each page in your web application. I also added an enum named UNDEFINED. Second, you will need to create a member variable to hold the ID of the page. I initialized mine to UNDEFINED. This gets placed in your ViewController class and is of the type of your enum. Third, you will need to create a property to access this member variable. I've named mine ServiceID. Fourth, create a method in your ViewController named getServicePage() that takes the enum as a parameter and returns a string containing the appropriate aspx file. Finally, in the constructor of each of your code-behind pages, set the ServiceID of the page. Now, anywhere in your code where you are redirecting or linking to an aspx page in your application, you will call the getServicePage() method to retrieve the name of the file to redirect to.

If you've ever refactored your website to the point of renaming web pages, you should immediately see that this will significantly simplify maintenance. If you want to rename an aspx file you now can rename the file and change the lookup in getServicePage and your site should still work. The only problem with this implementation is that there is no good way to enforce it's usage.

aspxmvc.gif

Conclusion

With the advent of ASP.NET, many of the hacks we have had in place have been removed. All of the view and controller related code is now in essentially one place instead of two. The defacto standard for writing view based application (MVC) is preserved.

0 Comments: