Click here to monitor SSC
  • Av rating:
  • Total votes: 141
  • Total comments: 21
Simone Chiaretta

An Introduction to ASP.NET MVC Extensibility

21 September 2011

Because ASP.NET MVC has been designed with extensibility as its design principle; almost every logical step of the processing pipeline can be replaced with your own implementation. In fact, the best way to develop applications with ASP.NET MVC is to extend the system, Simone starts a series that explains how to implement extensions to ASP.NET MVC, starting with the ones at the beginning of the pipeline (routing extensions) and finishing with the view extensions points.

If you are not extending, you are not doing it right: this is the main tenet that you must keep in mind while developing web applications with ASP.NET MVC.

I admit this is a strong statement, but over the next few months I'm going to explain why extending is the right way to develop with ASP.NET MVC, together with explaining when and where you can extend it, and, most importantly, how to do it. But not just yet: to start with, we will look at how a request is processed by the framework, and where during this processing you can replace the default behavior or inject your own logic. This obviously underpins everything we’ll be covering in later articles, so let’s make sure we understand this clearly.

Prerequisites

Given that we will be discussing several advanced topics in the coming months, it’s worth making sure you have a working knowledge of object oriented programming, .NET, ASP.NET, and the basics of ASP.NET MVC. All of the code samples I give you will be written in C#, so you’ll need to be comfortable with this language as well. The code samples will also be developed on .NET 4 and Visual Studio 2010.

What is extensibility?

The English dictionary defines extensibility as:

"the capability of being extended"

In computer science terms, a software system is extensible when it can be expanded without the need for changing and recompiling the original source code. This is also referred to as "open/closed principle", taken from Bertrand Meyer’s quite well-known writings about object-oriented programming:

"Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification."

  • Open for extension means that an entity can be made to behave in different ways from the original requirements, and that new requirements can be added.
  • Closed for modification means that the code of such an entity must not be changed once it is declared completed.

If a system does not meet these criteria, it will become very difficult to maintain and evolve, quickly leading to obsolescence.

These criteria are even more important for a commercial software framework that is somehow opinionated and adopts lots of conventions. If it weren’t extensible, then it wouldn’t be used by developers who didn't like the opinions or conventions adopted.

Because of that, ASP.NET MVC has been designed with extensibility as one of the main objectives; almost every step of the processing pipeline can be replaced with your own implementation. For example, if you don't like the default convention of having actions as public methods of a class, whose name is the name of the controller plus "Controller", you might swap it with your own IControllerFactory that implements the convention you think is most suitable for you or your team.

Chances are you’ve already extended ASP.NET MVC at some point or other. If you’ve used a view engine other than WebForm View Engine or Razor (such as Spark, for example), or if you’ve used a IoC container to create your controllers (such as Ninject or Unity), then congratulations! You’ve already used extensions, albeit other people’s.

Where Extensibility Points fit into the Processing Pipeline

At a very high level, the lifecycle of a request in ASP.NET MVC is:

  • The browser sends the GET or POST HTTP request;
  • The request is inspected and routed to the right controller and the right action;
  • The action collects or creates the data that needs to be returned to the browser;
  • This data is passed to the view that renders the response, which is sent back to the browser.

Hopefully you already knew this, but to give you a bit more context regarding where we can place all the various extensibility hooks, next we’re going to step through the processing pipeline in more detail, and with the help a flow diagram to visually identify it. I know that there’s a lot of ground to cover here, and it can appear quite dense if you’re not familiar with it, but take your time as you go through this material, and it should all be clear by the end.

The Routing part of the pipeline

The first macro area of the pipeline is the routing.

