Your First ASP.NET MVC Application
When downloading and installing the ASP.NET MVC framework SDK, a new project template is installed in Visual Studio-the ASP.NET MVC project template. This chapter describes how to use this template. We will briefly touch all aspects of ASP.NET MVC by creating a new ASP.NET MVC web application based on this Visual Studio template. Besides view, controller, and model, new concepts including ViewData-a means of transferring data between controller and view, routing-the link between a web browser URL and a specific action method inside a controller, and unit testing of a controller are also illustrated in this chapter.
In this chapter, you will:
- Receive an overview of all of the aspects of an ASP.NET MVC web application
- Explore the ASP.NET MVC web application project template that is installed in Visual Studio 2008
- Create a first action method and a corresponding view
- Create a strong-typed view
- Learn how a controller action method can pass strong-typed ViewData to the view
- Learn what unit testing is all about, and why it should be performed
- Learn how to create a unit test for an action method by using Visual Studio's unit test generation wizard and modifying the unit test code by hand
- Creating a new ASP.NET MVC web application project
Before we start creating an ASP.NET MVC web application, make sure that you have installed the ASP.NET MVC framework SDK from www.asp.net/mvc. After installation, open Visual Studio 2008 and select menu option File | New | Project. The following screenshot will be displayed. Make sure that you select the .NET framework 3.5 as the target framework. You will notice a new project template called ASP.NET MVC Web Application. This project template creates the default project structure for an ASP.NET MVC application.
After clicking on OK, Visual Studio will ask you if you want to create a test project. This dialog offers the choice between several unit testing frameworks that can be used for testing your ASP.NET MVC application.
You can decide for yourself if you want to create a unit testing project right now-you can also add a testing project later on. Letting the ASP.NET MVC project template create a test project now is convenient because it creates all of the project references, and contains an example unit test, although this is not required. For this example, continue by adding the default unit test project.
What's inside the box?
After the ASP.NET MVC project has been created, you will notice a default folder structure. There's a Controllers folder, a Models folder, a Views folder, as well as a Content folder and a Scripts folder. ASP.NET MVC comes with the convention that these folders (and namespaces) are used for locating the different blocks used for building the ASP.NET MVC framework. The Controllers folder obviously contains all of the controller classes; the Models folder contains the model classes; while the Views folder contains the view pages. Content will typically contain web site content such as images and stylesheet files, and Scripts will contain all of the JavaScript files used by the web application. By default, the Scripts folder contains some JavaScript files required for the use of Microsoft AJAX or jQuery.
Locating the different building blocks is done in the request life cycle, which is described in Chapter 4, Components in the ASP.NET MVC Framework. One of the first steps in the ASP.NET MVC request life cycle is mapping the requested URL to the correct controller action method. This process is referred to as routing. A default route is initialized in the Global.asax file and describes to the ASP.NET MVC framework how to handle a request. Double-clicking on the Global.asax file in the MvcApplication1 project will display the following code:
using System;In the Application_Start() event handler, which is fired whenever the application is compiled or the web server is restarted, a route table is registered. The default route is named Default, and responds to a URL in the form of http://www.example.com/{controller}/{action}/{id}. The variables between { and } are populated with actual values from the request URL or with the default values if no override is present in the URL. This default route will map to the Home controller and to the Index action method, according to the default routing parameters. We won't have any other action with this routing map.
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace MvcApplication1
{
public class GlobalApplication : System.Web.HttpApplication {
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",// Route name "{controller}/{action}/{id}",// URL with parameters new { controller = "Home", action = "Index",id = "" } // Parameter defaults );
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
}
}
By default, all possible URLs can be mapped through this default route. It is also possible to create our own routes. For example, let's map the URL http://www.example.com/Employee/Maarten to the Employee controller, the Show action, and the firstname parameter. The following code snippet can be inserted in the Global.asax file we've just opened. Because the ASP.NET MVC framework uses the first matching route, this code snippet should be inserted above the default route; otherwise the route will never be used.
routes.MapRoute(
"EmployeeShow", // Route name "Employee/{firstname}", // URL with parameters new { // Parameter defaults controller = "Employee",
action = "Show",
firstname = "" }
);
The EmployeeController class will pass an Employee object to the view. This Employee class should be added in the Models folder (right-click on this folder and then select Add | Class from the context menu). Here's the code for the Employee class:
namespace MvcApplication1.ModelsAfter adding the EmployeeController and Employee classes, the ASP.NET MVC project now appears as shown in the following screenshot:
{
public class Employee {
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
}
The EmployeeController class now looks like this:
using System.Web.Mvc;
using MvcApplication1.Models;namespace MvcApplication1.Controllers
{
public class EmployeeController : Controller {
public ActionResult Show(string firstname)
{
if (string.IsNullOrEmpty(firstname))
{
ViewData["ErrorMessage"] = "No firstname provided!";
}
else {
Employee employee = new Employee
{
FirstName = firstname,
LastName = "Example",
Email = firstname + "@example.com" };
ViewData["FirstName"] = employee.FirstName;
ViewData["LastName"] = employee.LastName;
ViewData["Email"] = employee.Email;
}
return View();
}
}
}
Note that we are returning an ActionResult (created by the View() method), which can be a view-rendering command, a page redirect, a JSON result (discussed in detail in Chapter 8, AJAX and ASP.NET MVC), a string, or any other custom class implementation inheriting the ActionResult that you want to return. Returning an ActionResult is not necessary. The controller can write content directly to the response stream if required, but this would be breaking the MVC pattern-the controller should never be responsible for the actual content of the response that is being returned.
Next , create a Show.aspx page in the Views | Employee folder. You can create a view by adding a new item to the project and selecting the MVC View Content Page template, located under the Web | MVC category, as we want this view to render in a master page (located in Views | Shared). There is an alternative way to create a view related to an action method, which will be covered later in this chapter.
In the view, you can display employee information or display an error message if an employee is not found.
Add the following code to the Show.aspx page:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" Inherits="System.Web.Mvc.ViewPage" %><asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <% if (ViewData["ErrorMessage"] != null) { %> <h1><%=ViewData["ErrorMessage"]%></h1> <% } else { %> <h1><%=ViewData["FirstName"]%> <%=ViewData["LastName"]%></h1> <p> E-mail: <%=ViewData["Email"]%> </p> <% } %>If the ViewData, set by the controller, is given an ErrorMessage, then the ErrorMessage is displayed on the resulting web page. Otherwise, the employee details are displayed.
</asp:Content>
Press the F5 button on your keyboard to start the development web server. Alter the URL in your browser to something ending in /Employee/Your_Name_Here, and see the action method and the view we've just created in action.
Strong-typed ViewData
In the previous example, we used the ViewData dictionary to pass data from the controller to the view. When developing the view, each dictionary item we want to display should be cast to the correct class, resulting in a less maintainable situation. It might also lead to code spaghetti in the view. It would be useful if the ViewData dictionary already knew which class type each of its items represented. This is where the model comes in handy! We are serving employee information to the view, so why not use the Employee class that we'd previously created as a "the" model for our view? Note that we'd already placed the Employee class inside the Model folder, which is the appropriate location for model classes.
Views can be made strong-typed by updating the view and replacing the base class of the page (System.Web.Mvc.ViewPage) with a generic version: System.Web.Mvc.ViewPage<Employee>. Make sure you compile your project after updating the first few lines of code in the Show.aspx file:
<%@ Page Title="" Language="C#"By applying the above code, the page's ViewData object will be made generic. This means that the ViewData object will not only be a dictionary, but will also contain a property named Model, which is of the type that has just been passed in: MvcApplication1.Models.Employee.
MasterPageFile="~/Views/Shared/Site.Master"
AutoEventWireup="true"
Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.Employee>" %>
This ViewData.Model property is also available as a Model property in the view. We will have to update the view to be able to use this new property. Simply change from ViewData[key] to a property Model (which contains the Employee instance). For example, Model.FirstName is in fact the FirstName property of the Employee instance that you want to render. Note that you can still use dictionary entries combined with this strong-typed model.
<%@ Page Title="" Language="C#"Before being able to run the application, the controller needs some updates as well. The main difference is that employee properties are no longer copied into the ViewData dictionary. Instead, the Employee instance is passed directly to the view.
MasterPageFile="~/Views/Shared/Site.Master"
AutoEventWireup="true"
Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.Employee>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<% if (ViewData["ErrorMessage"] != null) { %>
<h1><%=ViewData["ErrorMessage"]%></h1>
<% } else { %>
<h1><%=Model.FirstName%> <%=ViewData.Model.LastName%></h1>
<p>
E-mail: <%=Model.Email%>
</p>
<% } %>
</asp:Content>
using System.Web.Mvc;Note that we are passing the model data to the View() method. Alternatively, this can be done by stating Model = employee prior to returning the view. If you run the application, the result should be exactly the same as before.
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
public class EmployeeController : Controller {
public ActionResult Show(string firstname)
{
Employee employee = null;
if (string.IsNullOrEmpty(firstname))
{
ViewData["ErrorMessage"] = "No firstname provided!";
}
else {
employee = new Employee
{
FirstName = firstname,
LastName = "Example",
Email = firstname + "@example.com" };
}
return View(employee);
}
}
}
Creating a new view
During the development of the controller action method, creating a corresponding view is very straightforward. To create a new view for the current controller action, right-click somewhere on the method body, and select Add view... from the context menu. The following dialog box will be displayed:
In the Add view dialog box, some options can be specified. First of all, the view name can be modified if required. By default, this name will be the same as the action method name. It's also possible to select a view template, which we will set to Empty. This template can be used to easily create a view-for example, one which shows the details of an employee. You will see a little more about this in Chapter 4, Components in the ASP.NET MVC Framework.
From this dialog, it's also possible to make the view strongly-typed by simply selecting the corresponding checkbox and choosing the class to base the view on. The last option in this dialog box allows you to specify the master page.
Unit testing the controller
Unit testing is a software development process in which the smallest testable parts of an application, called units, are individually and independently tested for correct operation. Typically, these units are individual methods being tested. Most often, unit tests are run automatically, and provide immediate feedback (successful/unsuccessful/unknown result) to a developer on the changes he or she has just made to the code. If a test is unsuccessful, the changes to the code should be reviewed because the expected behavior of a portion of source code has changed and may affect other units or the application as a whole.
When we created the ASP.NET MVC web application, a test project was also created. This already contains an example test class for HomeController, testing both the Index and About actions.
In the MvcApplication1Tests project, right-click on the Controllers folder, and then select Add | Unit Test from the context menu. From the wizard that is displayed, select the Show method of EmployeeController and click on OK. Visual Studio will generate a test class.
Modify the generated test class to look like the following code:
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MvcApplication1.Controllers;namespace MvcApplication1Tests.Controllers
{
/// <summary> /// Summary description for EmployeeControllerTest /// </summary>[TestClass]Each test method is first initializing a new EmployeeController, after which the action method that needs to be tested is called. When the action method returns an ActionResult, it is cast to ViewResult on which some assertions are made. For example, if the Show action method of EmployeeController is called with parameter Maarten, an assertion is made that the controller passes the correct employee data to the view.
public class EmployeeControllerTest {
[TestMethod]
public void show_action_creates_employee_and_passes_to_view_when_firstname_is_specified()
{
// Setup EmployeeController controller = new EmployeeController();
// Execute ViewResult result = controller.Show("Maarten") as ViewResult;
// Verify Assert.IsNotNull(result);
ViewDataDictionary viewData = result.ViewData;
Assert.IsNotNull(viewData.Model);
Assert.AreEqual("Maarten", (viewData.Model as MvcApplication1.Models.Employee).FirstName);
Assert.IsNull(viewData["ErrorMessage"]);
}
[TestMethod]
public void show_action_passes_error_model_to_view_when_no_firstname_specified()
{
// Setup EmployeeController controller = new EmployeeController();
// Execute ViewResult result = controller.Show(null) as ViewResult;
// Verify Assert.IsNotNull(result);
ViewDataDictionary viewData = result.ViewData;
Assert.IsNull(viewData.Model);
Assert.IsNotNull(viewData["ErrorMessage"]);
}
}
}
This test suite does not require the application to be deployed on a web server. Instead, the EmployeeController is tested directly in the code. The test asserts that some properties of the ViewData are present. For example, if the Show() action is called with the parameter, Maarten, the Model should not be null and should contain an Employee with the first name, Maarten.
More advanced testing scenarios are explained in Chapter 9, Testing an Application.
Summary
In this chapter, we have seen an overview of all aspects of an ASP.NET MVC web application. We started by exploring the ASP.NET MVC web application project template that is installed in Visual Studio 2008, after which we created our own action method and corresponding view.
Another thing that we have seen is how to create a strong-typed view and how a controller action method can pass strong-typed ViewData to the view.
We ended this chapter by looking at the various aspects of unit testing-what it is, why it is used, how to create a unit test for an action method by using Visual Studio's unit test generation wizard, and modifying the unit test code by hand.
No comments:
Post a Comment