Models, Views and Controllers

Defining Model and business Rules

Model can be written in any way that makes sense to application - no constraints or requirements.

Can create custom classes, use generated code, use existing model, etc.

Have many attributes in common; work with data in data store, contain business and validation rules, provide mechanisms for selecting items.

Creating Custom Model

Usually place into Models folder, but can also exist in App_Code folder or in separate class library (latter common when shared between apps).

Create class for each entity in model.

Define properties for entity and methods to operate on them.

Business logic added as code or annotations.

Creating Partially Generated Model

MVC framework can be extended in way similar to LINQ To SQL or Entity Framework using the partial classes to define metatdata and validation classes.

Model Validation Rules

Easy way to add validation and business rules is via DataAnnotations class, e.g.

[Required(ErrorMessage = "Customer id required")]
public object CustomerId { get; set; }

Rules defined for model are processed by MVC validation engine.

Prior to executing methods such as Update or Insert should check controller class's ModelState.IsValid to determine if model has thrown validation errors.

Validation attributes can be used in both custom models and partially generated ones.

Creating Controllers

Inherit from Controller base class.

Create for each entity in model using _Entity_Controller naming convention.

Typically 1:1 mapping between user and controller actions.

Action methods responsible for handling errors and validation issues occurring within model.

Return ActionResult (typically a view) as result of action.

Result may pass data to view from model.

The WebFormViewEngine is used to render results to user.

Controller Template

Visual Studio includes template for creating controllers - available via Add | Controller.

Template accepts name and generates stubs fro common activities - create, read, update, delete.

Returning Different ActionResult objects

Most controller methods return the base class ActionResult.

The Controller class exposes a helper method to create each of the available ActionResult derived classes:

  • ViewResult (View) - return webpage
  • PartialViewResult (PartialView) - send section of view to be rendered inside another
  • RedirectResult (Redirect) - redirect to another controller and action method based on a URL
  • RedirectToRouteResult (RedirectToAction, RedirectToRoute) - redirect to another action method
  • ContentResult (Content) - return custom content type, e.g. text/plain
  • JsonResult (Json) - return a JSON formatted message
  • JavaScriptResult (JavaScript) - return JavaScript code to be executed by browser
  • FileResult (File) - Sends binary output as response
  • EmptyResult - Sends null as the response

Can also return simple types such as string or integer - these will be wrapped in ActionResult for you.

To indicate no action response decorate method with NonAction attribute.

Passing data to Action Methods

Passed from request to action method as series of name-value pairs.

For example, http://mySite/customer/search/?customerName=test&companyName=test will be mapped to the CustomerSearch(customerName, companyName) action method.

HTTP Post Parameters

Can call action method as part of HTTP post.

Method must be marked with HttpPost attribiute.

Method will take a FromCollection parameter which ASP.NET will fill with data posted by form.