Note: The complete flow chart for these steps is available to download as a .PDF from the speech bubble at the top of the article, or from here.

  1. Upon receiving the request, the Routing module tries to match the incoming URL with the routing table of the application. The matching is usually done by evaluating the routing URL pattern, or can be made more powerful by specifying Route constraints (IRouteConstraint), allowing you to apply more complex evaluation logic, and even talk to databases or webservices.

  2. Once the Route has been selected, the corresponding IRouteHandler is called. The default one calls the MvcHandler, which starts the real processing inside ASP.NET MVC. Obviously, if you replace the default handler with something completely different, you also take over the processing of the request, and so all the steps that follow might not apply anymore.

Creation of the Controller

  1. Based on the route parameters and the default naming conventions, the IControllerFactory creates an instance of the controller. If an IoC container has been configured, the controller factory retrieves the instance of the controller with the help of the IDependencyResolver.

The Action execution

Once the controller has been instantiated, the specified action has to be executed, and this is handled by the IActionInvoker. As with the IRouteHandler, in theory you could completely replace this component with your own implementation, but then you will be developing against your own conventions. This is certainly not a problem, but it just means that the next steps will not apply:

  1. The first part of the process involves identifying the method to execute: in the simple scenario, the method which shares the same name as the action is chosen, but this behavior can be modified by applying an attribute of type ActionNameSelectorAttribute, which allows you to apply custom logic to this process. If more than one method is found, the correct one is chosen with the help of another kind of attribute applied to the methods: ActionMethodSelectorAttribute.

  2. If the method selected for execution has one or more parameters, they have to be filled in with the right values. The Model Binder (or rather, the IModelBinder interface) is the component responsible for this: it not only deserializes the HTTP request data into CLR objects, but also validates that the values are correct. This is done based on the validation rules (if the default ones are not enough you can write your own rules and validation logic) returned by the Model Validation Provider. Alternatively, another way to validate an object is to implement the IValidatableObject for cases where you need a validation rule that acts on the whole object, rather than just individual properties.

  3. to the action. These filters are discovered via another component that can be customized and replaced: the IFilterProvider. In case you are using IoC, the filters are also instantiated using the IDependencyResolver. Out of the four types of filters available, two of them are executed before the actual action method: Authorization filters and part of the Action filters (specifically, OnActionExecuting). Filters are a very powerful way to implement orthogonal or cross-cutting behaviors that have to be shared among many actions or controllers. Filters can be applied per action, per controller, or even per application.
     

  4. If the filters don’t stop the execution the action method is executed and the parameters are filled in with the values that were previously discovered by the Model Binder
    .

  5. When the action method has completed its tasks, it returns a result of type ActionResult. This is executed to return the response back to the client that sent the original request. However, there might be other filters that can be applied before the response is returned: the OnActionExecuted and the OnResultExecuting.

Finally, the View

There are many action results: one simply returns a text string, another returns a binary file or a Json formatted result. Yet the most important Action Result is the ViewResult, which renders and returns an HTML page to the browser.

  1. In ASP.NET MVC 3 there are two standard view engines: the WebForm view engine and the new Razor view engine, and their views are just normal .aspx pages (typically without a code-behind file) or ASP.NET Web Pages. However, more than one view engine can be registered, so the first step in the execution of the View Result involves selecting the appropriate engine to use and the view to render; which is all done via the IViewEngine interface of the view engine (which, despite name, takes care of finding the correct view).
     

  2. Once the correct view engine and the view is chosen, it is instantiated, again with the help of the IDependencyResolver, and the model is then passed to the view.

    The lifetime of a view then depends a lot on what it does; in the simplest case it could just render a bit of text, even without the need of HTML helpers. Alternatively, in a more complex scenario, it might render a form with JavaScript validation. In the latter, more complex case, we actually encounter two new extensibility points: the templated helper… er… templates, and the Model Metadata Provider. These two components work together to help you write less code, automatically creating the form based on the properties of the model object that you have to edit, as well as the metadata attached to it. While the default provider looks at attributes on the model class, if you want to store the metadata somewhere else you can write your own provider that provides metadata based on your own conventions.

  3. At this stage, the Model Validation Provider (which we encountered during the action execution) comes onto the stage again. Once the information from the model and the validation rules has been retrieved, client-side validation scripts are automatically added to the page. This means that verifying the length of a string, the value of a number and many other simple requirements can happen without you having to writing a single line of code. Just like the server-side validation that happens during the model binding stage, you can also add your own validation rules on the client side within their JavaScript code.

