Models, Views and Controllers in ASP.NET websites
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 metadata 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 attribute.
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:
Method | Description |
---|---|
ActionLink | Create anchor tag |
BeginForm | Generates form tag |
DropDownList | Creates select list based on list of values |
Password | Create password input box |
RenderAction | Call action method on child controller, result rendered within its parent |
RenderPartial | Render a partial view (user control) from parent |
RouteLink | Anchor 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:
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.
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}/
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 (separated 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}")