[HttpPost]
public ActionResult Create(formCollection collection)  
{...

Request and Response Data

Can gain access to additional data in request (HTTP Get) or response (HTTP Post) via the Controllers Request and Response properties.

Passing Data to Views

Two main methods; a ViewDataDictionary or a strongly typed view.

The ViewDatadictionary is object collection referenced by string keys. Accessed via ViewData property on Controller and ViewPage base class.

Action Filters

Called both before and after action method is called.

Applied by adding attributes to action methods.

Actionfilters are attribute classes deriving from FilterAttribute class.

Can create own or use predefined ones including authorize, OutputCache and HandleError.

Creating Views

Views render user interface.

Stored in sub-folders of Views folder.

One sub-folder per controller.

Add views to folder based on action method names, e.g. Edit, Index, Create, etc.

ASP.NET will render view matching executed controller action method - unless told to use specific view, e.g. return View("SomeView");

All processing of input should be done by controller.

All data storage and manipulation by the model.

View do not require (and typically don't have) code behind files - instead use markup to reference data sent to them.

Inherit from System.Web.Mvc.ViewPage which is itself based on the ASP.NET Page class.

Can use master pages deriving from ViewMasterPage.

Create using View template - accessed via Add | View.

View page uses @ Page directive to set page title, master file and inheritance.

Markup similar to standard ASP.NET page, e.g. if using Masterpage place content within ContentPlaceholder controls.

<asp:Content ID="Content2" ContentPlaceHolderId="Maincontent" runat="server">

<h2>Customer Details</h2>

<%: ViewData["CustomerName"] %>

</asp:Content>

HTML Helper Classes

The System.Web.Mvx.HtmlHelper class assists in generating repetitive HTML.

HtmlHelper instance exposed through ViewPage.Html property, e.g.:

<% Html.DropDownList("countries", (SelectList)ViewData["countries"]) %>

Common helper methods:

MethodDescription
ActionLinkCreate anchor tag
BeginFormGenerates form tag
DropDownListCreates select list based on list of values
asswordCreate password input box
RenderActionCall action method on child controller, result rendered within its parent
RenderPartialRender a partial view (user control) from parent
RouteLinkAnchor tag based on route to action
ValidationMessage  Display validation messages for individual controls

 

Strongly Typed Views

View expects specific object type to be passed to it from model rather than general information stored in ViewData.

Within @ Page markup inherit from generic view page, e.g.

... Inherits="System.Web.Mvc.ViewPage<Customer>

The type is mapped to Model property on page:

<div class="display-field"><%: Model.CustomerID%></div>

Visual Studio helps to generate strongly typed views via Add View dialogue.

Data Scaffolding and Model Validation

Can use DataAnnotations scaffolding to provide metadata about model - indicates required fields, range validation, regular expressions, etc.

These attributes impact how views are generated, validated and processed by controller.

Typical markup to display validation messages:

<% using (Html.BeginForm() {%>

<% Html.ValidationSummary(true) %>

<fieldset>

<div class="editor-field">

<%: Html.TextBoxFor(model => model.CustomerID) %>

<% Html.ValidationMEssageFor(model => model.CustomerID) %>

...

When user submits form the Edit action called on controller.

View typically contains two Edit action methods, one for get and the other post. When user posts changes to server the Edit method is called, if form not valid then editing page returned.

public ActionResult Edit(string id)
{
    Models.northwndEntities nw = new Models.northwndEntities();
    var custQ = from c in nw.Customers where c.CustomerID = id select c;
    Models.Customer cust = cusrQ.FirstOrDefault;
    return View(cust);
}

[HttpPost]
public ActionResult Edit(string id, Models.Customer c)
{
    if (ModelState.IsValid == false)
        return View("Edit", c);
    else
        return RedirectToAction("Index");
}

Partial (Child) Views

Used when breaking view up into smaller parts, e.g. page presents dashboard of both sales and inventory data. Have parent for dashboard which brings into two child views - order and inventory.

Child view is a user control (.ascx).

Child view considered to be view and so can be accessed by controller.

Create either by selecting MVC 2 View User Control template from Add New Item, or selecting Create A Partial View via Add View.

Controller for this scenario

public class DashboardController: Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult OrderSummary()
    {
        return View();
    }

    public ActionResult InventorySummary()
    {
        return View();
    }
}

Request made for index page which will render the parent view. This will render children via HTML helper methods:

<asp:Content ... >

<h2>Index</h2>

<%= Html.Action("OrderSummary") %>

<%= Html.Action("InventorySummary") %>

</asp:Content>

The index view calls out to the DashboardController.OrderSummary and . DashboardController.InventorySummary. Both of these return a partial view that is rendered to the page.

Partial views have access to ViewData of their parent.

Customising Routing

Route Segments and Placeholders

URL pattern is defined set of segments delimited by /, e.g. Customer/Index/5

When request received the URL is broken into segments and placeholders.

Segments stored as key-value pairs where key is placeholder defined in routing definition and value is the value from the URL.

Placeholder keys defined as {controller}/{action}/{id}

Routing established by calling routes.MapRoute in Global.asax where routes is a RouteCollection instance. Values, such as Customer/Index/5, mapped according to the keys.

Can define placeholder by putting it within braces, e.g. {route}

Can define multiple placeholders in a segment by using literal delimiter, e.g. {language}-{country}/{route} has two segments (seperated by slash), first segment has two placeholders that act as keys in route collection.

Default Route Parameters

Route placeholders referred to as route parameters.

Used to pass named parameters (named with key) to routing engine.

Can set default values for route parameters which are used if they are omitted from URL.

routes.MapRoute("Default", "{controller}/{action}/{id", new { controller = "Home", action = "Index", id = UrlParameter.Optional } );

Route Constraints

Can specify that route segment must match a constraint, e.g. length, date range, etc.

If URL does not meet constraint then route not used to process request.

Define using regular expressions or objects implementing IRouteConstraint.

Routes.MapRoute("MonthYear", "{month}/{year}", new RouteValueDictionary(new {month = DateTime.Now.Month, year = DateTime.Now.Year}), new RouteValueDictionary(new {month = @"\d{2}", year = @"\d{4}" }));

Preventing Route Handling

There are scenarios where routing does not handle a request, e.g. when it maps to a physical file.

Can prevent physical files from circumventing routing by setting the RouteCollectionExtension objects RouteExistingFiles property to true:

routes.RouteExistingFiles = true

Can turn off routing for certain requests, via the RouteCollectionExtension.IgnoreRoute method:

routes.IgnoreRoute("{resource}.axd/{*pathinfo}")

Download