Another type of validation that happens on the client is remote validation; this is needed when the validation rules require access to resources on the server. For example, if you have a form in which you need to check whether a username has already been taken, the validator needs to call back to the server to determine whether the field is valid.

Now, when writing the code for the view, you will use three libraries:

  • HtmlHelper
  • AjaxHelper
  • UrlHelper

They contain helper methods to write input fields, create links based on the routes, AJAX-enabled forms, links and more. Moreover, extending them is very easy, and writing new methods for these libraries, as opposed to writing the code directly inline in the view, is also considered an ASP.NET MVC programming best practice.

Types of extensibility points

Now that we’ve gone over the process pipeline in more detail, you hopefully have a better idea of what’s happening under the hood, and you should also be starting to get a feel for the number of opportunities you have to extend your ASP.NET MVC application. However, as you might have already noticed, not all extensibility points need to be used with the same frequency:

Essentials

Some customizations are integral parts of the development of a web application with ASP.NET MVC, and thus are used in every project. The most common extensibility points in this category are:

  • Custom templates for the templated helpers
  • Server-side validation rules
  • Client-side validation rules
  • HTML Helpers

Productivity enhancers

Other extensibility points are not always needed, but if used can help make development more productive and less repetitive:

  • A base controller to enforce your own conventions
  • AJAX/URL Helpers
  • Action/Result Filters

Case-Specific

Next up, there are other extensibility points that you only need to use if you need to solve a specific problem:

  • Route Constraint
  • Route Handler
  • Action Result
  • Model Binders
  • ActionMethodSelector

Core Extensions

Finally, there are the extensibility points that change a big part of ASP.NET MVC, and which you are not very likely to implement. These might even remove some of the other extensibility points and add their own. Nevertheless, there is a very high chance that you used a custom component for one of these points that was developed by someone else:

  • Controller Factory
  • Action Invoker
  • View Engine
  • Model Validation Provider
  • Model Metadata Provider

How these Articles will be structured

During the course of these articles, I'm going to explain how to implement your extensions to ASP.NET MVC, starting with the ones at the beginning of the pipeline (routing extensions) and finishing with the view extensions points. I'll focus mainly on the first two categories (“Must Use” and “Productivity Enhancers”), but I'll also briefly touch upon the more rarely implemented ones, where there are useful modifications to be made.

For each point explained, a common structure will be followed:

  • why your own custom implementation might be needed
  • area of extensibility (routing, controller, views, etc)
  • type of extensibility (must use, only when necessary, etc)
  • the interfaces that have to be implemented
  • what the default implementation (if it exists) does
  • sample of already available implementations in the community (if any)
  • sample implementation

Thus far we’ve seen an overview of the entire ASP.NET MVC processing pipeline, and should now have a feel for the huge number of opportunities available to extend and customize how the framework behaves. We’ve also seen that not all extensibility hooks are created equal, and there are some which are more useful than others. Now that we’re all on the same page, the next article will cover how the routing process can be extended.

The free wallchart, 'ASP.NET MVC Pipeline', that goes with this article is available as a PDF file from the speechbubble at the head of the article or from here. It is best printed on an A3 printer.

Simone Chiaretta

Author profile:

Simone Chiaretta is a software architect and developer who enjoys sharing his 15+ years' worth of knowledge of Web development. He is currently web architect and team lead in the Internet team of the Council of the European Union, the main decision-making body of the European Union. He is also a Microsoft MVP in ASP.NET, an ASPInsider, founder of the Italian ALT.NET user group and frequent speaker for community events through Europe. Simone has written two books about ASP.NET MVC - "Beginning ASP.NET MVC 1.0" and "What's New in ASP.NET MVC 2" - and when not writing code, books, or blog posts, he loves being in the outdoors, and tries to train to take part in triathlons.
You can read his blog or follow him on twitter as @simonech

