First Look at ASP.NET MVC
What is ASP.NET MVC?
ASP.NET MVC is a framework for building scalable, standards-based web applications using well-established design patterns and the power of ASP.NET and the .NET Framework.
It is one of the best framework in the market that employs Model-View-Controller pattern to create web applications in highly modular way.
It couples the models, views, and controllers using interface-based contracts, thereby allowing each component to be easily tested independently.
The first stable version was released on March 2009 and the current stable version is 3.0 released on January 2011.
The source code is released open to public under the
Apache License 2.0 and is available in
codeplex.
MVC
MVC is a design architecture that helps to build a software system by separating the business
model from the presentation.
MVC split the application implementation into three parts: Model, View and Controller.
Model
The domain objects in the application that maintains all the state, validations and rules associated
with the business forms the Model.
View
The elements that displays the model to the user is called as a View. View is usually a HTML page.
Controller
The Controller is the component that receives input from the View and updates the Model.
It also feeds the View with the Model data to present to the user.
Separation of concerns
The busines model is separated from the displaying logic, this helps to create modular systems that are easy to test and maintain.
The mantra is.. Dump Views Thin Controllers Fat Models
Enables Test Driven Development
The core components of the applications is loosely coupled through interfaces making each component testable independently.
Easy integration with JavaScript frameworks
Generates clean HTML making the application to integrate easily with popular JavaScript frameworks.
SEO friendly URLs
The Routing System helps to create simple and friendly URLs that are easily crawled by search engines and memorizable by an user.
Full control over HTML
We can generate standard-compliant HTML that helps to reach the application to a wide range of devices and browsers.
Highly extensible
We can bend the framework to our application needs, because most of the parts in ASP.NET MVC processing pipeline can be replaceable with custom components.
Benefits of ASP.NET MVC
Separation of concerns
Enables Test Driven Development
Easy integration with JavaScript frameworks
SEO friendly URLs
Full control over HTML
Highly extensible
Components of ASP.NET MVC
Controllers
Controllers
All the controllers derives from the abstract class Controller.
All the controllers should be placed in a separate folder Controllers.
MVC requires the name of all controllers to end with "Controller".
Each controller contain one or more actions that recieves and processes the HTTP requests.
A Sample Controller
public class BlogController: Controller
{
public ActionResult Index() {
var posts = repo.GetAllPosts();
return View(posts);
}
public ActionResult Edit(int postId) {
var post = repo.GetPost(postId);
return View(post);
}
[HttpPost]
public ActionResult Edit(Post post) {
repo.SavePost(post);
RedirectToAction("Index");
}
}
Actions and Action Results
Actions and Action Results
Action are methods inside controllers that have typically a one-to-one mapping with user interactions.
Action methods return an instance of a class that derives from ActionResult.
Some of the built-in action result types are: ContentResult, ViewResult, RedirectToRouteResult, JsonResult, FileResult etc.
ViewResult returns a view from action. JsonResult returns json.
We can implement our own action results from the abstract class ActionResult.
Returning different results from actions
public class HomeController: Controller
{
public ContentResult SayHello() {
return Content("Hello, World!");
}
public ViewResult About() {
return View("AboutMe");
}
public JsonResult About() {
return Json(new { id = 1, success = true });
}
}
Views
Views
Views are templates that renders HTML output from the model data passed by the controllers.
All the views are placed in the Views folder in ASP.NET MVC.
ASP.NET MVC also contains partial, layout and error views.
Model or data from controllers are passed to views using ViewData, ViewBag.
A Sample Razor View
@model CustomValidation.MVC.Models.Party
@{
ViewBag.Title = "Party Planner";
}
<h2>
@ViewBag.Title</h2>
@using (Html.BeginForm())
{
@Html.ValidationSummary()
<p>
Start date (MM/dd/yyyy HH:mm:ss AM/PM) *:
@Html.TextBoxFor(x => x.StartDate, new { size = 25 })</p>
<p>
No. of joinees *: @Html.TextBoxFor(x => x.NoOfJoinees, new { size = 5 })</p>
<p>
Drinks? @Html.CheckBoxFor(x => x.Drinks)</p>
<p>
<input type="submit" value="Host the party!" />
</p>
}
Passing data to Views
public ActionResult Index() {
var posts = repo.GetAllPosts();
return View(posts);
}
public ActionResult Index() {
ViewData["posts"] = repo.GetAllPosts();
return View();
}
public ActionResult Index() {
ViewBag.Posts = repo.GetAllPosts();
return View();
}
View Engines
View Engines
The component that generates HTML output from the view and model is called as View Engine.
WebForms and Razor are the built-in view engines.
We can use multiple view engines or even use own view engine in an application.
ViewEngies are added in the Application_Start event of Global.asax.cs.
ViewEngines.Engines.Add(new RazorViewEngine());
ViewEngines.Engines.Add(new MyViewEngine());
Routing System
Routing System
The Routing system contains a set of routes that map URLs of any pattern to controller actions.
It also helps in generating outgoing URLs from the created routes.
Routes are added in the Application_Start event of Global.asax.cs.
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Post",
"Blog/Archive/{year}/{month}/{fileName}",
new { controller = "Blog", action = "Post" }
);
Filters
Filters
Fiters are used to perform logic either before an action method is called or after an action method runs.
Filters are attributes that can be applied over a controller or action or even at a global level.
The four types of filters are: Authorization, Action, Result and Exception filters.
[HandleError]
public class HomeController : Controller {
public ActionResult About() {
return View();
}
}
Model Binders
Model Binders
Model Binders provides a simple way to map posted form values to a .NET Framework type and pass the type to an action method as a parameter
Model Binders are like type converters, because they can convert HTTP requests into objects that are passed to an action method.
public ActionResult Details(Person person) {
return View(person);
}
Data Annotations
Data Annotations
Validations are easily applied to a model using data annotation attributes.
We can create our own custom validations by implementing ValidationAttribute or IValidatabaleObject.
public class Movie {
public int Id { get; set; }
[Required(ErrorMessage="Movie title is required.")]
[StringLength(8, ErrorMessage="Movie title cannot be longer than 8 characters")]
public string Title { get; set; }
[Required(ErrorMessage="Movie director is required")]
public string Director { get; set; }
}
Request-Handling Pipeline
Request-Handling Pipeline
Route Constraints
Route Constraints are applied to run some logic when the input request is received by a route.
We can create our own custom route constraints by implementing the IRouteConstraint interface.
Route Constraints are applied while creating the routes.
Creating a custom route constraint
public class UserAgentConstraint: IRouteConstraint {
private string requiredUserAgent;
public UserAgentConstraint(string agentParam) {
requiredUserAgent = agentParam;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName,
RouteValueDictionary values, RouteDirection routeDirection) {
return httpContext.Request.UserAgent.Contains(requiredUserAgent);
}
}
routes.MapRoute("MyRoute",
"{controller}/{action}",
new { controller = "Home", action = "Index" },
new { customConstraint = new UserAgentConstraint("IE")},
);
Route Handlers
Route handler is the component that handles the request when they are matched by a route.
MvcRouteHandler is the built-in handler. We can create custom handler by implementing the IRouteHandler interface.
When we create a route we can set the route handler to handle the request.
Creating a custom route handler
public class CustomRouteHandler: IRouteHandler {
public IHttpHandler GetHttpHandler(RequestContext requestContext) {
return new CustomHttpHandler();
}
}
public class CustomHttpHandler: IHttpHandler {
public bool IsReusable {
get{ return false; }
}
public void ProcessRequest(HttpContext context) {
context.Response.Write("Hello");
}
}
routes.Add(new Route("SayHello", new CustomRouteHandler()));
Controller Factories
This the component that instantiate and serve controller instances to process the request.
DeafultControllerFactory is the built-in factory class.
We can create our own factory to change the controller instantiation behavior by implementing the interface IControllerFactory.
The custom controller factory are set to the MVC framework using the ControllerBuilder class in Global.asax.cs.
One of the reason we go for creating custom controller factory is for Dependency Injection.
Creating a custom controller factory
public class CustomControllerFactory: IControllerFactory {
public IController CreateController(RequestContext requestContext,
string controllerName) {
return new HomeController(new PostRepository());
}
public SessionStateBehavior GetControllerSessionBehavior
(RequestContext requestContext, strong ControllerName) {
return SessionStateBehavior.Default;
}
public void ReleaseController(IController controller) {
if(controller is IDisposable)
((IDisposable)controller).Dispose();
}
}
ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());
Action Invokers
Action Invokers are the component that invokes the action methods of a controller.
ControllerActionInvoker is the default built-in action invoker and it invokes the actions based upon the name.
Custom action invokers can be created by implementing the interface IActionInvoker.
To use our custom action invoker by the framework, we have to set it to the controller.
Creating a custom action Invoker
public class CustomActionInvoker: IActionInvoker {
public bool InvokeAction(ControllerContext context, string actionName) {
if(actionName == "Index") {
context.HttpContext.Response.Write("This is Index action");
return true;
}
return false;
}
}
// setting the custom action invoker to the controller
public class CustomController: Controller {
public CustomController() {
this.ActionInvoker = new CustomActionInvoker();
}
}
View Engines
ASP.NET MVC comes with two built-in view engines: Razor and WebForms.
All the view engines are implemented from the interface IViewEngine.
MVC comes with some abstract implementations of IViewEngine like VirtualPathProviderViewEngine, BuildManagerViewEngine.
All the existing and new view engines are stored in the ViewEngines collection.
Creating a custom view engine
public class XsltViewEngine: VirtualPathProviderViewEngine {
public XsltViewEngine() {
base.ViewLocationFormats = new[] { "~/Views/{1}/{0}.xsl", "~/Views/Shared/{0}.xsl" };
base.PartialViewLocationFormats = base.ViewLocationFormats;
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) {
return new XsltView(partialPath);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) {
return new XsltView(viewPath);
}
}
Creating a custom view
public class XsltView : IView {
private readonly string _path;
public XsltView(string path) {
_path = path;
}
public void Render(ViewContext viewContext, TextWriter writer) {
var xsltFile = viewContext.HttpContext.Server.MapPath(_path);
var xmlData = viewContext.ViewData["data"] != null
? ((XElement)viewContext.ViewData["data"]).ToString() : "";
var xmlTree = XDocument.Parse(xmlData);
var xslt = new XslCompiledTransform();
xslt.Load(xsltFile);
xslt.Transform(xmlTree.CreateReader(), null, writer);
}
}
Adding the custom view engine
View engines are added to the ViewEngines collection from the Application_Start event of Global.asax.cs.
protected void Application_Start() {
ViewEngines.Engines.Add(new XsltViewEngine());
}
Filters
Filters inject extra logic into the request processing pipeline.
Filters are applied as attributes over controllers or actions.
We can create custom filters by implementing IAuthorizationFilter, IActionFilter, IResultFilter or IExceptionFilter.
Creating a custom action filter
public class AuditFilter : FilterAttribute, IActionFilter {
public void OnActionExecuted(ActionExecutedContext filterContext) {
Debug.WriteLine("OnActionExecuted is called");
}
public void OnActionExecuting(ActionExecutingContext filterContext) {
Debug.WriteLine("OnActionExecuting is called");
}
}
[AuditFilter]
public ActionResult EditProfile() {
}
MVC Extensions
Route Constraints
Route Handlers
Controller Factories
Action Invokers
View Engines
Filters
Web forms vs. MVC
Web forms - Advantages
Good for Rapid Application Development
Provides a rich set of UI controls
Makes to feel web as stateless through viewstate and event-driven programming model
Easy to learn
Web forms - Disadvantages
Unit testing is a pain
Viewstate increases the page size and bandwidth
Can't easy to integrate with JavaScript frameworks
Create maintainability issues for big applications
Hides the way how web works through heavy abstractions and this makes young developers not much aware of HTTP and other basic things.
MVC - Advantages
Follows Separation of Concerns by dividing an application into models, views and controllers
Loosely couples the components through interfaces making unit testing so easy
Gives full control of the generated HTML and there by makes CSS styling and JavaScript frameworks integration easy
Guides a better organization of code reducing maintenenace cost
Follows web standards making the application to reach wide range of devices
Provides good support for SEO
MVC - Disadvantages
Learning curve is steeper compared to Web Forms.
Adds complexity for simple applications
Need a strong understanding in HTTP, JavaScript and other basic stuff
No built-in UI controls