Search for other articles by Simone Chiaretta

Rate this article:   Avg rating: from a total of 141 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: Great article
Posted by: abellix78 (view profile)
Posted on: Friday, September 23, 2011 at 5:15 AM
Message: Nice and complete. I'll keep this as a reference!

Subject: Great MVC breakdown.
Posted by: mechInferno (view profile)
Posted on: Friday, September 23, 2011 at 9:33 AM
Message: This is just what I've been looking for. I've dabbled a bit in MVC development but I'm still fairly new to it.

I'm looking forward to the rest of these articles.

Thanks,

Subject: Nice article
Posted by: Anonymous (not signed in)
Posted on: Saturday, September 24, 2011 at 3:30 AM
Message: Looking forward to upcoming articles on subject.

Subject: Excellent start
Posted by: ymhee_bcex (view profile)
Posted on: Sunday, September 25, 2011 at 3:27 PM
Message: Simone,

This is a very challenging topic. You say (tongue in cheek, I am sure) that "if you are not extending, you are not doing it right", but one needs to be darn sure that he is indeed *extending* and not replicating something that already exists and he just doesn't know about it! So, most books are naturally concentrating on teaching what already exists.

Moreover, the decision to extend shouldn't be taken lightly - I would strongly discourage to extend just in order to implement the convention you think is most suitable for you or your team. Team members change; somebody else will maintain this code - and it's better to stay with what's accepted universally.

BUT - with these disclaimers - the subject of extending MVC clearly hasn't been covered enough! There was something written about Validation Provider, Model Binder, Action Filters - but not enough AND not well enough!

Good luck!

Subject: need
Posted by: Anonymous (not signed in)
Posted on: Sunday, September 25, 2011 at 5:00 PM
Message: I feel, You need to give few examples as well, otherwise there are plenty of such articles on the web.

Subject: This is just the introduction
Posted by: simonech (view profile)
Posted on: Monday, September 26, 2011 at 5:08 AM
Message: Dear ymhee_bcex,
I agree, extending should not be done because you don't know what the standard behavior is.
But I think that ASP.NET MVC is a fairly not-opinionated framework on purpose: to allow developers to build their own "opinions" on top of it.
Till v2 you had to build an extension if you wanted to use IoC/DI to create controllers.
With conventions I mean piece of code you find yourself writing over and over again. Sure you can create a "standard" method, but most of the times, in ASP.NET MVC, you can find a way to put it into the pipeline, and hide it away. I agree sometimes things can become too "automagical", but if they make development faster and less repetive, I go for them.
Developers come and go... but 99% of the teams I've been at already had helper libraries or even internal development libraries, so a new developer always have to tune into the practices of a team.

Anyway, there will be other 9 articles covering why extending, how to extend, with real worlds examples. I've blogged on my own blog the (tentative) list of articles:
10 articles on ASP.NET MVC Extensibility to land on Simple-Talk in the next months

Hope this answered your comment.
Simone

Subject: I want more! =)
Posted by: Anonymous (not signed in)
Posted on: Monday, September 26, 2011 at 8:54 AM
Message: awesome article. What are your references? how did you obtain all the knowledge required to write your article? Even books that are prefixed with "Pro" and "Advanced" are not nearly as advanced as this article.

Thanks

Subject: Thanks!!!
Posted by: Anonymous (not signed in)
Posted on: Monday, September 26, 2011 at 10:19 AM
Message: Very nice post, a must read. Thanks a lot.

Subject: Re: I want more! =)
Posted by: simonech (view profile)
Posted on: Tuesday, September 27, 2011 at 1:23 AM
Message: Dear Anonymous, the reference, most of the time for advanced topics, is code. ASP.NET MVC is opensource, so all code is available in its origianal form. And besides, what would have been the added value of writing an article with content coming from other books? :)

Subject: Great Article
Posted by: Anonymous (not signed in)
Posted on: Thursday, September 29, 2011 at 9:20 AM
Message: This article is great reading and put together well. Thanks!

Subject: Bare minerals store
Posted by: Make up ideas (not signed in)
Posted on: Thursday, September 29, 2011 at 1:16 PM
Message: To facilitate was a impressive place of duty. It is inspiring on behalf of all. Credit on behalf of sharing to facilitate slice.

Subject: Great post
Posted by: Holger (not signed in)
Posted on: Friday, September 30, 2011 at 3:11 AM
Message: Looking forward to the next articles.

Subject: Looking forward to reading the rest
Posted by: Tom (view profile)
Posted on: Friday, September 30, 2011 at 10:18 AM
Message: Very clear article, looking forward to reading more. How long till you publish the next one?

Subject: Cool article
Posted by: Anonymous (not signed in)
Posted on: Saturday, October 01, 2011 at 1:20 PM
Message: Thank you very much for such useful article

Subject: Re: Looking forward to reading the rest
Posted by: simonech (view profile)
Posted on: Sunday, October 02, 2011 at 4:02 PM
Message: Next article is already in the works.

Subject: Waiting for next article.
Posted by: Anonymous (not signed in)
Posted on: Tuesday, October 04, 2011 at 10:25 AM
Message: Thanks for this post.

Subject: Great and want more
Posted by: Anonymous (not signed in)
Posted on: Tuesday, October 04, 2011 at 7:24 PM
Message: Perfect !! waiting for more in this topic.

Subject: Plugin Architecture
Posted by: jchannon (view profile)
Posted on: Sunday, October 09, 2011 at 11:15 AM
Message: Great article and proposed ones. I was hoping that you might do an example of extensibility by illustrating how to do a plugin architecture ie. Have a main MVC app which will accept compiled dll's in the bin folder

Subject: Plugin Architecture
Posted by: jchannon (view profile)
Posted on: Sunday, October 09, 2011 at 11:38 AM
Message: The compiled DLL would have controllers, models and views but would be a plugin.

Would you be able to include this in your articles?

Subject: I am sure the next chapter is coming really soon now
Posted by: ymhee_bcex (view profile)
Posted on: Tuesday, November 01, 2011 at 9:40 PM
Message: In the meantime only comment spam is piling up :(

Subject: Very good article
Posted by: maxtyrann (view profile)
Posted on: Saturday, June 09, 2012 at 9:54 AM
Message: This is probably the best article if you want to know how ASP.NET MVC 3 works behind the scenes and that diagram is very very useful to summarize the information here. Thanks and congratulations ^^

I hope you can publish the rest of 10, a 6 month absence is a little discouraging, it kinda reminds me Andrew Siemer's article series at Dotnetslackers :(

 

Top Rated

Acceptance Testing with FitNesse: Multiplicities and Comparisons
 FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style... Read more...

Acceptance Testing with FitNesse: Symbols, Variables and Code-behind Styles
 Although FitNesse can be used as a generic automated testing tool for both applications and databases,... Read more...

Acceptance Testing with FitNesse: Documentation and Infrastructure
 FitNesse is a popular general-purpose wiki-based framework for writing acceptance tests for software... Read more...

TortoiseSVN and Subversion Cookbook Part 11: Subversion and Oracle
 It is only recently that the tools have existed to make source-control easy for database developers.... Read more...

TortoiseSVN and Subversion Cookbook Part 10: Extending the reach of Subversion
 Subversion provides a good way of source-controlling a database, but many operations are best done from... Read more...

Most Viewed

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Calling Cross Domain Web Services in AJAX
 The latest craze for mashups involves making cross-domain calls to Web Services from APIs made publicly... Read more...

Web Parts in ASP.NET 2.0
 Most Web Parts implementations allow users to create a single portal page where they can personalize... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.