Anda di halaman 1dari 334

Intro to ASP.

NET MVC 3 (C#)


By Scott Hanselman|January 12, 2011 This tutorial will teach you the basics of building an ASP.NET MVC Web application using Microsoft Visual Web Developer 2010 Express Service Pack 1, which is a free version of Microsoft Visual Studio. Before you start, make sure you've installed the prerequisites listed below. You can install all of them by clicking the following link: Web Platform Installer. Alternatively, you can individually install the prerequisites using the following links: Visual Studio Web Developer Express SP1 prerequisites ASP.NET MVC 3 Tools Update SQL Server Compact 4.0 (runtime + tools support) If you're using Visual Studio 2010 instead of Visual Web Developer 2010, install the prerequisites by clicking the following link: Visual Studio 2010 prerequisites. A Visual Web Developer project with C# source code is available to accompany this topic. Download the C# version. If you prefer Visual Basic, switch to the Visual Basic version of this tutorial.

What You'll Build


You'll implement a simple movie-listing application that supports creating, editing, and listing movies from a database. Below are two screenshots of the application youll build. It includes a page that displays a list of movies from a database:

The application also lets you add, edit, and delete movies, as well as see details about individual ones. All data-entry scenarios include validation to ensure that the data stored in the database is correct.

Skills You'll Learn


Here's what you'll learn:

How to create a new ASP.NET MVC project. How to create ASP.NET MVC controllers and views. How to create a new database using the Entity Framework Code First paradigm. How to retrieve and display data. How to edit data and enable data validation.

Getting Started
Start by running Visual Web Developer 2010 Express ("Visual Web Developer" for short) and select New

Projectfrom the Start page.


Visual Web Developer is an IDE, or integrated development environment. Just like you use Microsoft Word to write documents, you'll use an IDE to create applications. In Visual Web Developer there's a toolbar along the top showing various options available to you. There's also a menu that provides another way to perform tasks in the IDE. (For example, instead of selecting New Project from the Start page, you can use the menu and select File >New Project.)

Creating Your First Application

You can create applications using either Visual Basic or Visual C# as the programming language. Select Visual C# on the left and then select ASP.NET MVC 3 Web Application. Name your project "MvcMovie" and then click OK. (If you prefer Visual Basic, switch to the Visual Basic version of this tutorial.)

In the New ASP.NET MVC 3 Project dialog box, select Internet Application. Check Use

HTML5 markup and leaveRazor as the default view engine.

Click OK. Visual Web Developer used a default template for the ASP.NET MVC project you just created, so you have a working application right now without doing anything! This is a simple "Hello World!" project, and it's a good place to start your application.

From the Debug menu, select Start Debugging.

Notice that the keyboard shortcut to start debugging is F5. F5 causes Visual Web Developer to start a development web server and run your web application. Visual Web Developer then launches a browser and opens the application's home page. Notice that the address bar of the browser says localhost and not something like example.com. That's because localhost always points to your own local computer, which in this case is running the application you just built. When Visual Web Developer runs a web project, a random port is used for the web server. In the image below, the random port number is 43246. When you run the application, you'll probably see a different port number.

Right out of the box this default template gives you two pages to visit and a basic login page. The next step is to change how this application works and learn a little bit about ASP.NET MVC in the process. Close your browser and let's change some code.

Adding a Controller (C#)


By Scott Hanselman|January 12, 2011

This tutorial will teach you the basics of building an ASP.NET MVC Web application using Microsoft Visual Web Developer 2010 Express Service Pack 1, which is a free version of Microsoft Visual Studio. Before you start, make sure you've installed the prerequisites listed below. You can install all of them by clicking the following link: Web Platform Installer. Alternatively, you can individually install the prerequisites using the following links: Visual Studio Web Developer Express SP1 prerequisites ASP.NET MVC 3 Tools Update SQL Server Compact 4.0 (runtime + tools support) If you're using Visual Studio 2010 instead of Visual Web Developer 2010, install the prerequisites by clicking the following link: Visual Studio 2010 prerequisites. A Visual Web Developer project with C# source code is available to accompany this topic. Download the C# version. If you prefer Visual Basic, switch to the Visual Basic version of this tutorial. MVC stands for model-view-controller. MVC is a pattern for developing applications that are well architected and easy to maintain. MVC-based applications contain: Controllers: Classes that handle incoming requests to the application, retrieve model data, and then specify view templates that return a response to the client. Models: Classes that represent the data of the application and that use validation logic to enforce business rules for that data. Views: Template files that your application uses to dynamically generate HTML responses.

We'll be covering all these concepts in this tutorial series and show you how to use them to build an application. Let's begin by creating a controller class. In Solution Explorer, right-click the Controllers folder and then select Add Controller.

Name your new controller "HelloWorldController". Leave the default template as Empty

controller and click Add.

Notice in Solution Explorer that a new file has been created named HelloWorldController.cs. The file is open in the IDE.

Inside the public class HelloWorldController block, create two methods that look like the following code. The controller will return a string of HTML as an example. using System.Web; using System.Web.Mvc; namespace MvcMovie.Controllers { public class HelloWorldController : Controller { // // GET: /HelloWorld/ public string Index()

{ return "This is my <b>default</b> action..."; } // // GET: /HelloWorld/Welcome/ public string Welcome() { return "This is the Welcome action method..."; } } } Your controller is named HelloWorldController and the first method above is named Index. Lets invoke it from a browser. Run the application (press F5 or Ctrl+F5). In the browser, append "HelloWorld" to the path in the address bar. (For example, in the illustration below, it's http://localhost:43246/HelloWorld.) The page in the browser will look like the following screenshot. In the method above, the code returned a string directly. You told the system to just return some HTML, and it did!

ASP.NET MVC invokes different controller classes (and different action methods within them) depending on the incoming URL. The default mapping logic used by ASP.NET MVC uses a format like this to determine what code to invoke:

/[Controller]/[ActionName]/[Parameters] The first part of the URL determines the controller class to execute. So /HelloWorld maps to theHelloWorldController class. The second part of the URL determines the action method on the class to execute. So /HelloWorld/Index would cause the Index method of the HelloWorldController class to execute. Notice that we only had to browse to /HelloWorld and the Index method was used by default. This is because a method namedIndex is the default method that will be called on a controller if one is not explicitly specified. Browse to http://localhost:xxxx/HelloWorld/Welcome. The Welcome method runs and returns the string "This is the Welcome action method...". The default MVC mapping is /[Controller]/[ActionName]/[Parameters]. For this URL, the controller is HelloWorld and Welcome is the action method. You haven't used the [Parameters] part of the URL yet.

Let's modify the example slightly so that you can pass some parameter information from the URL to the controller (for example, /HelloWorld/Welcome?name=Scott&numtimes=4). Change your Welcome method to include two parameters as shown below. Note that the code uses the C# optional-parameter feature to indicate that thenumTimes parameter should default to 1 if no value is passed for that parameter. public string Welcome(string name, int numTimes = 1) { return HttpUtility.HtmlEncode("Hello " + name + ", NumTimes is: " + numTimes); } Run your application and browse to the example URL (http://localhost:xxxx/HelloWorld/Welcome?name=Scott&numtimes=4). You can try different values

for name and numtimes in the URL. The system automatically maps the named parameters from the query string in the address bar to parameters in your method.

In both these examples the controller has been doing the "VC" portion of MVC that is, the view and controller work. The controller is returning HTML directly. Ordinarily you don't want controllers returning HTML directly, since that becomes very cumbersome to code. Instead we'll typically use a separate view template file to help generate the HTML response. Let's look next at how we can do this.

Adding a View (C#)


By Scott Hanselman|January 12, 2011 This tutorial will teach you the basics of building an ASP.NET MVC Web application using Microsoft Visual Web Developer 2010 Express Service Pack 1, which is a free version of Microsoft Visual Studio. Before you start, make sure you've installed the prerequisites listed below. You can install all of them by clicking the following link: Web Platform Installer. Alternatively, you can individually install the prerequisites using the following links: Visual Studio Web Developer Express SP1 prerequisites ASP.NET MVC 3 Tools Update SQL Server Compact 4.0 (runtime + tools support)

If you're using Visual Studio 2010 instead of Visual Web Developer 2010, install the prerequisites by clicking the following link: Visual Studio 2010 prerequisites. A Visual Web Developer project with C# source code is available to accompany this topic. Download the C# version. If you prefer Visual Basic, switch to the Visual Basic version of this tutorial. In this section you're going to modify the HelloWorldController class to use view template files to cleanly encapsulate the process of generating HTML responses to a client. You'll create a view template file using the new Razor view engine introduced with ASP.NET MVC 3. Razorbased view templates have a .cshtml file extension, and provide an elegant way to create HTML output using C#. Razor minimizes the number of characters and keystrokes required when writing a view template, and enables a fast, fluid coding workflow. Start by using a view template with the Index method in the HelloWorldController class. Currently the Indexmethod returns a string with a message that is hard-coded in the controller class. Change the Index method to return a View object, as shown in the following: public ActionResult Index() { return View(); } This code uses a view template to generate an HTML response to the browser. In the project, add a view template that you can use with the Index method. To do this, right-click inside the Index method and click Add View.

The Add View dialog box appears. Leave the defaults the way they are and click the Add button:

The MvcMovie\Views\HelloWorld folder and the MvcMovie\Views\HelloWorld\Index.cshtml file are created. You can see them in Solution Explorer:

The following shows the Index.cshtml file that was created:

Add some HTML under the <h2> tag. The modified MvcMovie\Views\HelloWorld\Index.cshtml file is shown below. @{ ViewBag.Title = "Index"; } <h2>Index</h2> <p>Hello from our View Template!</p> Run the application and browse to the HelloWorld controller (http://localhost:xxxx/HelloWorld). The Index method in your controller didn't do much work; it simply ran the statement return View(), which specified that the method should use a view template file to render a response to the browser. Because you didn't explicitly specify the name of the view template file to use, ASP.NET MVC defaulted to

using the Index.cshtml view file in the \Views\HelloWorldfolder. The image below shows the string hardcoded in the view.

Looks pretty good. However, notice that the browser's title bar says "Index" and the big title on the page says "My MVC Application." Let's change those.

Changing Views and Layout Pages


First, you want to change the "My MVC Application" title at the top of the page. That text is common to every page. It actually is implemented in only one place in the project, even though it appears on every page in the application. Go to the /Views/Shared folder in Solution Explorer and open the _Layout.cshtml file. This file is called a layout pageand it's the shared "shell" that all other pages use.

Layout templates allow you to specify the HTML container layout of your site in one place and then apply it across multiple pages in your site. Note the @RenderBody() line near the bottom of the file. RenderBody is a placeholder where all the view-specific pages you create show up, "wrapped" in the layout page. Change the title heading in the layout template from "My MVC Application" to "MVC Movie App".

<div id="title"> <h1>MVC Movie App</h1> </div> Run the application and notice that it now says "MVC Movie App". Click the About link, and you see how that page shows "MVC Movie App", too. We were able to make the change once in the layout template and have all pages on the site reflect the new title.

The complete _Layout.cshtml file is shown below: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>@ViewBag.Title</title> <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" /> <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script> </head> <body> <div class="page"> <header> <div id="title"> <h1>MVC Movie App</h1>

</div> <div id="logindisplay"> @Html.Partial("_LogOnPartial") </div> <nav> <ul id="menu"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("About", "About", "Home")</li> </ul> </nav> </header> <section id="main"> @RenderBody() </section> <footer> </footer> </div> </body> </html> Now, let's change the title of the Index page (view). Open MvcMovie\Views\HelloWorld\Index.cshtml. There are two places to make a change: first, the text that appears in the title of the browser, and then in the secondary header (the <h2> element). You'll make them slightly different so you can see which bit of code changes which part of the app. @{ ViewBag.Title = "Movie List"; } <h2>My Movie List</h2> <p>Hello from our View Template!</p> To indicate the HTML title to display, the code above sets a Title property of the ViewBag object (which is in theIndex.cshtml view template). If you look back at the source code of the layout template, youll notice that the template uses this value in the <title> element as part of the <head> section of the HTML. Using this approach, you can easily pass other parameters between your view template and your layout file. Run the application and browse to http://localhost:xx/HelloWorld. Notice that the browser title, the primary heading, and the secondary headings have changed. (If you don't see changes in the browser, you might be viewing cached content. Press Ctrl+F5 in your browser to force the response from the server to be loaded.) Also notice how the content in the Index.cshtml view template was merged with the _Layout.cshtml view template and a single HTML response was sent to the browser. Layout templates make it really easy to make changes that apply across all of the pages in your application.

Our little bit of "data" (in this case the "Hello from our View Template!" message) is hard-coded, though. The MVC application has a "V" (view) and you've got a "C" (controller), but no "M" (model) yet. Shortly, we'll walk through how create a database and retrieve model data from it.

Passing Data from the Controller to the View


Before we go to a database and talk about models, though, let's first talk about passing information from the controller to a view. Controller classes are invoked in response to an incoming URL request. A controller class is where you write the code that handles the incoming parameters, retrieves data from a database, and ultimately decides what type of response to send back to the browser. View templates can then be used from a controller to generate and format an HTML response to the browser. Controllers are responsible for providing whatever data or objects are required in order for a view template to render a response to the browser. A view template should never perform business logic or interact with a database directly. Instead, it should work only with the data that's provided to it by the controller. Maintaining this "separation of concerns" helps keep your code clean and more maintainable. Currently, the Welcome action method in the HelloWorldController class takes a name and a numTimes parameter and then outputs the values directly to the browser. Rather than have the controller render this response as a string, lets change the controller to use a view template instead. The view template will generate a dynamic response, which means that you need to pass appropriate bits of data from the controller to the view in order to generate the response. You can do this by having the

controller put the dynamic data that the view template needs in a ViewBag object that the view template can then access. Return to the HelloWorldController.cs file and change the Welcome method to add a Message and NumTimes value to the ViewBag object. ViewBag is a dynamic object, which means you can put whatever you want in to it; the ViewBagobject has no defined properties until you put something inside it. The complete HelloWorldController.cs file looks like this: using System.Web; using System.Web.Mvc; namespace MvcMovie.Controllers { public class HelloWorldController : Controller { public ActionResult Index() { return View(); } public ActionResult Welcome(string name, int numTimes = 1) { ViewBag.Message = "Hello " + name; ViewBag.NumTimes = numTimes; return View(); } } } Now the ViewBag object contains data that will be passed to the view automatically. Next, you need a Welcome view template! In the Debug menu, select Build MvcMovie to make sure the project is compiled.

Then right-click inside the Welcome method and click Add View. Here's what the Add View dialog box looks like:

Click Add, and then add the following code under the <h2> element in the new Welcome.cshtml file. You'll create a loop that says "Hello" as many times as the user says it should. The complete Welcome.cshtml file is shown below. @{ ViewBag.Title = "Welcome"; } <h2>Welcome</h2> <ul> @for (int i=0; i < ViewBag.NumTimes; i++) { <li>@ViewBag.Message</li> } </ul> Run the application and browse to the following URL:

http://localhost:xx/HelloWorld/Welcome?name=Scott&numtimes=4 Now data is taken from the URL and passed to the controller automatically. The controller packages the data into aViewBag object and passes that object to the view. The view then displays the data as HTML to the user.

Well, that was a kind of an "M" for model, but not the database kind. Let's take what we've learned and create a database of movies.

Adding a Model (C#)


By Scott Hanselman|January 12, 2011 This tutorial will teach you the basics of building an ASP.NET MVC Web application using Microsoft Visual Web Developer 2010 Express Service Pack 1, which is a free version of Microsoft Visual Studio. Before you start, make sure you've installed the prerequisites listed below. You can install all of them by clicking the following link: Web Platform Installer. Alternatively, you can individually install the prerequisites using the following links: Visual Studio Web Developer Express SP1 prerequisites ASP.NET MVC 3 Tools Update SQL Server Compact 4.0 (runtime + tools support)

If you're using Visual Studio 2010 instead of Visual Web Developer 2010, install the prerequisites by clicking the following link: Visual Studio 2010 prerequisites. A Visual Web Developer project with C# source code is available to accompany this topic. Download the C# version. If you prefer Visual Basic, switch to the Visual Basic version of this tutorial.

Adding a Model
In this section you'll add some classes for managing movies in a database. These classes will be the "model" part of the ASP.NET MVC application. Youll use a .NET Framework data-access technology known as the Entity Framework to define and work with these model classes. The Entity Framework (often referred to as EF) supports a development paradigm called Code First. Code First allows you to create model objects by writing simple classes. (These are also known as POCO classes, from "plain-old CLR objects.") You can then have the database created on the fly from your classes, which enables a very clean and rapid development workflow.

Adding Model Classes


In Solution Explorer, right click the Models folder, select Add, and then select Class.

Name the class "Movie".

Add the following five properties to the Movie class: public class Movie { public int ID { get; set; } public string Title { get; set; } public DateTime ReleaseDate { get; set; } public string Genre { get; set; } public decimal Price { get; set; } }

We'll use the Movie class to represent movies in a database. Each instance of a Movie object will correspond to a row within a database table, and each property of the Movie class will map to a column in the table. In the same file, add the following MovieDBContext class: public class MovieDBContext : DbContext { public DbSet<Movie> Movies { get; set; } } The MovieDBContext class represents the Entity Framework movie database context, which handles fetching, storing, and updating Movie class instances in a database. The MovieDBContext derives from the DbContext base class provided by the Entity Framework. For more information about DbContext and DbSet, see Productivity Improvements for the Entity Framework. In order to be able to reference DbContext and DbSet, you need to add the following using statement at the top of the file: using System.Data.Entity; The complete Movie.cs file is shown below. using System; using System.Data.Entity; namespace MvcMovie.Models { public class Movie { public int ID { get; set; } public string Title { get; set; } set; } public string Genre { get; set; } public decimal Price { get; set; } } public class MovieDBContext : DbContext { public DbSet<Movie> Movies { get; set; } } }

public DateTime ReleaseDate { get;

Creating a Connection String and Working with SQL Server Compact


The MovieDBContext class you created handles the task of connecting to the database and mapping Movieobjects to database records. One question you might ask, though, is how to specify which database it will connect to. You'll do that by adding connection information in the Web.config file of the application. Open the application root Web.config file. (Not the Web.config file in the Views folder.) The image below show bothWeb.config files; open the Web.config file circled in red.

Add the following connection string to the <connectionStrings> element in the Web.config file. <add name="MovieDBContext" connectionString="Data Source=|DataDirectory|Movies.sdf" providerName="System.Data.SqlServerCe.4.0"/> The following example shows a portion of the Web.config file with the new connection string added: <configuration> <connectionStrings> <add name="MovieDBContext" connectionString="Data Source=|DataDirectory|Movies.sdf" providerName="System.Data.SqlServerCe.4.0"/> <add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient" /> </connectionStrings> This small amount of code and XML is everything you need to write in order to represent and store the movie data in a database. Next, you'll build a new MoviesController class that you can use to display the movie data and allow users to create new movie listings.

Accessing your Model's Data from a Controller (C#)


By Scott Hanselman|January 12, 2011 This tutorial will teach you the basics of building an ASP.NET MVC Web application using Microsoft Visual Web Developer 2010 Express Service Pack 1, which is a free version of Microsoft Visual Studio. Before you start, make sure you've installed the prerequisites listed below. You can install all of them by clicking the following link: Web Platform Installer. Alternatively, you can individually install the prerequisites using the following links: Visual Studio Web Developer Express SP1 prerequisites ASP.NET MVC 3 Tools Update SQL Server Compact 4.0 (runtime + tools support) If you're using Visual Studio 2010 instead of Visual Web Developer 2010, install the prerequisites by clicking the following link: Visual Studio 2010 prerequisites. A Visual Web Developer project with C# source code is available to accompany this topic. Download the C# version. If you prefer Visual Basic, switch to the Visual Basic version of this tutorial. In this section, you'll create a new MoviesController class and write code that retrieves the movie data and displays it in the browser using a view template. Be sure to build your application before proceeding. Right-click the Controllers folder and create a new MoviesController controller. Select the following options: Controller name: MoviesController. (This is the default. ) Template: Controller with read/write actions and views, using Entity Framework. Model class: Movie (MvcMovie.Models). Data context class: MovieDBContext (MvcMovie.Models). Views: Razor (CSHTML). (The default.)

Click Add. Visual Web Developer creates the following files and folders: A MoviesController.cs file in the project's Controllers folder. A Movies folder in the project's Views folder. Create.cshtml, Delete.cshtml, Details.cshtml, Edit.cshtml, and Index.cshtml in the new Views\Movies folder.

The ASP.NET MVC 3 scaffolding mechanism automatically created the CRUD (create, read, update, and delete) action methods and views for you. You now have a fully functional web application that lets you create, list, edit, and delete movie entries. Run the application and browse to the Movies controller by appending /Movies to the URL in the address bar of your browser. Because the application is relying on the default routing (defined in the Global.asax file), the browser request http://localhost:xxxxx/Movies is routed to the default Index action method of the Movies controller. In other words, the browser request http://localhost:xxxxx/Movies is effectively the same as the browser requesthttp://localhost:xxxxx/Movies/Index. The result is an empty list of movies, because you haven't added any yet.

Creating a Movie
Select the Create New link. Enter some details about a movie and then click the Create button.

Clicking the Create button causes the form to be posted to the server, where the movie information is saved in the database. You're then redirected to the /Movies URL, where you can see the newly created movie in the listing.

Create a couple more movie entries. Try the Edit, Details, and Delete links, which are all functional.

Examining the Generated Code


Open the Controllers\MoviesController.cs file and examine the generated Index method. A portion of the movie controller with the Index method is shown below. public class MoviesController : Controller { private MovieDBContext db = new MovieDBContext(); // // GET: /Movies/ public ViewResult Index() { return View(db.Movies.ToList()); } The following line from the MoviesController class instantiates a movie database context, as described previously. You can use the movie database context to query, edit, and delete movies.

private MovieDBContext db = new MovieDBContext(); A request to the Movies controller returns all the entries in the Movies table of the movie database and then passes the results to the Index view.

Strongly Typed Models and the @model Keyword


Earlier in this tutorial, you saw how a controller can pass data or objects to a view template using the ViewBagobject. The ViewBag is a dynamic object that provides a convenient late-bound way to pass information to a view. ASP.NET MVC also provides the ability to pass strongly typed data or objects to a view template. This strongly typed approach enables better compile-time checking of your code and richer IntelliSense in the Visual Web Developer editor. We're using this approach with the MoviesController class and Index.cshtml view template. Notice how the code creates a List object when it calls the View helper method in the Index action method. The code then passes this Movies list from the controller to the view: public ViewResult Index() { return View(db.Movies.ToList()); } By including a @model statement at the top of the view template file, you can specify the type of object that the view expects. When you created the movie controller, Visual Web Developer automatically included the following @modelstatement at the top of the Index.cshtml file: @model IEnumerable<MvcMovie.Models.Movie> This @model directive allows you to access the list of movies that the controller passed to the view by using a Modelobject that's strongly typed. For example, in the Index.cshtml template, the code loops through the movies by doing a foreach statement over the strongly typed Model object: @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.ReleaseDate) </td> <td> @Html.DisplayFor(modelItem => item.Genre) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> <td> @Html.ActionLink("Edit", "Edit", new { id=item.ID }) | @Html.ActionLink("Details", "Details", new { id=item.ID }) | @Html.ActionLink("Delete", "Delete", new { id=item.ID })

</td> </tr> } Because the Model object is strongly typed (as an IEnumerable<Movie> object), each item object in the loop is typed as Movie. Among other benefits, this means that you get compile-time checking of the code and full IntelliSense support in the code editor:

Working with SQL Server Compact

Entity Framework Code First detected that the database connection string that was provided pointed to a Moviesdatabase that didnt exist yet, so Code First created the database automatically. You can verify that it's been created by looking in the App_Data folder. If you don't see the Movies.sdf file, click the Show All Files button in the Solution Explorer toolbar, click the Refresh button, and then expand the App_Data folder.

Double-click Movies.sdf to open Server Explorer. Then expand the Tables folder to see the tables that have been created in the database. Note If you get an error when you double-click Movies.sdf, make sure you've installed SQL Server Compact 4.0 (runtime + tools support). (For links to the software, see the list of prerequisites in part 1 of this tutorial series.) If you install the release now, you'll have to close and re-open Visual Web Developer.

There are two tables, one for the Movie entity set and then the EdmMetadata table. The EdmMetadata table is used by the Entity Framework to determine when the model and the database are out of sync. Right-click the Movies table and select Show Table Data to see the data you created.

Right-click the Movies table and select Edit Table Schema.

Notice how the schema of the Movies table maps to the Movie class you created earlier. Entity Framework Code First automatically created this schema for you based on your Movie class. When you're finished, close the connection. (If you don't close the connection, you might get an error the next time you run the project).

You now have the database and a simple listing page to display content from it. In the next tutorial, we'll examine the rest of the scaffolded code and add a SearchIndex method and a SearchIndex view that lets you search for movies in this database.

Examining the Edit Methods and Edit View (C#)


By Scott Hanselman|January 12, 2011 This tutorial will teach you the basics of building an ASP.NET MVC Web application using Microsoft Visual Web Developer 2010 Express Service Pack 1, which is a free version of Microsoft Visual Studio. Before you start, make sure you've installed the prerequisites listed below. You can install all of them by clicking the following link: Web Platform Installer. Alternatively, you can individually install the prerequisites using the following links: Visual Studio Web Developer Express SP1 prerequisites ASP.NET MVC 3 Tools Update SQL Server Compact 4.0 (runtime + tools support) If you're using Visual Studio 2010 instead of Visual Web Developer 2010, install the prerequisites by clicking the following link: Visual Studio 2010 prerequisites. A Visual Web Developer project with C# source code is available to accompany this topic. Download the C# version. If you prefer Visual Basic, switch to the Visual Basic version of this tutorial. In this section, you'll examine the generated action methods and views for the movie controller. Then you'll add a custom search page.

Run the application and browse to the Movies controller by appending /Movies to the URL in the address bar of your browser. Hold the mouse pointer over an Edit link to see the URL that it links to.

The Edit link was generated by the Html.ActionLink method in the Views\Movies\Index.cshtml view: @Html.ActionLink("Edit", "Edit", new { id=item.ID })

The Html object is a helper that's exposed using a property on the WebViewPage base class. The ActionLinkmethod of the helper makes it easy to dynamically generate HTML hyperlinks that link to action methods on controllers. The first argument to the ActionLink method is the link text to render (for example, <a>Edit Me</a>). The second argument is the name of the action method to invoke. The final argument is an anonymous object that generates the route data (in this case, the ID of 4). The generated link shown in the previous image is http://localhost:xxxxx/Movies/Edit/4. The default route takes the URL pattern {controller}/{action}/{id}. Therefore, ASP.NET

translates http://localhost:xxxxx/Movies/Edit/4 into a request to the Edit action method of the Movies controller with the parameter ID equal to 4. You can also pass action method parameters using a query string. For example, the URLhttp://localhost:xxxxx/Movies/Edit?ID=4 also passes the parameter ID of 4 to the Edit action method of the Moviescontroller.

Open the Movies controller. The two Edit action methods are shown below. // // GET: /Movies/Edit/5 public ActionResult Edit(int id) {

Movie movie = db.Movies.Find(id); return View(movie); } // // POST: /Movies/Edit/5 [HttpPost] public ActionResult Edit(Movie movie) { if (ModelState.IsValid) { db.Entry(movie).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } return View(movie); } Notice the second Edit action method is preceded by the HttpPost attribute. This attribute specifies that that overload of the Edit method can be invoked only for POST requests. You could apply the HttpGet attribute to the first edit method, but that's not necessary because it's the default. (We'll refer to action methods that are implicitly assigned the HttpGet attribute as HttpGet methods.) The HttpGet Edit method takes the movie ID parameter, looks up the movie using the Entity Framework Findmethod, and returns the selected movie to the Edit view. When the scaffolding system created the Edit view, it examined the Movie class and created code to render <label> and <input> elements for each property of the class. The following example shows the Edit view that was generated: @model MvcMovie.Models.Movie @{ ViewBag.Title = "Edit"; } <h2>Edit</h2> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>Movie</legend> @Html.HiddenFor(model => model.ID)

<div class="editor-label"> @Html.LabelFor(model => model.Title) </div> <div class="editor-field"> @Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title) </div> <div class="editor-label"> @Html.LabelFor(model => model.ReleaseDate) </div> <div class="editor-field"> @Html.EditorFor(model => model.ReleaseDate) @Html.ValidationMessageFor(model => model.ReleaseDate) </div> <div class="editor-label"> @Html.LabelFor(model => model.Genre) </div> <div class="editor-field"> @Html.EditorFor(model => model.Genre) @Html.ValidationMessageFor(model => model.Genre) </div> <div class="editor-label"> @Html.LabelFor(model => model.Price) </div> <div class="editor-field"> @Html.EditorFor(model => model.Price) @Html.ValidationMessageFor(model => model.Price) </div> <p> <input type="submit" value="Save" /> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index") </div> Notice how the view template has a @model MvcMovie.Models.Movie statement at the top of the file this specifies that the view expects the model for the view template to be of type Movie. The scaffolded code uses several helper methods to streamline the HTML markup. The Html.LabelFor helper displays the name of the field ("Title", "ReleaseDate", "Genre", or "Price").

The Html.EditorFor helper displays an HTML <input> element. The Html.ValidationMessageFor helper displays any validation messages associated with that property. Run the application and navigate to the /Movies URL. Click an Edit link. In the browser, view the source for the page. The HTML in the page looks like the following example. (The menu markup was excluded for clarity.) <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Edit</title> <link href="/Content/Site.css" rel="stylesheet" type="text/css" /> <script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script> <script src="/Scripts/modernizr-1.7.min.js" type="text/javascript"></script> </head> <body> <div class="page"> <header> <div id="title"> <h1>MVC Movie App</h1> </div> ... </header> <section id="main">

<h2>Edit</h2> <script src="/Scripts/jquery.validate.min.js" type="text/javascript"></script> <script src="/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script> <form action="/Movies/Edit/4" method="post"> <legend>Movie</legend> <fieldset>

<input data-val="true" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="4" /> <div class="editor-label"> <label for="Title">Title</label> </div> <div class="editor-field"> <input class="text-box single-line" id="Title" name="Title" type="text" value="Rio Bravo" /> <span class="field-validation-valid" data-valmsg-for="Title" data-valmsgreplace="true"></span> </div>

<div class="editor-label"> <label for="ReleaseDate">ReleaseDate</label> </div> <div class="editor-field"> <input class="text-box single-line" data-val="true" data-valrequired="The ReleaseDate field is required." id="ReleaseDate" name="ReleaseDate" type="text" value="4/15/1959 12:00:00 AM" /> <span class="field-validation-valid" data-valmsg-for="ReleaseDate" datavalmsg-replace="true"></span> </div> <div class="editor-label"> <label for="Genre">Genre</label> </div> <div class="editor-field"> <input class="text-box single-line" id="Genre" name="Genre" type="text" value="Western" /> <span class="field-validation-valid" data-valmsg-for="Genre" data-valmsgreplace="true"></span> </div> <div class="editor-label"> <label for="Price">Price</label> </div> <div class="editor-field"> <input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number." data-val-required="The Price field is required." id="Price" name="Price" type="text" value="9.99" /> <span class="field-validation-valid" data-valmsg-for="Price" data-valmsgreplace="true"></span> </div> <p> <input type="submit" value="Save" /> </p> </fieldset> </form> <div> <a href="/Movies">Back to List</a> </div> </section> <footer> </footer>

</div> </body> </html> The <input> elements are in an HTML <form> element whose action attribute is set to post to the /Movies/EditURL. The form data will be posted to the server when the Edit button is clicked.

Processing the POST Request


The following listing shows the HttpPost version of the Edit action method. [HttpPost] public ActionResult Edit(Movie movie) { if (ModelState.IsValid) { db.Entry(movie).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } return View(movie); } The ASP.NET framework model binder takes the posted form values and creates a Movie object that's passed as the movie parameter. The ModelState.IsValid check in the code verifies that the data submitted in the form can be used to modify a Movie object. If the data is valid, the code saves the movie data to the Movies collection of theMovieDBContext instance. The code then saves the new movie data to the database by calling the SaveChangesmethod of MovieDBContext, which persists changes to the database. After saving the data, the code redirects the user to the Index action method of the MoviesController class, which causes the updated movie to be displayed in the listing of movies. If the posted values aren't valid, they are redisplayed in the form. The Html.ValidationMessageFor helpers in theEdit.cshtml view template take care of displaying appropriate error messages.

Note about locales If you normally work with a locale other than English, see Supporting ASP.NET MVC 3 Validation with Non-English Locales.

Making the Edit Method More Robust


The HttpGet Edit method generated by the scaffolding system doesn't check that the ID that's passed to it is valid. If a user removes the ID segment from the URL (http://localhost:xxxxx/Movies/Edit), the following error is displayed:

A user could also pass an ID that doesn't exist in the database, such as http://localhost:xxxxx/Movies/Edit/1234. You can make two changes to the HttpGet Edit action method to address this limitation. First, change the IDparameter to have a default value of zero when an ID isn't explicitly passed. You can also check that the Findmethod actually found a movie before returning the movie object to the view template. The updated Edit method is shown below. public ActionResult Edit(int id = 0) { Movie movie = db.Movies.Find(id); if (movie == null) { return HttpNotFound(); }

return View(movie); } If no movie is found, the HttpNotFound method is called. All the HttpGet methods follow a similar pattern. They get a movie object (or list of objects, in the case of Index), and pass the model to the view. The Create method passes an empty movie object to the Create view. All the methods that create, edit, delete, or otherwise modify data do so in the HttpPost overload of the method. Modifying data in an HTTP GET method is a security risk, as described in the blog post entry ASP.NET MVC Tip #46 Dont use Delete Links because they create Security Holes. Modifying data in a GET method also violates HTTP best practices and the architectural REST pattern, which specifies that GET requests should not change the state of your application. In other words, performing a GET operation should be a safe operation that has no side effects.

Adding a Search Method and Search View


In this section you'll add a SearchIndex action method that lets you search movies by genre or name. This will be available using the /Movies/SearchIndex URL. The request will display an HTML form that contains input elements that a user can fill in in order to search for a movie. When a user submits the form, the action method will get the search values posted by the user and use the values to search the database.

Displaying the SearchIndex Form


Start by adding a SearchIndex action method to the existing MoviesController class. The method will return a view that contains an HTML form. Here's the code: public ActionResult SearchIndex(string searchString) { var movies = from m in db.Movies select m; if (!String.IsNullOrEmpty(searchString)) { movies = movies.Where(s => s.Title.Contains(searchString)); } return View(movies); } The first line of the SearchIndex method creates the following LINQ query to select the movies: var movies = from m in db.Movies select m; The query is defined at this point, but hasn't yet been run against the data store. If the searchString parameter contains a string, the movies query is modified to filter on the value of the search string, using the following code: if (!String.IsNullOrEmpty(searchString)) {

movies = movies.Where(s => s.Title.Contains(searchString)); } LINQ queries are not executed when they are defined or when they are modified by calling a method such as Whereor OrderBy. Instead, query execution is deferred, which means that the evaluation of an expression is delayed until its realized value is actually iterated over or the ToList method is called. In the SearchIndex sample, the query is executed in the SearchIndex view. For more information about deferred query execution, see Query Execution. Now you can implement the SearchIndex view that will display the form to the user. Right-click inside theSearchIndex method and then click Add View. In the Add View dialog box, specify that you're going to pass aMovie object to the view template as its model class. In the Scaffold template list, choose List, then click Add.

When you click the Add button, the Views\Movies\SearchIndex.cshtml view template is created. Because you selectedList in the Scaffold template list, Visual Web Developer automatically generated (scaffolded) some default content in the view. The scaffolding created an HTML form. It examined the Movie class and created code to render<label> elements for each property of the class. The listing below shows the Create view that was generated: @model IEnumerable<MvcMovie.Models.Movie>

@{ ViewBag.Title = "SearchIndex"; } <h2>SearchIndex</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th> Title </th> <th> ReleaseDate </th> <th> Genre </th> <th> Price </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.ReleaseDate) </td> <td> @Html.DisplayFor(modelItem => item.Genre) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> <td> @Html.ActionLink("Edit", "Edit", new { id=item.ID }) | @Html.ActionLink("Details", "Details", new { id=item.ID }) | @Html.ActionLink("Delete", "Delete", new { id=item.ID }) </td> </tr> }

</table> Run the application and navigate to /Movies/SearchIndex. Append a query string such as ?searchString=ghost to the URL. The filtered movies are displayed.

If you change the signature of the SearchIndex method to have a parameter named id, {controller}/{action}/{id} The modified SearchIndex method would look as follows: public ActionResult SearchIndex(string id) { string searchString = id; var movies = from m in db.Movies select m; if (!String.IsNullOrEmpty(searchString)) {

the id parameter will match the {id} placeholder for the default routes set in the Global.asax file.

movies = movies.Where(s => s.Title.Contains(searchString)); } return View(movies); } You can now pass the search title as route data (a URL segment) instead of as a query string value.

However, you can't expect users to modify the URL every time they want to search for a movie. So now you you'll add UI to help them filter movies. If you changed the signature of the SearchIndex method to test how to pass the route-bound ID parameter, change it back so that your SearchIndex method takes a string parameter namedsearchString: public ActionResult SearchIndex(string searchString) { var movies = from m in db.Movies select m;

if (!String.IsNullOrEmpty(searchString)) { movies = movies.Where(s => s.Title.Contains(searchString)); } return View(movies); } Open the Views\Movies\SearchIndex.cshtml file, and just after @Html.ActionLink("Create New", "Create"), add the following: @using (Html.BeginForm()){ <p> Title: @Html.TextBox("SearchString") <input type="submit" value="Filter" /></p> } The following example shows a portion of the Views\Movies\SearchIndex.cshtml file with the added filtering markup. @model IEnumerable<MvcMovie.Models.Movie> @{ ViewBag.Title = "SearchIndex"; } <h2>SearchIndex</h2> <p> @Html.ActionLink("Create New", "Create") @using (Html.BeginForm()){ <p> Title: @Html.TextBox("SearchString") <br /> <input type="submit" value="Filter" /></p> } </p> The Html.BeginForm helper creates an opening <form> tag. The Html.BeginForm helper causes the form to post to itself when the user submits the form by clicking the Filter button. Run the application and try searching for a movie.

There's no HttpPost overload of the SearchIndex method. You don't need it, because the method isn't changing the state of the application, just filtering data. You could add the following HttpPost SearchIndex method. In that case, the action invoker would match theHttpPost SearchIndex method, and the HttpPost SearchIndex method would run as shown in the image below. [HttpPost] public string SearchIndex(FormCollection fc, string searchString) { return "<h3> From [HttpPost]SearchIndex: " + searchString + "</h3>"; }

However, even if you add this HttpPost version of the SearchIndex method, there's a limitation in how this has all been implemented. Imagine that you want to bookmark a particular search or you want to send a link to friends that they can click in order to see the same filtered list of movies. Notice that the URL for the HTTP POST request is the same as the URL for the GET request (localhost:xxxxx/Movies/SearchIndex) -- there's no search information in the URL itself. Right now, the search string information is sent to the server as a form field value. This means you can't capture that search information to bookmark or send to friends in a URL. The solution is to use an overload of BeginForm that specifies that the POST request should add the search information to the URL and that is should be routed to the HttpGet version of the SearchIndex method. Replace the existing parameterless BeginForm method with the following: @using (Html.BeginForm("SearchIndex","Movies",FormMethod.Get))

Now when you submit a search, the URL contains a search query string. Searching will also go to the HttpGet SearchIndex action method, even if you have a HttpPost SearchIndex method.

Adding Search by Genre


If you added the HttpPost version of the SearchIndex method, delete it now. Next, you'll add a feature to let users search for movies by genre. Replace the SearchIndex method with the following code: public ActionResult SearchIndex(string movieGenre, string searchString) { var GenreLst = new List<string>(); var GenreQry = from d in db.Movies orderby d.Genre select d.Genre; GenreLst.AddRange(GenreQry.Distinct()); ViewBag.movieGenre = new SelectList(GenreLst);

var movies = from m in db.Movies select m; if (!String.IsNullOrEmpty(searchString)) { movies = movies.Where(s => s.Title.Contains(searchString)); } if (string.IsNullOrEmpty(movieGenre)) return View(movies); else { return View(movies.Where(x => x.Genre == movieGenre)); } } This version of the SearchIndex method takes an additional parameter, namely movieGenre. The first few lines of code create a List object to hold movie genres from the database. The following code is a LINQ query that retrieves all the genres from the database. var GenreQry = from d in db.Movies orderby d.Genre select d.Genre; The code uses the AddRange method of the generic List collection to add all the distinct genres to the list. (Without the Distinct modifier, duplicate genres would be added for example, comedy would be added twice in our sample). The code then stores the list of genres in the ViewBag object. The following code shows how to check the movieGenre parameter. If it's not empty the code further constrains the movies query to limit the selected movies to the specified genre. if (string.IsNullOrEmpty(movieGenre)) return View(movies); else { return View(movies.Where(x => x.Genre == movieGenre)); }

Adding Markup to the SearchIndex View to Support Search by Genre


Add an Html.DropDownList helper to the Views\Movies\SearchIndex.cshtml file, just before the TextBox helper. The completed markup is shown below: <p> @Html.ActionLink("Create New", "Create") @using (Html.BeginForm()){

<p>Genre: @Html.DropDownList("movieGenre", "All") Title: @Html.TextBox("SearchString") <input type="submit" value="Filter" /></p> } </p> Run the application and browse to /Movies/SearchIndex. Try a search by genre, by movie name, and by both criteria. In this section you examined the CRUD action methods and views generated by the framework. You created a search action method and view that let users search by movie title and genre. In the next section, you'll look at how to add a property to the Movie model and how to add an initializer that will automatically create a test database.

Adding a New Field to the Movie Model and Table(C#)


By Scott Hanselman|January 12, 2011 This tutorial will teach you the basics of building an ASP.NET MVC Web application using Microsoft Visual Web Developer 2010 Express Service Pack 1, which is a free version of Microsoft Visual Studio. Before you start, make sure you've installed the prerequisites listed below. You can install all of them by clicking the following link: Web Platform Installer. Alternatively, you can individually install the prerequisites using the following links: Visual Studio Web Developer Express SP1 prerequisites ASP.NET MVC 3 Tools Update SQL Server Compact 4.0 (runtime + tools support) If you're using Visual Studio 2010 instead of Visual Web Developer 2010, install the prerequisites by clicking the following link: Visual Studio 2010 prerequisites. A Visual Web Developer project with C# source code is available to accompany this topic. Download the C# version. If you prefer Visual Basic, switch to the Visual Basic version of this tutorial. In this section you'll make some changes to the model classes and learn how you can update the database schema to match the model changes.

Adding a Rating Property to the Movie Model


Start by adding a new Rating property to the existing Movie class. Open the Movie.cs file and add the Ratingproperty like this one: public string Rating { get; set; } The complete Movie class now looks like the following code: public class Movie {

public public public public public public }

int string DateTime string decimal string

ID Title ReleaseDate Genre Price Rating

{ { { { { {

get; get; get; get; get; get;

set; set; set; set; set; set;

} } } } } }

Recompile the application using the Debug > Build Movie menu command. Now that you've updated the Model class, you also need to update the \Views\Movies\Index.cshtml and\Views\Movies\Create.cshtml view templates in order to support the new Rating property. Open the \Views\Movies\Index.cshtml file and add a <th>Rating</th> column heading just after the Price column. Then add a <td> column near the end of the template to render the @item.Rating value. Below is what the updated Index.cshtml view template looks like: <table> <tr> <th></th> <th>Title</th> <th>Release Date</th> <th>Genre</th> <th>Price</th> <th>Rating</th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.ReleaseDate) </td> <td> @Html.DisplayFor(modelItem => item.Genre) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> <td> @Html.DisplayFor(modelItem => item.Rating ) </td> <td> @Html.ActionLink("Edit Me", "Edit", new { id=item.ID }) | @Html.ActionLink("Details", "Details", new { id=item.ID }) | @Html.ActionLink("Delete", "Delete", new { id=item.ID }) </td>

</tr> } </table> Next, open the \Views\Movies\Create.cshtml file and add the following markup near the end of the form. This renders a text box so that you can specify a rating when a new movie is created. <div class="editor-label"> @Html.LabelFor(model => model.Rating) </div> <div class="editor-field"> @Html.EditorFor(model => model.Rating) @Html.ValidationMessageFor(model => model.Rating) </div>

Managing Model and Database Schema Differences


You've now updated the application code to support the new Rating property. Now run the application and navigate to the /Movies URL. When you do this, though, you'll see the following error:

You're seeing this error because the updated Movie model class in the application is now different than the schema of the Movie table of the existing database. (There's no Rating column in the database table.) By default, when you use Entity Framework Code First to automatically create a database, as you did earlier in this tutorial, Code First adds a table to the database to help track whether the schema of the database is in sync with the model classes it was generated from. If they aren't in sync, the Entity Framework throws an error. This makes it easier to track down issues at development time that you might otherwise only find (by obscure errors) at run time. The sync-checking feature is what causes the error message to be displayed that you just saw.

There are two approaches to resolving the error: 1. Have the Entity Framework automatically drop and re-create the database based on the new model class schema. This approach is very convenient when doing active development on a test database, because it allows you to quickly evolve the model and database schema together. The downside, though, is that you lose existing data in the database so you don't want to use this approach on a production database! Explicitly modify the schema of the existing database so that it matches the model classes. The advantage of this approach is that you keep your data. You can make this change either manually or by creating a database change script.

2.

For this tutorial, we'll use the first approach you'll have the Entity Framework Code First automatically re-create the database anytime the model changes.

Automatically Re-Creating the Database on Model Changes


Let's update the application so that Code First automatically drops and re-creates the database anytime you change the model for the application. Warning You should enable this approach of automatically dropping and re-creating the database only when you're using a development or test database, and never on a production database that contains real data. Using it on a production server can lead to data loss. In Solution Explorer, right click the Models folder, select Add, and then select Class.

Name the class "MovieInitializer". Update the MovieInitializer class to contain the following code: using System; using System.Collections.Generic; using System.Data.Entity; namespace MvcMovie.Models { public class MovieInitializer : DropCreateDatabaseIfModelChanges<MovieDBContext> { protected override void Seed(MovieDBContext context) { var movies = new List<Movie> { new Movie { Title = "When Harry Met Sally", ReleaseDate=DateTime.Parse("1989-1-11"), Genre="Romantic Comedy", Rating="R", Price=7.99M}, new Movie { Title = "Ghostbusters ", ReleaseDate=DateTime.Parse("1984-3-13"), Genre="Comedy", Rating="R", Price=8.99M}, new Movie { Title = "Ghostbusters 2", ReleaseDate=DateTime.Parse("1986-2-23"), Genre="Comedy", Rating="R", Price=9.99M}, new Movie { Title = "Rio Bravo", ReleaseDate=DateTime.Parse("1959-4-15"), Genre="Western", Rating="R", Price=3.99M}, }; movies.ForEach(d => context.Movies.Add(d)); } } } The MovieInitializer class specifies that the database used by the model should be dropped and automatically re-created if the model classes ever change. The code includes a Seed method to specify some default data to automatically add to the database any time it's created (or re-created). This provides a useful way to populate the database with some sample data, without requiring you to manually populate it each time you make a model change. Now that you've defined the MovieInitializer class, you'll want to wire it up so that each time the application runs, it checks whether the model classes are different from the schema in the database. If

they are, you can run the initializer to re-create the database to match the model and then populate the database with the sample data. Open the Global.asax file that's at the root of the MvcMovies project:

The Global.asax file contains the class that defines the entire application for the project, and contains anApplication_Start event handler that runs when the application first starts.

Let's add two using statements to the top of the file. The first references the Entity Framework namespace, and the second references the namespace where our MovieInitializer class lives: using System.Data.Entity; using MvcMovie.Models; // Database.SetInitialize // MovieInitializer

Then find the Application_Start method and add a call to Database.SetInitializer at the beginning of the method, as shown below: protected void Application_Start() { Database.SetInitializer<MovieDBContext>(new MovieInitializer()); AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); } The Database.SetInitializer statement you just added indicates that the database used by the MovieDBContextinstance should be automatically deleted and re-created if the schema and the database don't match. And as you saw, it will also populate the database with the sample data that's specified in the MovieInitializer class. Close the Global.asax file. Re-run the application and navigate to the /Movies URL. When the application starts, it detects that the model structure no longer matches the database schema. It automatically re-creates the database to match the new model structure and populates the database with the sample movies:

Click the Create New link to add a new movie. Note that you can add a rating.

Click Create. The new movie, including the rating, now shows up in the movies listing:

In this section you saw how you can modify model objects and keep the database in sync with the changes. You also learned a way to populate a newly created database with sample data so you can try out scenarios. Next, let's look at how you can add richer validation logic to the model classes and enable some business rules to be enforced.

Adding Validation to the Model (C#)


By Scott Hanselman|January 12, 2011 This tutorial will teach you the basics of building an ASP.NET MVC Web application using Microsoft Visual Web Developer 2010 Express Service Pack 1, which is a free version of Microsoft Visual Studio. Before you start, make sure you've installed the prerequisites listed below. You can install all of them by clicking the following link: Web Platform Installer. Alternatively, you can individually install the prerequisites using the following links: Visual Studio Web Developer Express SP1 prerequisites ASP.NET MVC 3 Tools Update SQL Server Compact 4.0 (runtime + tools support) If you're using Visual Studio 2010 instead of Visual Web Developer 2010, install the prerequisites by clicking the following link: Visual Studio 2010 prerequisites. A Visual Web Developer project with C# source code is available to accompany this topic. Download the C# version. If you prefer Visual Basic, switch to the Visual Basic version of this tutorial.

In this section you'll add validation logic to the Movie model, and you'll ensure that the validation rules are enforced any time a user attempts to create or edit a movie using the application.

Keeping Things DRY


One of the core design tenets of ASP.NET MVC is DRY ("Don't Repeat Yourself"). ASP.NET MVC encourages you to specify functionality or behavior only once, and then have it be reflected everywhere in an application. This reduces the amount of code you need to write and makes the code you do write much easier to maintain. The validation support provided by ASP.NET MVC and Entity Framework Code First is a great example of the DRY principle in action. You can declaratively specify validation rules in one place (in the model class) and then those rules are enforced everywhere in the application. Let's look at how you can take advantage of this validation support in the movie application.

Adding Validation Rules to the Movie Model


You'll begin by adding some validation logic to the Movie class. Open the Movie.cs file. Add a using statement at the top of the file that references theSystem.ComponentModel.DataAnnotations namespace: using System.ComponentModel.DataAnnotations; The namespace is part of the .NET Framework. It provides a built-in set of validation attributes that you can apply declaratively to any class or property. Now update the Movie class to take advantage of the built-in Required, StringLength, and Range validation attributes. Use the following code as an example of where to apply the attributes. public class Movie { public int ID { get; set; } [Required(ErrorMessage = "Title is required")] public string Title { get; set; } [Required(ErrorMessage = "Date is required")] public DateTime ReleaseDate { get; set; } [Required(ErrorMessage = "Genre must be specified")] public string Genre { get; set; } [Required(ErrorMessage = "Price Required")] [Range(1, 100, ErrorMessage = "Price must be between $1 and $100")] public decimal Price { get; set; } [StringLength(5)]

public string Rating { get; set; } } The validation attributes specify behavior that you want to enforce on the model properties they are applied to. TheRequired attribute indicates that a property must have a value; in this sample, a movie has to have values for theTitle, ReleaseDate, Genre, and Price properties in order to be valid. The Range attribute constrains a value to within a specified range. The StringLength attribute lets you set the maximum length of a string property, and optionally its minimum length. Code First ensures that the validation rules you specify on a model class are enforced before the application saves changes in the database. For example, the code below will throw an exception when the SaveChanges method is called, because several required Movie property values are missing and the price is zero (which is out of the valid range). MovieDBContext db = new MovieDBContext(); Movie movie = new Movie(); movie.Title = "Gone with the Wind"; movie.Price = 0.0M; db.Movies.Add(movie); db.SaveChanges();

// <= Will throw validation exception

Having validation rules automatically enforced by the .NET Framework helps make your application more robust. It also ensures that you can't forget to validate something and inadvertently let bad data into the database. Here's a complete code listing for the updated Movie.cs file: using System; using System.Data.Entity; using System.ComponentModel.DataAnnotations; namespace MvcMovie.Models { public class Movie { public int ID { get; set; } [Required(ErrorMessage = "Title is required")] public string Title { get; set; } [Required(ErrorMessage = "Date is required")] public DateTime ReleaseDate { get; set; } [Required(ErrorMessage = "Genre must be specified")] public string Genre { get; set; } [Required(ErrorMessage = "Price Required")] [Range(1, 100, ErrorMessage = "Price must be between $1 and $100")] public decimal Price { get; set; }

[StringLength(5)] public string Rating { get; set; } } public class MovieDBContext : DbContext { public DbSet<Movie> Movies { get; set; } } }

Validation Error UI in ASP.NET MVC


Re-run the application and navigate to the /Movies URL. Click the Create Movie link to add a new movie. Fill out the form with some invalid values and then click the Createbutton.

Notice how the form has automatically used a background color to highlight the text boxes that contain invalid data and has emitted an appropriate validation error message next to each one. The error messages match the error strings you specified when you annotated the Movie class. The errors are enforced both client-side (using JavaScript) and server-side (in case a user has JavaScript disabled). A real benefit is that you didn't need to change a single line of code in the MoviesController class or in theCreate.cshtml view in order to enable this validation UI. The controller and views you created earlier in this tutorial automatically picked up the validation rules that you specified using attributes on the Movie model class.

How Validation Occurs in the Create View and Create Action Method

You might wonder how the validation UI was generated without any updates to the code in the controller or views. The next listing shows what the Create methods in the MovieController class look like. They're unchanged from how you created them earlier in this tutorial. // // GET: /Movies/Create public ActionResult Create() { return View(); } // // POST: /Movies/Create [HttpPost] public ActionResult Create(Movie movie) { if (ModelState.IsValid) { db.Movies.Add(movie); db.SaveChanges(); return RedirectToAction("Index"); } return View(movie); } The first action method displays the initial Create form. The second handles the form post. The second Createmethod calls ModelState.IsValid to check whether the movie has any validation errors. Calling this method evaluates any validation attributes that have been applied to the object. If the object has validation errors, theCreate method redisplays the form. If there are no errors, the method saves the new movie in the database. Below is the Create.cshtml view template that you scaffolded earlier in the tutorial. It's used by the action methods shown above both to display the initial form and to redisplay it in the event of an error.

@model MvcMovie.Models.Movie @{ ViewBag.Title = "Create"; } <h2> Create</h2> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>Movie</legend> <div class="editor-label"> @Html.LabelFor(model => model.Title) </div> <div class="editor-field"> @Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title) </div> <div class="editor-label"> @Html.LabelFor(model => model.ReleaseDate) </div> <div class="editor-field"> @Html.EditorFor(model => model.ReleaseDate) @Html.ValidationMessageFor(model => model.ReleaseDate) </div> <div class="editor-label"> @Html.LabelFor(model => model.Genre) </div> <div class="editor-field"> @Html.EditorFor(model => model.Genre) @Html.ValidationMessageFor(model => model.Genre) </div> <div class="editor-label"> @Html.LabelFor(model => model.Price) </div> <div class="editor-field"> @Html.EditorFor(model => model.Price) @Html.ValidationMessageFor(model => model.Price) </div> <div class="editor-label"> @Html.LabelFor(model => model.Rating) </div> <div class="editor-field">

@Html.EditorFor(model => model.Rating) @Html.ValidationMessageFor(model => model.Rating) </div> <p> <input type="submit" value="Create" /> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index") </div> Notice how the code uses an Html.EditorFor helper to output the <input> element for each Movie property. Next to this helper is a call to the Html.ValidationMessageFor helper method. These two helper methods work with the model object that's passed by the controller to the view (in this case, a Movie object). They automatically look for validation attributes specified on the model and display error messages as appropriate. What's really nice about this approach is that neither the controller nor the Create view template knows anything about the actual validation rules being enforced or about the specific error messages displayed. The validation rules and the error strings are specified only in the Movie class. If you want to change the validation logic later, you can do so in exactly one place. You won't have to worry about different parts of the application being inconsistent with how the rules are enforced all validation logic will be defined in one place and used everywhere. This keeps the code very clean, and makes it easy to maintain and evolve. And it means that that you'll be fully honoring the DRY principle.

Adding Formatting to the Movie Model


Open the Movie.cs file. The System.ComponentModel.DataAnnotations namespace provides formatting attributes in addition to the built-in set of validation attributes. You'll apply the DisplayFormat attribute and a DataTypeenumeration value to the release date and to the price fields. The following code shows the ReleaseDate andPrice properties with the appropriate DisplayFormat attribute. [DataType(DataType.Date)] public DateTime ReleaseDate { get; set; } [DataType(DataType.Currency)] public decimal Price { get; set; } Alternatively, you could explicitly set a DataFormatString value. The following code shows the release date property with a date format string (namely, "d"). You'd use this to specify that you don't want to time as part of the release date. [DisplayFormat(DataFormatString = "{0:d}")] public DateTime ReleaseDate { get; set; } The following code formats the Price property as currency. [DisplayFormat(DataFormatString = "{0:c}")] public decimal Price { get; set; } The complete Movie class is shown below.

public class Movie { public int ID { get; set; } [Required(ErrorMessage = "Title is required")] public string Title { get; set; } [Required(ErrorMessage = "Date is required")] [DisplayFormat(DataFormatString = "{0:d}")] public DateTime ReleaseDate { get; set; } [Required(ErrorMessage = "Genre must be specified")] public string Genre { get; set; } [Required(ErrorMessage = "Price Required")] [Range(1, 100, ErrorMessage = "Price must be between $1 and $100")] [DisplayFormat(DataFormatString = "{0:c}")] public decimal Price { get; set; } [StringLength(5)] public string Rating { get; set; } } Run the application and browse to the Movies controller.

In the next part of the series, we'll review the application and make some improvements to the automatically generated Details and Delete methods.

Improving the Details and Delete Methods (C#)


By Scott Hanselman|January 12, 2011 This tutorial will teach you the basics of building an ASP.NET MVC Web application using Microsoft Visual Web Developer 2010 Express Service Pack 1, which is a free version of Microsoft Visual Studio. Before you start, make sure you've installed the prerequisites listed below. You can install all of them by clicking the following link: Web Platform Installer. Alternatively, you can individually install the prerequisites using the following links:

Visual Studio Web Developer Express SP1 prerequisites ASP.NET MVC 3 Tools Update SQL Server Compact 4.0 (runtime + tools support) If you're using Visual Studio 2010 instead of Visual Web Developer 2010, install the prerequisites by clicking the following link: Visual Studio 2010 prerequisites. A Visual Web Developer project with C# source code is available to accompany this topic. Download the C# version. If you prefer Visual Basic, switch to the Visual Basic version of this tutorial. In this part of the tutorial, you'll make some improvements to the automatically generated Details and Deletemethods. These changes aren't required, but with just a few small bits of code, you can easily enhance the application.

Improving the Details and Delete Methods


When you scaffolded the Movie controller, ASP.NET MVC generated code that worked great, but that can be made more robust with just a few small changes. Open the Movie controller and modify the Details method by returning HttpNotFound when a movie isn't found. You should also modify the Details method to set a default value for the ID that's passed to it. (You made similar changes to the Edit method in part 6 of this tutorial.) However, you must change the return type of the Detailsmethod from ViewResult to ActionResult, because the HttpNotFound method doesn't return a ViewResultobject. The following example shows the modified Details method. public ActionResult Details(int id = 0) { Movie movie = db.Movies.Find(id); if (movie == null) { return HttpNotFound(); } return View(movie); } Code First makes it easy to search for data using the Find method. An important security feature that we built into the method is that the code verifies that the Find method has found a movie before the code tries to do anything with it. For example, a hacker could introduce errors into the site by changing the URL created by the links fromhttp://localhost:xxxx/Movies/Details/1 to something like http://localhost:xxxx/Movies/Details/12345 (or some other value that doesn't represent an actual movie). If you don't the check for a null movie, this could result in a database error. Similarly, change the Delete and DeleteConfirmed methods to specify a default value for the ID parameter and to return HttpNotFound when a movie isn't found. The updated Delete methods in the Movie controller are shown below. // GET: /Movies/Delete/5 public ActionResult Delete(int id = 0) { Movie movie = db.Movies.Find(id); if (movie == null)

{ return HttpNotFound(); } return View(movie); } // // POST: /Movies/Delete/5 [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id = 0) { Movie movie = db.Movies.Find(id); if (movie == null) { return HttpNotFound(); } db.Movies.Remove(movie); db.SaveChanges(); return RedirectToAction("Index"); } Note that the Delete method doesn't delete the data. Performing a delete operation in response to a GET request (or for that matter, performing an edit operation, create operation, or any other operation that changes data) opens up a security hole. For more information about this, see Stephen Walther's blog entry ASP.NET MVC Tip #46 Don't use Delete Links because they create Security Holes. The HttpPost method that deletes the data is named DeleteConfirmed to give the HTTP POST method a unique signature or name. The two method signatures are shown below: // GET: /Movies/Delete/5 public ActionResult Delete(int id = 0) // // POST: /Movies/Delete/5 [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id = 0) The common language runtime (CLR) requires overloaded methods to have a unique signature (same name, different list of parameters). However, here you need two Delete methods -- one for GET and one for POST -- that both require the same signature. (They both need to accept a single integer as a parameter.) To sort this out, you can do a couple of things. One is to give the methods different names. That's what we did in he preceding example. However, this introduces a small problem: ASP.NET maps segments of a URL to action methods by name, and if you rename a method, routing normally wouldn't be able to find that method. The solution is what you see in the example, which is to add the ActionName("Delete") attribute to theDeleteConfirmed method. This effectively performs mapping for the routing system so that a URL that includes/Delete/ for a POST request will find the DeleteConfirmed method.

Another way to avoid a problem with methods that have identical names and signatures is to artificially change the signature of the POST method to include an unused parameter. For example, some developers add a parameter type FormCollection that is passed to the POST method, and then simply don't use the parameter: public ActionResult Delete(FormCollection fcNotUsed, int id = 0) { Movie movie = db.Movies.Find(id); if (movie == null) { return HttpNotFound(); } db.Movies.Remove(movie); db.SaveChanges(); return RedirectToAction("Index"); }

Wrapping Up
You now have a complete ASP.NET MVC application that stores data in a SQL Server Compact database. You can create, read, update, delete, and search for movies.

This basic tutorial got you started making controllers, associating them with views, and passing around hard-coded data. Then you created and designed a data model. Entity Framework Code First created a database from the data model on the fly, and the ASP.NET MVC scaffolding system automatically generated the action methods and views for basic CRUD operations. You then added a search form that let users search the database. You changed the database to include a new column of data, and then updated two pages to create and display this new data. You added validation by marking the data model with attributes from the DataAnnotations namespace. The resulting validation runs on the client and on the server. If you'd like to deploy your application, it's helpful to first test the application on your local IIS 7 server. You can use this Web Platform Installer link to enable IIS setting for ASP.NET applications. See the following deployment links: ASP.NET Deployment Content Map Enabling IIS 7.x Web Application Projects Deployment

I now encourage you to move on to our intermediate-level Creating an Entity Framework Data Model for an ASP.NET MVC Application and MVC Music Store tutorials, to explore the ASP.NET articles on MSDN, and to check out the many videos and resources at http://asp.net/mvc to learn even more about ASP.NET MVC! The ASP.NET MVC forums are a great place to ask questions.

Part 1: Overview and File->New Project


By Jon Galloway|April 21, 2011 The MVC Music Store is a tutorial application that introduces and explains step-by-step how to use ASP.NET MVC and Visual Studio for web development. The MVC Music Store is a lightweight sample store implementation which sells music albums online, and implements basic site administration, user sign-in, and shopping cart functionality. This tutorial series details all of the steps taken to build the ASP.NET MVC Music Store sample application. Part 1 covers Overview and File->New Project.

Overview
The MVC Music Store is a tutorial application that introduces and explains step-by-step how to use ASP.NET MVC and Visual Web Developer for web development. Well be starting slowly, so beginner level web development experience is okay. The application well be building is a simple music store. There are three main parts to the application: shopping, checkout, and administration.

Visitors can browse Albums by Genre:

They can view a single album and add it to their cart:

They can review their cart, removing any items they no longer want:

Proceeding to Checkout will prompt them to login or register for a user account.

After creating an account, they can complete the order by filling out shipping and payment information. To keep things simple, were running an amazing promotion: everythings free if they enter promotion code FREE!

After ordering, they see a simple confirmation screen:

In addition to customer-faceing pages, well also build an administrator section that shows a list of albums from which Administrators can Create, Edit, and Delete albums:

1. File -> New Project


Installing the software
This tutorial will begin by creating a new ASP.NET MVC 3 project using the free Visual Web Developer 2010 Express (which is free), and then well incrementally add features to create a complete functioning application. Along the way, well cover database access, form posting scenarios, data validation, using master pages for consistent page layout, using AJAX for page updates and validation, user login, and more. You can follow along step by step, or you can download the completed application fromhttp://mvcmusicstore.codeplex.com. You can use either Visual Studio 2010 SP1 or Visual Web Developer 2010 Express SP1 (a free version of Visual Studio 2010) to build the application. Well be using the SQL Server Compact (also free) to host the database. Before you start, make sure you've installed the prerequisites listed below. You can install all of them using the following Web Platform Installer link: http://www.microsoft.com/web/gallery/install.aspx?appid=VWD2010SP1Pack Note: You can find this link on the big green button at this (easier to remember) link: http://asp.net/mvc

The Web Platform Installer will check what youve got installed and just download what you need.

If you want to individually install the prerequisites using the following links instead of using the above link, use the following links (written out in case youre using a printed version of this tutorial):

Visual Studio Web Developer Express SP1 prerequisites http://www.microsoft.com/web/gallery/install.aspx?appid=VWD2010SP1Pack ASP.NET MVC 3 Tools Update http://www.microsoft.com/web/gallery/install.aspx?appid=MVC3 SQL Server Compact 4.0 - including both runtime and tools support http://www.microsoft.com/web/gallery/install.aspx?appid=SQLCE;SQLCEVSTools_4_0 Note: If you're using Visual Studio 2010 instead of Visual Web Developer 2010, install the prerequisites with this link instead: Visual Studio Web Developer Express SP1 prerequisites http://www.microsoft.com/web/gallery/install.aspx?appsxml=&appid=VS2010SP1Pack I highly recommend you use the first Web Platform Installer link, as it will make sure youve got everything set up correctly.

Creating a new ASP.NET MVC 3 project


Well start by selecting New Project from the File menu in Visual Web Developer. This brings up the New Project dialog.

Well select the Visual C# -> Web Templates group on the left, then choose the ASP.NET MVC 3 Web Application template in the center column. Name your project MvcMusicStore and press the OK button.

This will display a secondary dialog which allows us to make some MVC specific settings for our project. Select the following: Project Template - select Empty View Engine - select Razor Use HTML5 semantic markup - checked Verify that your settings are as shown below, then press the OK button.

This will create our project. Lets take a look at the folders that have been added to our application in the Solution Explorer on the right side.

The Empty MVC 3 template isnt completely empty it adds a basic folder structure:

ASP.NET MVC makes use of some basic naming conventions for folder names:

Folder

Purpose

/Controllers

Controllers respond to input from the browser, decide what to do with it, and return response to the user.

/Views

Views hold our UI templates

/Models

Models hold and manipulate data

/Content

This folder holds our images, CSS, and any other static content

/Scripts

This folder holds our JavaScript files

These folders are included even in an Empty ASP.NET MVC application because the ASP.NET MVC framework by default uses a convention over configuration approach and makes some default assumptions based on folder naming conventions. For instance, controllers look for views in the Views folder by default without you having to explicitly specify this in your code. Sticking with the default conventions reduces the amount of code you need to write, and can also make it easier for other developers to understand your project. Well explain these conventions more as we build our application.

Part 2: Controllers
By Jon Galloway|April 21, 2011 The MVC Music Store is a tutorial application that introduces and explains step-by-step how to use ASP.NET MVC and Visual Studio for web development. The MVC Music Store is a lightweight sample store implementation which sells music albums online, and implements basic site administration, user sign-in, and shopping cart functionality. This tutorial series details all of the steps taken to build the ASP.NET MVC Music Store sample application. Part 2 covers Controllers. With traditional web frameworks, incoming URLs are typically mapped to files on disk. For example: a request for a URL like "/Products.aspx" or "/Products.php" might be processed by a "Products.aspx" or "Products.php" file. Web-based MVC frameworks map URLs to server code in a slightly different way. Instead of mapping incoming URLs to files, they instead map URLs to methods on classes. These classes are called "Controllers" and they are responsible for processing incoming HTTP requests, handling user input,

retrieving and saving data, and determining the response to send back to the client (display HTML, download a file, redirect to a different URL, etc.).

Adding a HomeController
Well begin our MVC Music Store application by adding a Controller class that will handle URLs to the Home page of our site. Well follow the default naming conventions of ASP.NET MVC and call it HomeController. Right-click the Controllers folder within the Solution Explorer and select Add, and then the Controller command:

This will bring up the Add Controller dialog. Name the controller HomeController and press the Add button.

This will create a new file, HomeController.cs, with the following code: using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcMusicStore.Controllers { public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { return View(); } } } To start as simply as possible, lets replace the Index method with a simple method that just returns a string. Well make two changes: Change the method to return a string instead of an ActionResult

Change the return statement to return Hello from Home

The method should now look like this: public string Index() { return "Hello from Home"; }

Running the Application


Now lets run the site. We can start our web-server and try out the site using any of the following:: Choose the Debug Start Debugging menu item Click the Green arrow button in the toolbar

Use the keyboard shortcut, F5.

Using any of the above steps will compile our project, and then cause the ASP.NET Development Server that is built-into Visual Web Developer to start. A notification will appear in the bottom corner of the screen to indicate that the ASP.NET Development Server has started up, and will show the port number that it is running under.

Visual Web Developer will then automatically open a browser window whose URL points to our webserver. This will allow us to quickly try out our web application:

Okay, that was pretty quick we created a new website, added a three line function, and weve got text in a browser. Not rocket science, but its a start. Note: Visual Web Developer includes the ASP.NET Development Server, which will run your website on a random free port number. In the screenshot above, the site is running at http://localhost:26641/, so its using port 26641. Your port number will be different. When we talk about URLs like /Store/Browse in this tutorial, that will go after the port number. Assuming a port number of 26641, browsing to /Store/Browse will mean browsing to http://localhost:26641/Store/Browse.

Adding a StoreController
We added a simple HomeController that implements the Home Page of our site. Lets now add another controller that well use to implement the browsing functionality of our music store. Our store controller will support three scenarios: A listing page of the music genres in our music store A browse page that lists all of the music albums in a particular genre A details page that shows information about a specific music album

Well start by adding a new StoreController class.. If you havent already, stop running the application either by closing the browser or selecting the Debug Stop Debugging menu item. Now add a new StoreController. Just like we did with HomeController, well do this by right-clicking on the Controllers folder within the Solution Explorer and choosing the Add->Controller menu item

Our new StoreController already has an Index method. Well use this Index method to implement our listing page that lists all genres in our music store. Well also add two additional methods to implement the two other scenarios we want our StoreController to handle: Browse and Details. These methods (Index, Browse and Details) within our Controller are called Controller Actions, and as youve already seen with the HomeController.Index()action method, their job is to respond to URL requests and (generally speaking) determine what content should be sent back to the browser or user that invoked the URL. Well start our StoreController implementation by changing theIndex() method to return the string Hello from Store.Index() and well add similar methods for Browse() and Details(): using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcMusicStore.Controllers { public class StoreController : Controller { // // GET: /Store/

public string Index() { return "Hello from Store.Index()"; } // // GET: /Store/Browse public string Browse() { return "Hello from Store.Browse()"; } // // GET: /Store/Details public string Details() { return "Hello from Store.Details()"; } } } Run the project again and browse the following URLs: /Store /Store/Browse /Store/Details

Accessing these URLs will invoke the action methods within our Controller and return string responses:

Thats great, but these are just constant strings. Lets make them dynamic, so they take information from the URL and display it in the page output.

First well change the Browse action method to retrieve a querystring value from the URL. We can do this by adding a genre parameter to our action method. When we do this ASP.NET MVC will automatically pass any querystring or form post parameters named genre to our action method when it is invoked. // // GET: /Store/Browse?genre=Disco public string Browse(string genre) { string message = HttpUtility.HtmlEncode("Store.Browse, Genre = " + genre); return message; } Note: Were using the HttpUtility.HtmlEncode utility method to sanitize the user input. This prevents users from injecting Javascript into our View with a link like /Store/Browse?Genre=<script>window.location=http://hackersite.com</script>. Now lets browse to /Store/Browse?Genre=Disco

Lets next change the Details action to read and display an input parameter named ID. Unlike our previous method, we wont be embedding the ID value as a querystring parameter. Instead well embed it directly within the URL itself. For example: /Store/Details/5. ASP.NET MVC lets us easily do this without having to configure anything. ASP.NET MVCs default routing convention is to treat the segment of a URL after the action method name as a parameter named ID. If

your action method has a parameter named ID then ASP.NET MVC will automatically pass the URL segment to you as a parameter. // // GET: /Store/Details/5 public string Details(int id) { string message = "Store.Details, ID = " + id; return message; } Run the application and browse to /Store/Details/5:

Lets recap what weve done so far: Weve created a new ASP.NET MVC project in Visual Web Developer Weve discussed the basic folder structure of an ASP.NET MVC application Weve learned how to run our website using the ASP.NET Development Server Weve created two Controller classes: a HomeController and a StoreController Weve added Action Methods to our controllers which respond to URL requests and return text to the browser

Part 3: Views and ViewModels


By Jon Galloway|April 21, 2011 The MVC Music Store is a tutorial application that introduces and explains step-by-step how to use ASP.NET MVC and Visual Studio for web development. The MVC Music Store is a lightweight sample store implementation which sells music albums online, and implements basic site administration, user sign-in, and shopping cart functionality. This tutorial series details all of the steps taken to build the ASP.NET MVC Music Store sample application. Part 3 covers Views and ViewModels. So far weve just been returning strings from controller actions. Thats a nice way to get an idea of how controllers work, but its not how youd want to build a real web application. We are going to want a better way to generate HTML back to browsers visiting our site one where we can use template files to more easily customize the HTML content send back. Thats exactly what Views do.

Adding a View template


To use a view-template, well change the HomeController Index method to return an ActionResult, and have it return View(), like below: public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { return View(); } } The above change indicates that instead of returned a string, we instead want to use a View to generate a result back. Well now add an appropriate View template to our project. To do this well position the text cursor within the Index action method, then right-click and select Add View. This will bring up the Add View dialog:

The Add View dialog allows us to quickly and easily generate View template files. By default the Add View dialog pre-populates the name of the View template to create so that it matches the action method that will use it. Because we used the Add View context menu within the Index() action method of our HomeController, the Add View dialog above has Index as the view name pre-populated by default. We dont need to change any of the options on this dialog, so click the Add button. When we click the Add button, Visual Web Developer will create a new Index.cshtml view template for us in the \Views\Home directory, creating the folder if doesnt already exist.

The name and folder location of the Index.cshtml file is important, and follows the default ASP.NET MVC naming conventions. The directory name, \Views\Home, matches the controller - which is named HomeController. The view template name, Index, matches the controller action method which will be displaying the view. ASP.NET MVC allows us to avoid having to explicitly specify the name or location of a view template when we use this naming convention to return a view. It will by default render the \Views\Home\Index.cshtml view template when we write code like below within our HomeController: public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { return View(); } } Visual Web Developer created and opened the Index.cshtml view template after we clicked the Add button within the Add View dialog. The contents of Index.cshtml are shown below. @{ ViewBag.Title = "Index";

} <h2>Index</h2> This view is using the Razor syntax, which is more concise than the Web Forms view engine used in ASP.NET Web Forms and previous versions of ASP.NET MVC. The Web Forms view engine is still available in ASP.NET MVC 3, but many developers find that the Razor view engine fits ASP.NET MVC development really well. The first three lines set the page title using ViewBag.Title. Well look at how this works in more detail soon, but first lets update the text heading text and view the page. Update the <h2> tag to say This is the Home Page as shown below. @{ ViewBag.Title = "Index"; } <h2>This is the Home Page</h2> Running the application shows that our new text is visible on the home page.

Using a Layout for common site elements


Most websites have content which is shared between many pages: navigation, footers, logo images, stylesheet references, etc. The Razor view engine makes this easy to manage using a page called _Layout.cshtml which has automatically been created for us inside the /Views/Shared folder.

Double-click on this folder to view the contents, which are shown below. <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>@ViewBag.Title</title> <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" /> <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script> </head> <body> @RenderBody() </body> </html> The content from our individual views will be displayed by the @RenderBody() command, and any common content that we want to appear outside of that can be added to the _Layout.cshtml markup. Well want our MVC Music Store to have a common header with links to our Home page and Store area on all pages in the site, so well add that to the template directly above that @RenderBody() statement. <!DOCTYPE html> <html> <head> <title>@ViewBag.Title</title>

<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" /> <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script> </head> <body> <div id="header"> <h1> ASP.NET MVC MUSIC STORE</h1> <ul id="navlist"> <li class="first"><a href="/" id="current">Home</a></li> <li><a href="/Store/">Store</a></li> </ul> </div> @RenderBody() </body> </html>

Updating the StyleSheet


The empty project template includes a very streamlined CSS file which just includes styles used to display validation messages. Our designer has provided some additional CSS and images to define the look and feel for our site, so well add those in now. The updated CSS file and Images are included in the Content directory of MvcMusicStore-Assets.zip which is available at http://mvcmusicstore.codeplex.com. Well select both of them in Windows Explorer and drop them into our Solutions Content folder in Visual Web Developer, as shown below:

Youll be asked to confirm if you want to overwrite the existing Site.css file. Click Yes.

The Content folder of your application will now appear as follows:

Now let's run the application and see how our changes look on the Home page.

Lets review whats changed: The HomeControllers Index action method found and displayed the \Views\Home\Index.cshtmlView template, even though our code called return View(), because our View template followed the standard naming convention. The Home Page is displaying a simple welcome message that is defined within the \Views\Home\Index.cshtml view template. The Home Page is using our _Layout.cshtml template, and so the welcome message is contained within the standard site HTML layout.

Using a Model to pass information to our View


A View template that just displays hardcoded HTML isnt going to make a very interesting web site. To create a dynamic web site, well instead want to pass information from our controller actions to our view templates. In the Model-View-Controller pattern, the term Model refers to objects which represent the data in the application. Often, model objects correspond to tables in your database, but they dont have to. Controller action methods which return an ActionResult can pass a model object to the view. This allows a Controller to cleanly package up all the information needed to generate a response, and then pass this information off to a View template to use to generate the appropriate HTML response. This is easiest to understand by seeing it in action, so lets get started.

First well create some Model classes to represent Genres and Albums within our store. Lets start by creating a Genre class. Right-click the Models folder within your project, choose the Add Class option, and name the file Genre.cs.

Then add a public string Name property to the class that was created:

public class Genre { public string Name { get; set; } } Note: In case you're wondering, the { get; set; } notation is making use of C#'s auto-implemented properties feature. This gives us the benefits of a property without requiring us to declare a backing field. Next, follow the same steps to create an Album class (named Album.cs) that has a Title and a Genre property: public class Album { public string Title { get; set; } public Genre Genre { get; set; } } Now we can modify the StoreController to use Views which display dynamic information from our Model. If - for demonstration purposes right now - we named our Albums based on the request ID, we could display that information as in the view below.

Well start by changing the Store Details action so it shows the information for a single album. Add a using statement to the top of the StoreControllers class to include the MvcMusicStore.Models namespace, so we dont need to type MvcMusicStore.Models.Album every time we want to use the album class. The usings section of that class should now appear as below. using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;

using MvcMusicStore.Models; Next, well update the Details controller action so that it returns an ActionResult rather than a string, as we did with the HomeControllers Index method. public ActionResult Details(int id) Now we can modify the logic to return an Album object to the view. Later in this tutorial we will be retrieving the data from a database but for right now we will use "dummy data" to get started. public ActionResult Details(int id) { var album = new Album { Title = "Album " + id }; return View(album); } Note: If youre unfamiliar with C#, you may assume that using var means that our album variable is latebound. Thats not correct the C# compiler is using type-inference based on what were assigning to the variable to determine that album is of type Album and compiling the local album variable as an Album type, so we get compile-time checking and Visual Studio code-editor support. Lets now create a View template that uses our Album to generate an HTML response. Before we do that we need to build the project so that the Add View dialog knows about our newly created Album class. You can build the project by selecting the DebugBuild MvcMusicStore menu item (for extra credit, you can use the Ctrl-Shift-B shortcut to build the project).

Now that we've set up our supporting classes, we're ready to build our View template. Right-click within the Details method and select Add View from the context menu.

We are going to create a new View template like we did before with the HomeController. Because we are creating it from the StoreController it will by default be generated in a \Views\Store\Index.cshtml file. Unlike before, we are going to check the Create a strongly-typed view checkbox. We are then going to select our Album class within the View data-class drop-downlist. This will cause the Add View dialog to create a View template that expects that an Album object will be passed to it to use.

When we click the Add button our \Views\Store\Details.cshtml View template will be created, containing the following code. @model MvcMusicStore.Models.Album @{ ViewBag.Title = "Details"; } <h2>Details</h2> Notice the first line, which indicates that this view is strongly-typed to our Album class. The Razor view engine understands that it has been passed an Album object, so we can easily access model properties and even have the benefit of IntelliSense in the Visual Web Developer editor.

Update the <h2> tag so it displays the Albums Title property by modifying that line to appear as follows. <h2>Album: @Model.Title</h2> Notice that IntelliSense is triggered when you enter the period after the @Model keyword, showing the properties and methods that the Album class supports. Let's now re-run our project and visit the /Store/Details/5 URL. We'll see details of an Album like below.

Now well make a similar update for the Store Browse action method. Update the method so it returns an ActionResult, and modify the method logic so it creates a new Genre object and returns it to the View. public ActionResult Browse(string genre) { var genreModel = new Genre { Name = genre }; return View(genreModel); } Right-click in the Browse method and select Add View from the context menu, then add a View that is strongly-typed add a strongly typed to the Genre class.

Update the <h2> element in the view code (in /Views/Store/Browse.cshtml) to display the Genre information. @model MvcMusicStore.Models.Genre @{ ViewBag.Title = "Browse"; } <h2>Browsing Genre: @Model.Name</h2> Now lets re-run our project and browse to the /Store/Browse?Genre=Disco URL. Well see the Browse page displayed like below.

Finally, lets make a slightly more complex update to the Store Index action method and view to display a list of all the Genres in our store. Well do that by using a List of Genres as our model object, rather than just a single Genre. public ActionResult Index() { var genres = new List<Genre> { new Genre { Name = "Disco"}, new Genre { Name = "Jazz"}, new Genre { Name = "Rock"} }; return View(genres); } Right-click in the Store Index action method and select Add View as before, select Genre as the Model class, and press the Add button.

First well change the @model declaration to indicate that the view will be expecting several Genre objects rather than just one. Change the first line of /Store/Index.cshtml to read as follows: @model IEnumerable<MvcMusicStore.Models.Genre> This tells the Razor view engine that it will be working with a model object that can hold several Genre objects. Were using an IEnumerable<Genre> rather than a List<Genre> since its more generic, allowing us to change our model type later to any object type that supports the IEnumerable interface. Next, well loop through the Genre objects in the model as shown in the completed view code below. @model IEnumerable<MvcMusicStore.Models.Genre> @{ ViewBag.Title = "Store"; } <h3>Browse Genres</h3> <p> Select from @Model.Count()

genres:</p> <ul> @foreach (var genre in Model) { <li>@genre.Name</li> } </ul> Notice that we have full IntelliSense support as we enter this code, so that when we type @Model. we see all methods and properties supported by an IEnumerable of type Genre.

Within our foreach loop, Visual Web Developer knows that each item is of type Genre, so we see IntelliSence for each the Genre type.

Next, the scaffolding feature examined the Genre object and determined that each will have a Name property, so it loops through and writes them out. It also generates Edit, Details, and Delete links to each individual item. Well take advantage of that later in our store manager, but for now wed like to have a simple list instead.

When we run the application and browse to /Store, we see that both the count and list of Genres is displayed.

Adding Links between pages


Our /Store URL that lists Genres currently lists the Genre names simply as plain text. Lets change this so that instead of plain text we instead have the Genre names link to the appropriate /Store/Browse URL, so that clicking on a music genre like Disco will navigate to the /Store/Browse?genre=Disco URL. We could update our \Views\Store\Index.cshtml View template to output these links using code like below (dont type this in - were going to improve on it): <ul> @foreach (var genre in Model) { <li><a href="/Store/Browse?genre=@genre.Name">@genre.Name</a></li> } </ul> That works, but it could lead to trouble later since it relies on a hardcoded string. For instance, if we wanted to rename the Controller, wed need to search through our code looking for links that need to be updated.

An alternative approach we can use is to take advantage of an HTML Helper method. ASP.NET MVC includes HTML Helper methods which are available from our View template code to perform a variety of common tasks just like this. The Html.ActionLink() helper method is a particularly useful one, and makes it easy to build HTML <a> links and takes care of annoying details like making sure URL paths are properly URL encoded. Html.ActionLink() has several different overloads to allow specifying as much information as you need for your links. In the simplest case, youll supply just the link text and the Action method to go to when the hyperlink is clicked on the client. For example, we can link to /Store/ Index() method on the Store Details page with the link text Go to the Store Index using the following call: @Html.ActionLink("Go to the Store Index", "Index") Note: In this case, we didnt need to specify the controller name because were just linking to another action within the same controller thats rendering the current view. Our links to the Browse page will need to pass a parameter, though, so well use another overload of the Html.ActionLink method that takes three parameters: 1. Link text, which will display the Genre name 2. Controller action name (Browse) 3. Route parameter values, specifying both the name (Genre) and the value (Genre name)

Putting that all together, heres how well write those links to the Store Index view: <ul> @foreach (var genre in Model) { <li>@Html.ActionLink(genre.Name, "Browse", new { genre = genre.Name })</li> } </ul> Now when we run our project again and access the /Store/ URL we will see a list of genres. Each genre is a hyperlink when clicked it will take us to our /Store/Browse?genre=[genre] URL.

The HTML for the genre list looks like this: <ul> <li><a href="/Store/Browse?genre=Disco">Disco</a> </li> <li><a href="/Store/Browse?genre=Jazz">Jazz</a> </li> <li><a href="/Store/Browse?genre=Rock">Rock</a> </li> </ul>

Part 4: Models and Data Access


By Jon Galloway|April 21, 2011 The MVC Music Store is a tutorial application that introduces and explains step-by-step how to use ASP.NET MVC and Visual Studio for web development.

The MVC Music Store is a lightweight sample store implementation which sells music albums online, and implements basic site administration, user sign-in, and shopping cart functionality. This tutorial series details all of the steps taken to build the ASP.NET MVC Music Store sample application. Part 4 covers Models and Data Access. So far, weve just been passing dummy data from our Controllers to our View templates. Now were ready to hook up a real database. In this tutorial well be covering how to use SQL Server Compact Edition (often called SQL CE) as our database engine. SQL CE is a free, embedded, file based database that doesnt require any installation or configuration, which makes it really convenient for local development.

Database access with Entity Framework Code-First


Well use the Entity Framework (EF) support that is included in ASP.NET MVC 3 projects to query and update the database. EF is a flexible object relational mapping (ORM) data API that enables developers to query and update data stored in a database in an object-oriented way. Entity Framework version 4 supports a development paradigm called code-first. Code-first allows you to create model object by writing simple classes (also known as POCO from "plain-old" CLR objects), and can even create the database on the fly from your classes.

Changes to our Model Classes


We will be leveraging the database creation feature in Entity Framework in this tutorial. Before we do that, though, lets make a few minor changes to our model classes to add in some things well be using later on.

Adding the Artist Model Classes


Our Albums will be associated with Artists, so well add a simple model class to describe an Artist. Add a new class to the Models folder named Artist.cs using the code shown below. namespace MvcMusicStore.Models { public class Artist { public int ArtistId { get; set; } public string Name { get; set; } } }

Updating our Model Classes


Update the Album class as shown below. namespace MvcMusicStore.Models { public class Album

{ public int } public public public public public public public } } Next, make the following updates to the Genre class. using System.Collections.Generic; namespace MvcMusicStore.Models { public partial class Genre { public int GenreId public string Name public string Description public List<Album> Albums } } int int string decimal string Genre Artist GenreId ArtistId Title Price AlbumArtUrl Genre Artist { { { { { { { get; get; get; get; get; get; get; set; set; set; set; set; set; set; } } } } } } } AlbumId { get; set;

{ { { {

get; get; get; get;

set; set; set; set;

} } } }

Adding the App_Data folder


Well add an App_Data directory to our project to hold our SQL Server Express database files. App_Data is a special directory in ASP.NET which already has the correct security access permissions for database access. From the Project menu, select Add ASP.NET Folder, then App_Data.

Creating a Connection String in the web.config file


We will add a few lines to the websites configuration file so that Entity Framework knows how to connect to our database. Double-click on the Web.config file located in the root of the project.

Scroll to the bottom of this file and add a <connectionStrings> section directly above the last line, as shown below. <connectionStrings> <add name="MusicStoreEntities" connectionString="Data Source=|DataDirectory|MvcMusicStore.sdf" providerName="System.Data.SqlServerCe.4.0"/> </connectionStrings> </configuration>

Adding a Context Class


Right-click the Models folder and add a new class named MusicStoreEntities.cs.

This class will represent the Entity Framework database context, and will handle our create, read, update, and delete operations for us. The code for this class is shown below. using System.Data.Entity; namespace MvcMusicStore.Models { public class MusicStoreEntities : DbContext { public DbSet<Album> Albums { get; set; } public DbSet<Genre> Genres { get; set; } } } Thats it - theres no other configuration, special interfaces, etc. By extending the DbContext base class, our MusicStoreEntities class is able to handle our database operations for us. Now that weve got that

hooked up, lets add a few more properties to our model classes to take advantage of some of the additional information in our database.

Adding our store catalog data


We will take advantage of a feature in Entity Framework which adds seed data to a newly created database. This will pre-populate our store catalog with a list of Genres, Artists, and Albums. The MvcMusicStore-Assets.zip download - which included our site design files used earlier in this tutorial - has a class file with this seed data, located in a folder named Code. Within the Code / Models folder, locate the SampleData.cs file and drop it into the Models folder in our project, as shown below.

Now we need to add one line of code to tell Entity Framework about that SampleData class. Double-click on the Global.asax file in the root of the project to open it and add the following line to the top the Application_Start method. protected void Application_Start() { System.Data.Entity.Database.SetInitializer( new MvcMusicStore.Models.SampleData()); AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); }

At this point, weve completed the work necessary to configure Entity Framework for our project.

Querying the Database


Now lets update our StoreController so that instead of using dummy data it instead calls into our database to query all of its information. Well start by declaring a field on the StoreController to hold an instance of the MusicStoreEntities class, named storeDB: public class StoreController : Controller { MusicStoreEntities storeDB = new MusicStoreEntities();

Updating the Store Index to query the database


The MusicStoreEntities class is maintained by the Entity Framework and exposes a collection property for each table in our database. Lets update our StoreControllers Index action to retrieve all Genres in our database. Previously we did this by hard-coding string data. Now we can instead just use the Entity Framework context Generes collection: public ActionResult Index() { var genres = storeDB.Genres.ToList(); return View(genres); } No changes need to happen to our View template since were still returning the same StoreIndexViewModel we returned before - were just returning live data from our database now. When we run the project again and visit the /Store URL, well now see a list of all Genres in our database:

Updating Store Browse and Details to use live data


With the /Store/Browse?genre=[some-genre] action method, were searching for a Genre by name. We only expect one result, since we shouldnt ever have two entries for the same Genre name, and so we can use the .Single() extension in LINQ to query for the appropriate Genre object like this (dont type this yet): var example = storeDB.Genres.Single(g => g.Name == Disco); The Single method takes a Lambda expression as a parameter, which specifies that we want a single Genre object such that its name matches the value weve defined. In the case above, we are loading a single Genre object with a Name value matching Disco. Well take advantage of an Entity Framework feature that allows us to indicate other related entities we want loaded as well when the Genre object is retrieved. This feature is called Query Result Shaping, and enables us to reduce the number of times we need to access the database to retrieve all of the information we need. We want to pre-fetch the Albums for Genre we retrieve, so well update our query to include from Genres.Include(Albums) to indicate that we want related albums as well. This is more efficient, since it will retrieve both our Genre and Album data in a single database request. With the explanations out of the way, heres how our updated Browse controller action looks: public ActionResult Browse(string genre) { // Retrieve Genre and its Associated Albums from database

var genreModel = storeDB.Genres.Include("Albums") .Single(g => g.Name == genre); return View(genreModel); } We can now update the Store Browse View to display the albums which are available in each Genre. Open the view template (found in /Views/Store/Browse.cshtml) and add a bulleted list of Albums as shown below. @model MvcMusicStore.Models.Genre @{ ViewBag.Title = "Browse"; } <h2>Browsing Genre: @Model.Name</h2> <ul> @foreach (var album in Model.Albums) { <li> @album.Title </li> } </ul> Running our application and browsing to /Store/Browse?genre=Jazz shows that our results are now being pulled from the database, displaying all albums in our selected Genre.

Well make the same change to our /Store/Details/[id] URL, and replace our dummy data with a database query which loads an Album whose ID matches the parameter value. public ActionResult Details(int id) { var album = storeDB.Albums.Find(id); return View(album); } Running our application and browsing to /Store/Details/1 shows that our results are now being pulled from the database.

Now that our Store Details page is set up to display an album by the Album ID, lets update the Browse view to link to the Details view. We will use Html.ActionLink, exactly as we did to link from Store Index to Store Browse at the end of the previous section. The complete source for the Browse view appears below. @model MvcMusicStore.Models.Genre @{ ViewBag.Title = "Browse";

} <h2>Browsing Genre: @Model.Name</h2> <ul> @foreach (var album in Model.Albums) { <li> @Html.ActionLink(album.Title, "Details", new { id = album.AlbumId }) </li> } </ul> Were now able to browse from our Store page to a Genre page, which lists the available albums, and by clicking on an album we can view details for that album.

Creating an Entity Framework Data Model for an ASP.NET MVC Application (1 of 10)
By Tom Dykstra|April 11, 2011

The Contoso University sample web application demonstrates how to create ASP.NET MVC applications using the Entity Framework. The sample application is a website for a fictional Contoso University. It includes functionality such as student admission, course creation, and instructor assignments. This tutorial series explains the steps taken to build the Contoso University sample application. You candownload the completed application or create it by following the steps in the tutorial. The tutorial shows examples in C#. The downloadable sample contains code in both C# and Visual Basic. If you have questions that are not directly related to the tutorial, you can post them to the ASP.NET Entity Framework forum or theEntity Framework and LINQ to Entities forum. This tutorial series assumes you know how to work with ASP.NET MVC in Visual Studio. If you dont, a good place to start is a basic ASP.NET MVC Tutorial. If you prefer to work with the ASP.NET Web Forms model, see the Getting Started with the Entity Framework and Continuing with the Entity Framework tutorials. Before you start, make sure you have the following software installed on your computer: Visual Studio 2010 SP1 or Visual Web Developer Express 2010 SP1 (If you use one of these links, the following items will be installed automatically.) ASP.NET MVC 3 Tools Update Microsoft SQL Server Compact 4.0 Microsoft Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0

The Contoso University Web Application


The application you'll be building in these tutorials is a simple university website.

Users can view and update student, course, and instructor information. A few of the screens you'll create are shown below.

The UI style of this site has been kept close to what's generated by the built-in templates, so that the tutorial can focus mainly on how to use the Entity Framework.

Entity Framework Development Approaches

As shown in the following diagram, there are three ways you can work with data in the Entity Framework: Database First, Model First, and Code First.

Database First
If you already have a database, the Entity Framework can automatically generate a data model that consists of classes and properties that correspond to existing database objects such as tables and columns. The information about your database structure (store schema), your data model (conceptual model), and the mapping between them is stored in XML in an .edmx file. Visual Studio provides the Entity Framework designer, which is a graphical designer that you can use to display and edit the .edmx file. The sections Getting Started With the Entity Frameworkand Continuing With the Entity Framework in the Web Forms tutorial series use Database First development.

Model First
If you don't yet have a database, you can begin by creating a model using the Entity Framework designer in Visual Studio. When the model is finished, the designer can generate DDL (data definition language) statements to create the database. This approach also uses an .edmx file to store model and mapping

information. The What's New in the Entity Framework 4 tutorial includes a brief example of Model First development.

Code First
Whether you have an existing database or not, you can code your own classes and properties that correspond to tables and columns and use them with the Entity Framework without an .edmx file. That's why you sometimes see this approach called code only, although the official name is Code First. The mapping between the store schema and the conceptual model represented by your code is handled by convention and by a special mapping API. If you don't yet have a database, the Entity Framework can automatically create the database for you, or drop and re-create it if the model changes. This tutorial series uses Code First development. The data access API that was developed for Code First is based on the DbContext class. This API can also be used with the Database First and Model First development workflows. For more information, see When is Code First not code first? on the Entity Framework team blog.

POCO (Plain Old CLR Objects)


By default, when you use the Database First or Model First development approaches, the entity classes in your data model inherit from the EntityObject class, which provides them with Entity Framework functionality. This means that these classes technically aren't persistence ignorant and so don't conform fully to one of the requirements ofdomain-driven design. All development approaches of the Entity Framework can also work with POCO (plain old CLR objects) classes, which essentially means that they are persistence-ignorant because they don't inherit from theEntityObject class. In this tutorial you'll use POCO classes.

Creating an MVC Web Application


Before you start, make sure you have the following installed on your computer: Visual Studio 2010 SP1 or Visual Web Developer Express 2010 SP1 (If you use one of these links, the following items will be installed automatically.) ASP.NET MVC 3 Tools Update Microsoft SQL Server Compact 4.0 Microsoft Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0 Open Visual Studio and create a new project named "ContosoUniversity" using the ASP.NET MVC 3 Web Application template:

In the New ASP.NET MVC 3 Project dialog box select the Internet Application template and the Razor view engine, clear the Create a unit test project check box, and then click OK.

Setting Up the Site Style


A few simple changes will set up the site menu, layout, and home page. In order to set up the Contoso University menu, in the Views\Shared\_Layout.cshtml file, replace the existing h1heading text and the menu links, as shown in the following example: <!DOCTYPE html> <html> <head> <title>@ViewBag.Title</title> <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css"

/> <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script> </head> <body> <div class="page"> <div id="header"> <div id="title"> <h1>Contoso University</h1> </div> <div id="logindisplay"> @Html.Partial("_LogOnPartial") </div> <div id="menucontainer"> <ul id="menu"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("About", "About", "Home")</li> <li>@Html.ActionLink("Students", "Index", "Student")</li> <li>@Html.ActionLink("Courses", "Index", "Course")</li> <li>@Html.ActionLink("Instructors", "Index", "Instructor")</li> <li>@Html.ActionLink("Departments", "Index", "Department")</li> </ul> </div> </div> <div id="main"> @RenderBody() </div> <div id="footer"> </div> </div> </body> </html> In the Views\Home\Index.cshtml file, delete everything under the h2 heading. In the Controllers\HomeController.cs file, replace "Welcome to ASP.NET MVC!" with "Welcome to Contoso University!" In the Content\Site.css file, make the following changes in order to move the menu tabs to the left: In the definition for #main, add clear: both;, as shown in the following example: #main { clear: both; padding: 30px 30px 15px 30px; background-color: #fff; border-radius: 4px 0 0 0; -webkit-border-radius: 4px 0 0 0;

-moz-border-radius: 4px 0 0 0; } In the definition for nav and #menucontainer, add clear: both; float: left;, as shown in the following example: nav, #menucontainer { margin-top: 40px; clear: both; float: left; } Run the site. You see the home page with the main menu.

Creating the Data Model


Next you'll create your first entity classes for the Contoso University application. You'll start with the following three entities:

There's a one-to-many relationship between Student and Enrollment entities, and there's a one-tomany relationship between Course and Enrollment entities. In other words, a student can be enrolled in any number of courses, and a course can have any number of students enrolled in it. In the following sections you'll create a class for each one of these entities. Note If you try to compile the project before you finish creating all of these entity classes, you'll get compiler errors.

The Student Entity

In the Models folder, create Student.cs and replace the existing code with the following code: using System; using System.Collections.Generic; namespace ContosoUniversity.Models { public class Student { public int StudentID { get; set; } public string LastName { get; set; } public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } } } The StudentID property will become the primary key column of the database table that corresponds to this class. By default, the Entity Framework interprets a property that's named ID or classnameID as the primary key. The Enrollments property is a navigation property. Navigation properties hold other entities that are related to this entity. In this case, the Enrollments property of a Student entity will hold all of the Enrollment entities that are related to that Student entity. In other words, if a given Student row in the database has two related Enrollmentrows (rows that contain that student's primary key value in their StudentID foreign key column), that Studententity's Enrollments navigation property will contain those two Enrollment entities. Navigation properties are typically defined as virtual so that they can take advantage of an Entity Framework function called lazy loading. (Lazy loading will be explained later, in the Reading Related Data tutorial later in this series.) If a navigation property can hold multiple entities (as in many-to-many or one-to-many relationships), its type must be ICollection.

The Enrollment Entity

In the Models folder, create Enrollment.cs and replace the existing code with the following code: using System; using System.Collections.Generic;

namespace ContosoUniversity.Models { public class Enrollment { public int EnrollmentID { get; set; } public int CourseID { get; set; } public int StudentID { get; set; } public decimal? Grade { get; set; } public virtual Course Course { get; set; } public virtual Student Student { get; set; } } } The question mark after the decimal type declaration indicates that the Grade property is nullable. A grade that's null is different from a zero grade null means a grade hasn't been assigned yet, while zero means a zero grade has been assigned. The StudentID property is a foreign key, and the corresponding navigation property is Student. An Enrollmententity is associated with one Student entity, so the property can only hold a single Student entity (unlike theStudent.Enrollments navigation property you saw earlier, which can hold multiple Enrollment entities). The CourseID property is a foreign key, and the corresponding navigation property is Course. An Enrollmententity is associated with one Course entity.

The Course Entity

In the Models folder, create Course.cs, replacing the existing code with the following code: using System; using System.Collections.Generic; namespace ContosoUniversity.Models { public class Course { public int CourseID { get; set; } public string Title { get; set; } public int Credits { get; set; }

public virtual ICollection<Enrollment> Enrollments { get; set; } } } The Enrollments property is a navigation property. A Course entity can be related to any number of Enrollmententities.

Creating the Database Context

The main class that coordinates Entity Framework functionality for a given data model is the database context class. You create this class by deriving from the System.Data.Entity.DbContext class. In your code you specify which entities are included in the data model. You can also customize certain Entity Framework behavior. In the code for this project, the class is named SchoolContext. Create a DAL folder. In that folder create a new class file named SchoolContext.cs, and replace the existing code with the following code: using using using using using System; System.Collections.Generic; System.Data.Entity; ContosoUniversity.Models; System.Data.Entity.ModelConfiguration.Conventions;

namespace ContosoUniversity.Models { public class SchoolContext : DbContext { public DbSet<Student> Students { get; set; } public DbSet<Enrollment> Enrollments { get; set; } public DbSet<Course> Courses { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); } } } This code creates a DbSet property for each entity set. In Entity Framework terminology, an entity set typically corresponds to a database table, and an entity corresponds to a row in the table. The statement in the OnModelCreating method prevents table names from being pluralized. If you didn't do this, the generated tables would be named Students, Courses, and Enrollments. Instead, the table names will beStudent, Course, and Enrollment. Developers disagree about whether table names should be pluralized or not. This tutorial uses the singular form, but the important point is that you can select whichever form you prefer by including or omitting this line of code. (This class is in the Models namespace, because in some situations Code First assumes that the entity classes and the context class are in the same namespace.)

Setting the Connection String

You don't have to create a connection string. If you don't create one, the Entity Framework will automatically create a SQL Server Express database for you. In this tutorial, however, you'll work with SQL Server Compact, so you need to create a connection string to specify that. Open the project Web.config file and add a new connection string to the connectionStrings collection, as shown in the following example. (Make sure you update the Web.config file in the root project folder. There's also aWeb.config file is in the Views subfolder that you don't need to update. ) <add name="SchoolContext" connectionString="Data Source=|DataDirectory|School.sdf" providerName="System.Data.SqlServerCe.4.0"/> By default, the Entity Framework looks for a connection string named the same as the object context class. The connection string you've added specifies a SQL Server Compact database named School.sdf located in the App_Datafolder.

Initializing the Database with Test Data


The Entity Framework can automatically create (or drop and re-create) a database for you when the application runs. You can specify that this should be done every time your application runs or only when the model is out of sync with the existing database. You can also write a class that includes a method that the Entity Framework automatically calls after creating the database in order to populate it with test data. In this section you'll specify that the database should be dropped and re-created whenever the model changes. In the DAL folder, create a new class file named SchoolInitializer.cs and replace the existing code with the following code, which causes a database to be created when needed and loads test data into the new database. using using using using using using System; System.Collections.Generic; System.Linq; System.Web; System.Data.Entity; ContosoUniversity.Models;

namespace ContosoUniversity.DAL { public class SchoolInitializer : DropCreateDatabaseIfModelChanges<SchoolContext> { protected override void Seed(SchoolContext context) { var students = new List<Student> { new Student { FirstMidName = "Carson", LastName = "Alexander", EnrollmentDate = DateTime.Parse("2005-09-01") }, new Student { FirstMidName = "Meredith", LastName = "Alonso", EnrollmentDate = DateTime.Parse("2002-09-01") }, new Student { FirstMidName = "Arturo", LastName = "Anand", EnrollmentDate = DateTime.Parse("2003-09-01") },

new Student { FirstMidName = "Gytis", LastName EnrollmentDate = DateTime.Parse("2002-09-01") }, new Student { FirstMidName = "Yan", LastName EnrollmentDate = DateTime.Parse("2002-09-01") }, new Student { FirstMidName = "Peggy", LastName EnrollmentDate = DateTime.Parse("2001-09-01") }, new Student { FirstMidName = "Laura", LastName EnrollmentDate = DateTime.Parse("2003-09-01") }, new Student { FirstMidName = "Nino", LastName EnrollmentDate = DateTime.Parse("2005-09-01") } }; students.ForEach(s => context.Students.Add(s)); context.SaveChanges(); var courses = new List<Course> { new Course { Title = "Chemistry", Credits new Course { Title = "Microeconomics", Credits new Course { Title = "Macroeconomics", Credits new Course { Title = "Calculus", Credits new Course { Title = "Trigonometry", Credits new Course { Title = "Composition", Credits new Course { Title = "Literature", Credits }; courses.ForEach(s => context.Courses.Add(s)); context.SaveChanges();

= "Barzdukas", = "Li", = "Justice", = "Norman", = "Olivetto",

= = = = = = =

3, 3, 3, 4, 4, 3, 4,

}, }, }, }, }, }, }

var enrollments = new List<Enrollment> { new Enrollment { StudentID = 1, CourseID = 1, Grade new Enrollment { StudentID = 1, CourseID = 2, Grade new Enrollment { StudentID = 1, CourseID = 3, Grade new Enrollment { StudentID = 2, CourseID = 4, Grade new Enrollment { StudentID = 2, CourseID = 5, Grade new Enrollment { StudentID = 2, CourseID = 6, Grade new Enrollment { StudentID = 3, CourseID = 1 new Enrollment { StudentID = 4, CourseID = 1, new Enrollment { StudentID = 4, CourseID = 2, Grade new Enrollment { StudentID = 5, CourseID = 3, Grade new Enrollment { StudentID = 6, CourseID = 4 new Enrollment { StudentID = 7, CourseID = 5, Grade }; enrollments.ForEach(s => context.Enrollments.Add(s)); context.SaveChanges(); } } }

= = = = = =

}, }, }, }, }, }, }, }, = 4 }, = 3 }, }, = 2 },

1 3 1 2 4 4

The Seed method takes the database context object as an input parameter, and the code in the method uses that object to add new entities to the database. For each entity type, the code creates a collection of new entities, adds them to the appropriate DbSet property, and then saves the changes to the database. It isn't necessary to call theSaveChanges method after each group of entities, as is done here, but doing that helps you locate the source of a problem if an exception occurs while the code is writing to the database. Make the following changes in the Global.asax.cs file to cause this initializer code to run when the application begins: Add using statements: using System.Data.Entity; using ContosoUniversity.Models; using ContosoUniversity.DAL; In the Application_Start method, call an Entity Framework method that runs the database initializer code: Database.SetInitializer<SchoolContext>(new SchoolInitializer()); The application is now set up so that when you access the database for the first time in a given run of the application, the Entity Framework compares the database to the model (your SchoolContext class). If there's a difference, the application drops and re-creates the database. Note When you deploy an application to a production web server, you must remove code that seeds the database. Now you'll create a web page to display data, and the process of requesting the data will automatically trigger the creation of the database. You'll begin by creating a new controller. But before you do that, build the project to make the model and context classes available to MVC controller scaffolding.

Creating a Student Controller


To create a Student controller, right-click the Controllers folder in Solution Explorer, select Add, and then clickController. In the Add Controller dialog box, make the following selections and then click Add: Controller name: StudentController. Template: Controller with read/write actions and views, using Entity Framework. (The default.) Model class: Student (ContosoUniversity.Models). (If you don't see this option in the drop-down list, build the project and try again.) Data context class: SchoolContext (ContosoUniversity.Models). Views: Razor (CSHTML). (The default.)

Open the Controllers\StudentController.cs file. You see a class variable has been created that instantiates a database context object: private SchoolContext db = new SchoolContext(); The Index action method gets a list of students from the Students property of the database context instance: public ViewResult Index() { return View(db.Students.ToList()); } The automatic scaffolding has also created a set of Student views. To customize the default headings and column order in the Index view, open Views\Student\Index.cshtml and replace the existing code with the following code: @model IEnumerable<ContosoUniversity.Models.Student> @{ ViewBag.Title = "Students"; } <h2>Students</h2> <p> @Html.ActionLink("Create New", "Create") </p>

<table> <tr> <th></th> <th>Last Name</th> <th>First Name</th> <th>Enrollment Date</th> </tr> @foreach (var item in Model) { <tr> <td> @Html.ActionLink("Edit", "Edit", new { id=item.StudentID }) | @Html.ActionLink("Details", "Details", new { id=item.StudentID }) | @Html.ActionLink("Delete", "Delete", new { id=item.StudentID }) </td> <td> @Html.DisplayFor(modelItem => item.LastName) </td> <td> @Html.DisplayFor(modelItem => item.FirstMidName) </td> <td> @Html.DisplayFor(modelItem => item.EnrollmentDate) </td> </tr> } </table> Run the site, click the Students tab, and you see a list of students.

Close the browser. In Solution Explorer, select the ContosoUniversity project (make sure the project and not the solution is selected). Click Show all Files if you aren't already in that mode. Click Refresh and then expand theApp_Data folder to see the School.sdf file.

Double-click School.sdf to open Server Explorer. Then expand the Tables folder to see the tables that have been created in the database. Note If you get an error when you double-click School.sdf, make sure you have installed Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0. (For links to the software, see the list of prerequisites at the top of this page.) If you install the tools now, you'll have to close and re-open Visual Studio.

There's one table for each entity set, plus one additional table. The EdmMetadata table is used by the Entity Framework to determine when the model and the database are out of sync.

Right-click one of the tables and select Show Table Data to see the data that was loaded in the table by theSchoolInitializer class.

When you're finished, close the connection. (If you don't close the connection, you might get an error the next time you run the project).

Conventions
The amount of code you had to write in order for the Entity Framework to be able to create a complete database for you is minimal because of the use of conventions, or assumptions that the Entity Framework makes. Some of them have already been noted: The pluralized forms of entity class names are used as table names. Entity property names are used for column names. Entity properties that are named ID or classnameID are recognized as primary key properties. The Entity Framework connects to your database by looking for a connection string that has the same name as your context class (in this case, SchoolContext). You've seen that conventions can be overridden (for example, you specified that table names shouldn't be pluralized), and you'll learn more about conventions and how to override them in the Creating a More Complex Data Model tutorial later in this series. You've now created a simple application that uses the Entity Framework and SQL Server Compact to store and display data. In the following tutorial you'll learn how to perform basic CRUD (create, read, update, delete) operations. Links to other Entity Framework resources can be found at the end of the last tutorial in this series.

Implementing Basic CRUD Functionality with the Entity Framework in ASP.NET MVC Application (2 of 10)
By Tom Dykstra|April 11, 2011 The Contoso University sample web application demonstrates how to create ASP.NET MVC applications using the Entity Framework. The sample application is a website for a fictional Contoso University. It includes functionality such as student admission, course creation, and instructor assignments. This tutorial series explains the steps taken to build the Contoso University sample application. You candownload the completed application or create it by following the steps in the tutorial. The tutorial shows examples in C#. The downloadable sample contains code in both C# and Visual Basic. If you have questions that are not directly related to the tutorial, you can post them to the ASP.NET Entity Framework forum or theEntity Framework and LINQ to Entities forum. This tutorial series assumes you know how to work with ASP.NET MVC in Visual Studio. If you dont, a good place to start is a basic ASP.NET MVC Tutorial. If you prefer to work with the ASP.NET Web Forms model, see the Getting Started with the Entity Framework and Continuing with the Entity Framework tutorials. Before you start, make sure you have the following software installed on your computer: Visual Studio 2010 SP1 or Visual Web Developer Express 2010 SP1 (If you use one of these links, the following items will be installed automatically.) ASP.NET MVC 3 Tools Update Microsoft SQL Server Compact 4.0 Microsoft Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0 In the previous tutorial you created an MVC application that stores and displays data using the Entity Framework and SQL Server Compact. In this tutorial you will review and customize the CRUD (create, read, update, delete) code that the MVC scaffolding automatically creates for you in controllers and views. Note It's a common practice to implement the repository pattern in order to create an abstraction layer between your controller and the data access layer. To keep these tutorials simple, you won't implement a repository until a later tutorial in this series (Implementing the Repository and Unit of Work Patterns). In this tutorial, you will create the following web pages:

Creating a Details Page

The scaffolded code for the Index page left out the Enrollments property, because that property holds a collection. In the Details page you will display the contents of the collection in an HTML table. In Controllers\StudentController.cs, the action method for the Details view resembles the following example: public ViewResult Details(int id) { Student student = db.Students.Find(id); return View(student); } The code uses the Find method to retrieve a single Student entity corresponding to the key value that's passed to the method as the id parameter. The id value comes from a query string in the Details hyperlink on the Index page. Open Views\Student\Details.cshtml. Each field is displayed using a DisplayFor helper, as shown in the following example: <div class="display-label">LastName</div> <div class="display-field"> @Html.DisplayFor(model => model.LastName) </div> To display a list of enrollments, add the following code after the EnrollmentDate field, immediately before the closing fieldset tag: <div class="display-label"> @Html.LabelFor(model => model.Enrollments) </div> <div class="display-field"> <table> <tr> <th>Course Title</th> <th>Grade</th> </tr> @foreach (var item in Model.Enrollments) { <tr> <td> @Html.DisplayFor(modelItem => item.Course.Title) </td> <td> @Html.DisplayFor(modelItem => item.Grade) </td> </tr> } </table> </div> This code loops through the entities in the Enrollments navigation property. For each Enrollment entity in the property, it displays the course title and the grade. The course title is retrieved from the Course entity that's stored in the Course navigation property of the Enrollments entity. All of this

data is retrieved from the database automatically when it's needed. (In other words, you are using lazy loading here. You did not specify eager loadingfor the Courses navigation property, so the first time you try to access that property, a query is sent to the database to retrieve the data. You can read more about lazy loading and eager loading in the Reading Related Data tutorial later in this series.) Run the page by selecting the Students tab and clicking a Details hyperlink. You see the list of courses:

Creating a Create Page


In Controllers\StudentController.cs, replace the HttpPost Create action method with the following code to add atry-catch block to the scaffolded method: [HttpPost] public ActionResult Create(Student student) { try { if (ModelState.IsValid) { db.Students.Add(student); db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator."); } return View(student); } This code adds the Student entity created by the ASP.NET MVC model binder to the Students entity set and then saves the changes to the database. (Model binder refers to the ASP.NET MVC functionality that makes it easier for you to work with data submitted by a form; a model binder converts posted form values to .NET Framework types and passes them to the action method in parameters. In this case, the model binder instantiates a Student entity for you using property values from the Form collection.) The try-catch block is the only difference between this code and what the automatic scaffolding created. If an exception that derives from DataException is caught while the changes are being saved, a generic error message is displayed. These kinds of errors are typically caused by something external to the application rather than a programming error, so the user is advised to try again. The code in Views\Student\Create.cshtml is similar to what you saw in Details.cshtml, except that EditorFor and ValidationMessageFor helpers are used for each field instead of DisplayFor. The following example shows the relevant code: <div class="editor-label"> @Html.LabelFor(model => model.LastName) </div> <div class="editor-field"> @Html.EditorFor(model => model.LastName) @Html.ValidationMessageFor(model => model.LastName) </div> No changes are required in Create.cshtml. Run the page by selecting the Students tab and clicking Create New.

Data validation works by default. Enter names and an invalid date and click Create to see the error message.

In this case you're seeing client-side validation that's implemented using JavaScript. But server-side validation is also implemented. Even if client validation failed, bad data would be caught and an exception would be thrown in server code.

Change the date to a valid value such as 9/1/2005 and click Create to see the new student appear in the Indexpage.

Creating an Edit Page

In Controllers\StudentController.cs, the HttpGet Edit method (the one without the HttpPost attribute) uses the Findmethod to retrieve the selected Student entity, as you saw in the Details method. You don't need to change this method. However, replace the HttpPost Edit action method with the following code to add a try-catch block: [HttpPost] public ActionResult Edit(Student student) { try { if (ModelState.IsValid) { db.Entry(student).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator."); } return View(student); } This code is similar to what you saw in the HttpPost Create method. However, instead of adding the entity created by the model binder to the entity set, this code sets a flag on the entity that indicating it has been changed. When the SaveChanges method is called, the Modified flag causes the Entity Framework to create SQL statements to update the database row. All columns of the database row will be updated, including those that the user didn't change, and concurrency conflicts are ignored. (You will learn how to handle concurrency in the Handling Concurrency tutorial later in this series.)

Entity States and the Attach and SaveChanges Methods


The database context keeps track of whether entities in memory are in sync with their corresponding rows in the database, and this information determines what happens when you call the SaveChanges method. For example, when you pass a new entity to the Add method, that entity's state is set to Added. Then when you call theSaveChanges method, the database context issues a SQL INSERT command. An entity may be in one of the following states: Added. The entity does not yet exist in the database. The SaveChanges method must issue an INSERTstatement. Unchanged. Nothing needs to be done with this entity by the SaveChanges method. When you read an entity from the database, the entity starts out with this status. Modified. Some or all of the entity's property values have been modified. The SaveChanges method must issue an UPDATE statement. Deleted. The entity has been marked for deletion. The SaveChanges method must issue a DELETE statement. Detached. The entity isn't being tracked by the database context.

In a desktop application, state changes are typically set automatically. In this type of application, you read an entity and make changes to some of its property values. This causes its entity state to automatically be changed toModified. Then when you call SaveChanges, the Entity Framework generates a SQL UPDATE statement that updates only the actual properties that you changed. However, in a web application this sequence is interrupted, because the database context instance that reads an entity is disposed after a page is rendered. When the HttpPost Edit action method is called, this is the result of a new request and you have a new instance of the context, so you have to manually set the entity state to Modified.Then when you call SaveChanges, the Entity Framework updates all columns of the database row, because the context has no way to know which properties you changed. If you want the SQL Update statement to update only the fields that the user actually changed, you can save the original values in some way (such as hidden fields) so that they are available when the HttpPost Edit method is called. Then you can create a Student entity using the original values, call the Attach method with that original version of the entity, update the entity's values to the new values, and then call SaveChanges. For more information, see Add/Attach and Entity States and Local Data on the Entity Framework team blog. The code in Views\Student\Edit.cshtml is similar to what you saw in Create.cshtml, and no changes are required. Run the page by selecting the Students tab and then clicking an Edit hyperlink.

Change some of the data and click Save. You see the changed data in the Index page.

Creating a Delete Page

In Controllers\StudentController.cs, the template code for the HttpGet Delete method uses the Find method to retrieve the selected Student entity, as you saw in the Details and Edit methods. However, to implement a custom error message when the call to SaveChanges fails, you will add some functionality to this method and its corresponding view. As you saw for update and create operations, delete operations require two action methods. The method that is called in response to a GET request displays a view that gives the user a chance to approve or cancel the delete operation. If the user approves it, a POST request is created. When that happens, the HttpPost Delete method is called and then that method actually performs the delete operation. You will add a try-catch block to the HttpPost Delete method to handle any errors that might occur when the database is updated. If an error occurs, the HttpPost Delete method calls the HttpGet Delete method, passing it a parameter that indicates that an error has occurred. The HttpGet Delete method then redisplays the confirmation page along with the error message, giving the user an opportunity to cancel or to try again. Replace the HttpGet Delete action method with the following code, which manages error reporting: public ActionResult Delete(int id, bool? saveChangesError) { if (saveChangesError.GetValueOrDefault()) { ViewBag.ErrorMessage = "Unable to save changes. Try again, and if the problem persists see your system administrator."; } return View(db.Students.Find(id)); } This code accepts an optional Boolean parameter that indicates whether it was called after a failure to save changes. This parameter is null (false) when the HttpGet Delete method is called in response to a page request. When it is called by the HttpPost Delete method in response to a database update error, the parameter is trueand an error message is passed to the view. Replace the HttpPost Delete action method (named DeleteConfirmed) with the following code, which performs the actual delete operation and catches any database update errors. [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) { try { Student student = db.Students.Find(id); db.Students.Remove(student); db.SaveChanges(); } catch (DataException) { //Log the error (add a variable name after DataException) return RedirectToAction("Delete", new System.Web.Routing.RouteValueDictionary { { "id", id }, { "saveChangesError", true } }); }

return RedirectToAction("Index"); } This code retrieves the selected entity, then calls the Remove method to set the entity's status to Deleted. WhenSaveChanges is called, a SQL DELETE command is generated. If improving performance in a high-volume application is a priority, you could avoid an unnecessary SQL query to retrieve the row by replacing the lines of code that call the Find and Remove methods with the following code: Student studentToDelete = new Student() { StudentID = id }; db.Entry(studentToDelete).State = EntityState.Deleted; This code instantiates a Student entity using only the primary key value and then sets the entity state to Deleted. That's all that the Entity Framework needs in order to delete the entity. As noted, the HttpGet Delete method doesn't delete the data. Performing a delete operation in response to a GET request (or for that matter, performing an edit operation, create operation, or any other operation that changes data) creates a security risk. For more information, see ASP.NET MVC Tip #46 Don't use Delete Links because they create Security Holes on Stephen Walther's blog. In Views\Student\Delete.cshtml, add the following code between the h2 heading and the h3 heading: <p class="error">@ViewBag.ErrorMessage</p> Run the page by selecting the Students tab and clicking a Delete hyperlink:

Click Delete. The Index page is displayed without the deleted student. (You'll see an example of the error handling code in action in the Handling Concurrency tutorial later in this series.)

Ensuring that Database Connections Are Not Left Open

To make sure that database connections are properly closed and the resources they hold freed up, you should see to it that the context instance is disposed. That is why you will find a Dispose method at the end of theStudentController class in StudentController.cs, as shown in the following example: protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); } The base Controller class already implements the IDisposable interface, so this code simply adds an override to the Dispose(bool) method to explicitly dispose the context instance. You now have a complete set of pages that perform simple CRUD operations for Student entities. In the next tutorial you'll expand the functionality of the Index page by adding sorting and paging.

Sorting, Filtering, and Paging with the Entity Framework in an ASP.NET MVC Application (3 of 10)
By Tom Dykstra|April 11, 2011 The Contoso University sample web application demonstrates how to create ASP.NET MVC applications using the Entity Framework. The sample application is a website for a fictional Contoso University. It includes functionality such as student admission, course creation, and instructor assignments. This tutorial series explains the steps taken to build the Contoso University sample application. You candownload the completed application or create it by following the steps in the tutorial. The tutorial shows examples in C#. The downloadable sample contains code in both C# and Visual Basic. If you have questions that are not directly related to the tutorial, you can post them to the ASP.NET Entity Framework forum or theEntity Framework and LINQ to Entities forum. This tutorial series assumes you know how to work with ASP.NET MVC in Visual Studio. If you dont, a good place to start is a basic ASP.NET MVC Tutorial. If you prefer to work with the ASP.NET Web Forms model, see the Getting Started with the Entity Framework and Continuing with the Entity Framework tutorials. Before you start, make sure you have the following software installed on your computer: Visual Studio 2010 SP1 or Visual Web Developer Express 2010 SP1 (If you use one of these links, the following items will be installed automatically.) ASP.NET MVC 3 Tools Update Microsoft SQL Server Compact 4.0 Microsoft Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0 In the previous tutorial you implemented a set of web pages for basic CRUD operations for Student entities. In this tutorial you'll add sorting, filtering, and paging functionality to the Students Index page. You'll also create a page that does simple grouping.

The following illustration shows what the page will look like when you're done. The column headings are links that the user can click to sort by that column. Clicking a column heading repeatedly toggles between ascending and descending sort order.

Adding Column Sort Links to the Students Index Page


To add sorting to the Student Index page, you'll change the Index method of the Student controller and add code to the Student Index view.

Adding Sorting Functionality to the Index Method


In Controllers\StudentController.cs, replace the Index method with the following code: public ViewResult Index(string sortOrder) { ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Name desc" : ""; ViewBag.DateSortParm = sortOrder == "Date" ? "Date desc" : "Date"; var students = from s in db.Students select s; switch (sortOrder) { case "Name desc": students = students.OrderByDescending(s => s.LastName); break; case "Date": students = students.OrderBy(s => s.EnrollmentDate); break; case "Date desc": students = students.OrderByDescending(s => s.EnrollmentDate); break; default: students = students.OrderBy(s => s.LastName); break; } return View(students.ToList()); } This code receives a sortOrder parameter from the query string in the URL, which is provided by ASP.NET MVC as a parameter to the action method. The parameter will be a string that's either "Name" or "Date", optionally followed by a space and the string "desc" to specify descending order. The first time the Index page is requested, there's no query string. The students are displayed in ascending order by LastName, which is the default as established by the fall-through case in the switch statement. When the user clicks a column heading hyperlink, the appropriate sortOrder value is provided in the query string. The two ViewBag variables are used so that the view can configure the column heading hyperlinks with the appropriate query string values: ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Name desc" : ""; ViewBag.DateSortParm = sortOrder == "Date" ? "Date desc" : "Date"; These are ternary statements. The first one specifies that if the sortOrder parameter is null or empty,ViewBag.NameSortParm should be set to "Name desc"; otherwise, it should be set to an empty string. There are four possibilities, depending on how the data is currently sorted: If the current order is Last Name ascending, the Last Name link must specify Last Name descending, and theEnrollment Date link must specify Date ascending. If the current order is Last Name descending, the links must indicate Last Name ascending (that is, empty string) and Date ascending.

If the current order is Date ascending, the links must indicate Last Name ascending and Date descending. If the current order is Date descending, the links must indicate Last Name ascending and Date ascending. The method uses LINQ to Entities to specify the column to sort by. The code creates an IQueryable variable before the switch statement, modifies it in the switch statement, and calls the ToList method after the switch statement. When you create and modify IQueryable variables, no query is sent to the database. The query is not executed until you convert the IQueryable object into a collection by calling a method such as ToList. Therefore, this code results in a single query that is not executed until the return View statement.

Adding Column Heading Hyperlinks to the Student Index View

In Views\Student\Index.cshtml, replace the <tr> and <th> elements for the heading row with the following code: <tr> <th></th> <th> @Html.ActionLink("Last Name", "Index", new { sortOrder=ViewBag.NameSortParm }) </th> <th> First Name </th> <th> @Html.ActionLink("Enrollment Date", "Index", new { sortOrder=ViewBag.DateSortParm }) </th> </tr> This code uses the information in the ViewBag properties to set up hyperlinks with the appropriate query string values. Run the page and click the column headings to verify that sorting works.

Adding a Search Box to the Students Index Page

To add filtering to the Student Index page, you'll add a text box and a submit button to the view and make corresponding changes in the Index method. The text box will let you enter a string to search for in the first name and last name fields.

Adding Filtering Functionality to the Index Method


In Controllers\StudentController.cs, replace the Index method with the following code: public ViewResult Index(string sortOrder, string searchString) { ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Name desc" : ""; ViewBag.DateSortParm = sortOrder == "Date" ? "Date desc" : "Date"; var students = from s in db.Students select s; if (!String.IsNullOrEmpty(searchString)) { students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper()) || s.FirstMidName.ToUpper().Contains(searchString.ToUpper())); } switch (sortOrder) { case "Name desc": students = students.OrderByDescending(s => s.LastName); break; case "Date": students = students.OrderBy(s => s.EnrollmentDate); break; case "Date desc": students = students.OrderByDescending(s => s.EnrollmentDate); break; default: students = students.OrderBy(s => s.LastName); break; } return View(students.ToList()); } You've added a searchString parameter to the Index method. You've also added a where clause to the LINQ statement that selects only students whose first name or last name contains the search string. The search string value is received from a text box that you'll add later to the Index view. The statement that adds the where clause is executed only if there's a value to search for: if (!String.IsNullOrEmpty(searchString)) { students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper()) ||

s.FirstMidName.ToUpper().Contains(searchString.ToUpper())); } Note The .NET Framework implementation of the Contains method returns all rows when you pass an empty string to it, but the Entity Framework provider for SQL Server Compact 4.0 returns zero rows for empty strings. Therefore the code in the example (putting the Wherestatement inside an if statement) makes sure that you get the same results for all versions of SQL Server. Also, the .NET Framework implementation of the Contains method performs a case-sensitive comparison by default, but Entity Framework SQL Server providers perform case-insensitive comparisons by default. Therefore, calling the ToUpper method to make the test explicitly case-insensitive ensures that results do not change when you change the code later to use a repository, which will return an IEnumerable collection instead of an IQueryableobject. (When you call the Contains method on an IEnumerable collection, you get the .NET Framework implementation; when you call it on an IQueryable object, you get the database provider implementation.)

Adding a Search Box to the Student Index View


In Views\Student\Index.cshtml, add a caption, a text box, and a Search button immediately before the opening tabletag: @using (Html.BeginForm()) { <p> Find by name: @Html.TextBox("SearchString") <input type="submit" value="Search" /></p> } Run the page, enter a search string, and click Search to verify that filtering is working.

Adding Paging to the Students Index Page


To add paging to the Student Index page, you'll start by installing the PagedList NuGet package. Then you'll make additional changes in the Index method and add paging links to the Index view. The following illustration shows the paging links.

Installing the PagedList NuGet Package


The NuGet PagedList package installs a PagedList collection type. When you put query results in a PagedListcollection, several properties and methods are provided that facilitate paging. In Visual Studio, make sure the project (not the solution) is selected. From the Tools menu, select Library Package Manager and then Add Library Package Reference. In the Add Library Package Reference dialog box, click the Online tab on the left and then enter "pagedlist" in the search box. When you see the PagedList package, click Install.

Adding Paging Functionality to the Index Method


In Controllers\StudentController.cs, add a using statement for the PagedList namespace: using PagedList; Replace the Index method with the following code: public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page) { ViewBag.CurrentSort = sortOrder; ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Name desc" : ""; ViewBag.DateSortParm = sortOrder == "Date" ? "Date desc" : "Date"; if (Request.HttpMethod == "GET") { searchString = currentFilter; } else {

page = 1; } ViewBag.CurrentFilter = searchString; var students = from s in db.Students select s; if (!String.IsNullOrEmpty(searchString)) { students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper()) || s.FirstMidName.ToUpper().Contains(searchString.ToUpper())); } switch (sortOrder) { case "Name desc": students = students.OrderByDescending(s => s.LastName); break; case "Date": students = students.OrderBy(s => s.EnrollmentDate); break; case "Date desc": students = students.OrderByDescending(s => s.EnrollmentDate); break; default: students = students.OrderBy(s => s.LastName); break; } int pageSize = 3; int pageNumber = (page ?? 1); return View(students.ToPagedList(pageNumber, pageSize)); } This code adds a page parameter, a current sort order parameter, and a current filter parameter to the method signature, as shown here: public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page) The first time the page is displayed, or if the user hasn't clicked a paging link, the page variable is null. If a paging link is clicked, the page variable will contain the page number to display. A ViewBag property provides the view with the current sort order, because this must be included in the paging links in order to keep the sort order the same while paging: ViewBag.CurrentSort = sortOrder; Another ViewBag property provides the view with the current filter string, because this string must be restored to the text box when the page is redisplayed. In addition, the string must be included in the paging links in order to maintain the filter settings during paging. Finally, if the search string is changed

during paging, the page has to be reset to 1, because the new filter can result in different data to display, hence the original page might not even exist anymore. if (Request.HttpMethod == "GET") { searchString = currentFilter; } else { page = 1; } ViewBag.CurrentFilter = searchString; At the end of the method, the student query is converted to a PagedList instead of to a List so that it will be passed to the view in a collection that supports paging. This is the code: int pageSize = 3; int pageNumber = (page ?? 1); return View(students.ToPagedList(pageNumber, pageSize)); The ToPagedList method takes a page number value. The two question marks represent an operator that defines a default value for a nullable type; the expression (page ?? 1) means return the value of page if it has a value, or return 1 if page is null.

Adding Paging Links to the Student Index View

In Views\Student\Index.cshtml, replace the existing code with the following code: @model PagedList.IPagedList<ContosoUniversity.Models.Student> @{ ViewBag.Title = "Students"; } <h2>Students</h2> <p> @Html.ActionLink("Create New", "Create") </p> @using (Html.BeginForm()) { <p> Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string) <input type="submit" value="Search" /></p> } <table> <tr> <th></th> <th> @Html.ActionLink("Last Name", "Index", new { sortOrder=ViewBag.NameSortParm,

currentFilter=ViewBag.CurrentFilter }) </th> <th> First Name </th> <th> @Html.ActionLink("Enrollment Date", "Index", new { sortOrder = ViewBag.DateSortParm, currentFilter = ViewBag.CurrentFilter }) </th> </tr> @foreach (var item in Model) { <tr> <td> @Html.ActionLink("Edit", "Edit", new { id=item.StudentID }) | @Html.ActionLink("Details", "Details", new { id=item.StudentID }) | @Html.ActionLink("Delete", "Delete", new { id=item.StudentID }) </td> <td> @Html.DisplayFor(modelItem => item.LastName) </td> <td> @Html.DisplayFor(modelItem => item.FirstMidName) </td> <td> @Html.DisplayFor(modelItem => item.EnrollmentDate) </td> </tr> } </table> <div> Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount @if (Model.HasPreviousPage) { @Html.ActionLink("<<", "Index", new { page = 1, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter }) @Html.Raw(" "); @Html.ActionLink("< Prev", "Index", new { page = Model.PageNumber - 1, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter }) } else { @:<<

@Html.Raw(" "); @:< Prev } @if (Model.HasNextPage) { @Html.ActionLink("Next >", "Index", new { page = Model.PageNumber + 1, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter }) @Html.Raw(" "); @Html.ActionLink(">>", "Index", new { page = Model.PageCount, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter }) } else { @:Next > @Html.Raw(" ") @:>> } </div> The @model statement at the top of the page specifies that the view now gets a PagedList object instead of a Listobject. The text box is initialized with the current search string so that the user can page through filter results without the search string disappearing: Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string) The column header links use the query string to pass the current search string to the controller so that the user can sort within filter results: @Html.ActionLink("Last Name", "Index", new { sortOrder=ViewBag.NameSortParm, currentFilter=ViewBag.CurrentFilter }) On one line at the bottom of the page, this code displays the following navigation UI: Page [current page number] of [total number of pages] << < Prev Next > >> The << symbol is a link to the first page, < Prev is a link to the previous page, and so on. If the user is currently on page 1, the links to move backward are disabled; similarly, if the user is on the last page, the links to move forward are disabled. Each paging link passes the new page number and the current sort order and search string to the controller in the query string. This lets you maintain the sort order and filter results during paging. If there are no pages to display, "Page 0 of 0" is shown. (In that case the page number is greater than the page count because Model.PageNumber is 1, and Model.PageCount is 0.) Run the page.

Click the paging links in different sort orders to make sure paging works. Then enter a search string and try paging again to verify that paging also works correctly with sorting and filtering.

Creating an About Page That Shows Student Statistics


For the Contoso University website's About page, you'll display how many students have enrolled for each enrollment date. This requires grouping and simple calculations on the groups. To accomplish this, you'll do the following:

Create a view model class for the data that you need to pass to the view. Modify the About method in the Home controller. Modify the About view.

Creating the View Model

Create a ViewModels folder. In that folder, create EnrollmentDateGroup.cs and replace the existing code with the following code: using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.ViewModels { public class EnrollmentDateGroup { [DisplayFormat(DataFormatString = "{0:d}")] public DateTime? EnrollmentDate { get; set; } public int StudentCount { get; set; } } }

Modifying the Home Controller


In HomeController.cs, add the following using statements: using ContosoUniversity.DAL; using ContosoUniversity.Models; using ContosoUniversity.ViewModels; Add a class variable for the database context: private SchoolContext db = new SchoolContext(); Replace the About method with the following code: public ActionResult About() { var data = from student in db.Students group student by student.EnrollmentDate into dateGroup select new EnrollmentDateGroup() { EnrollmentDate = dateGroup.Key, StudentCount = dateGroup.Count() }; return View(data); } The LINQ statement groups the student entities by enrollment date, calculates the number of entities in each group, and stores the results in a collection of EnrollmentDateGroup view model objects.

Add a Dispose method: protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); }

Modifying the About View


Replace the code in the Views\Home\About.cshtml file with the following code: @model IEnumerable<ContosoUniversity.ViewModels.EnrollmentDateGroup> @{ ViewBag.Title = "Student Body Statistics"; } <h2>Student Body Statistics</h2> <table> <tr> <th> Enrollment Date </th> <th> Students </th> </tr> @foreach (var item in Model) { <tr> <td> @String.Format("{0:d}", item.EnrollmentDate) </td> <td> @item.StudentCount </td> </tr> } </table> Run the page. The count of students for each enrollment date is displayed in a table.

You've now seen how to create a data model and implement basic CRUD, sorting, filtering, paging, and grouping functionality. In the next tutorial you'll begin looking at more advanced topics by expanding the data model. Links to other Entity Framework resources can be found at the end of the last tutorial in this series.

Creating a More Complex Data Model for an ASP.NET MVC Application (4 of 10)
By Tom Dykstra|April 11, 2011

The Contoso University sample web application demonstrates how to create ASP.NET MVC applications using the Entity Framework. The sample application is a website for a fictional Contoso University. It includes functionality such as student admission, course creation, and instructor assignments. This tutorial series explains the steps taken to build the Contoso University sample application. You candownload the completed application or create it by following the steps in the tutorial. The tutorial shows examples in C#. The downloadable sample contains code in both C# and Visual Basic. If you have questions that are not directly related to the tutorial, you can post them to the ASP.NET Entity Framework forum or theEntity Framework and LINQ to Entities forum. This tutorial series assumes you know how to work with ASP.NET MVC in Visual Studio. If you dont, a good place to start is a basic ASP.NET MVC Tutorial. If you prefer to work with the ASP.NET Web Forms model, see the Getting Started with the Entity Framework and Continuing with the Entity Framework tutorials. Before you start, make sure you have the following software installed on your computer: Visual Studio 2010 SP1 or Visual Web Developer Express 2010 SP1 (If you use one of these links, the following items will be installed automatically.) ASP.NET MVC 3 Tools Update Microsoft SQL Server Compact 4.0 Microsoft Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0 In the previous tutorials you worked with a simple data model that was composed of three entities. In this tutorial you'll add more entities and relationships and make use of data annotation attributes to control the behavior of your model classes. When you're finished, the entity classes will make up the completed data model that's shown in the following illustration:

Using Attributes to Control Formatting, Validation, and Database Mapping


In this section you'll see examples of attributes you can add to model classes to specify formatting, validation, and database mapping. Then in the following sections you'll create the complete School data model by adding attributes to the classes you already created and creating new classes for the remaining entity types in the model.

The DisplayFormat Attribute


For student enrollment dates, all of the web pages currently display the time along with the date, although all you care about for this field is the date. By using data annotation attributes, you can make one code change that will fix the display format everywhere. To see an example of that, you'll add an attribute to the EnrollmentDate property in the Student class. In Models\Student.cs, add a using statement for the System.ComponentModel.DataAnnotations namespace and add a DisplayFormat attribute to the EnrollmentDate property, as shown in the following example: using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Student { public int StudentID { get; set; } public string LastName { get; set; } public string FirstMidName { get; set; } [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)] public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } } } The format string specifies that only a short date should be displayed for this property. TheApplyFormatInEditMode setting specifies that this formatting should also be applied when the value is displayed in a text box for editing. (You might not want that for some fields for example, for currency values, you might not want the currency symbol in the text box for editing.) Run the Student Index page again and notice that times are no longer displayed for the enrollment dates. The same will be true if you run the other Student pages.

The MaxLength Attribute


You can also specify data validation rules and messages using attributes. Suppose you want to ensure that users don't enter more than 50 characters for a name. To add this limitation, add Range attributes to the LastName andFirstMidName properties, as shown in the following example: using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models { public class Student { public int StudentID { get; set; } [MaxLength(50)] public string LastName { get; set; } [MaxLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")] public string FirstMidName { get; set; } [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)] public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } } } If a user attempts to enter a last name that's too long, a default error message will be displayed. If a long first name is entered, the custom error message you specified will be displayed. Run the Create page, enter two names longer than 50 characters, and click Create to see the error messages. (You'll have to enter a valid date in order to get past the date validation.)

It's a good idea to always specify the maximum length for string properties. If you don't, when Code First creates the database, the corresponding columns will have the maximum length allowed for strings in the database, which would be an inefficient database table structure.

The Column Attribute


You can also use attributes to control how your classes and properties are mapped to the database. Suppose you had used the name FirstMidName for the first-name field because the field might also contain a middle name. But you want the database column to be named FirstName, because users who will be writing ad-hoc queries against the database are accustomed to that name. To make this mapping, you can use the Column attribute. The Column attribute specifies that when the database is created, the column of the Student table that maps to theFirstMidName property will be named FirstName. In other words, when your code refers toStudent.FirstMidName, the data will come from or be updated in the FirstName column of the Student table. (If you don't specify column names, they are assumed to be the same as property names.) Add the column name attribute to the FirstMidName property, as shown in the following example: [Column("FirstName")] public string FirstMidName { get; set; } Run the Student Index page again and you see that nothing has changed. (You can't just run the site and view the home page; you have to select the Student Index page because that causes the database to be accessed, which causes the database to be automatically dropped and re-created.) However, if you open the database in Server Explorer as you did earlier, you can expand the Student table to see that the column name is FirstName.

In the Properties window, you'll also notice that the name-related fields are defined as 50 characters in length, thanks to the MaxLength attributes you added earlier.

In most cases, you can also make mapping changes using method calls, as you'll see later in this tutorial. In the following sections you'll make more use of data annotations attributes as you expand the School data model. In each section you'll create a class for an entity or modify a class that you created in the first tutorial. Note If you try to compile before you finish creating all of these entity classes, you might get compiler errors.

Creating the Instructor Entity

Create Models\Instructor.cs, replacing the existing code with the following code:

using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Instructor { public Int32 InstructorID { get; set; } [Required(ErrorMessage = "Last name is required.")] [Display(Name="Last Name")] [MaxLength(50)] public string LastName { get; set; } [Required(ErrorMessage = "First name is required.")] [Column("FirstName")] [Display(Name = "First Name")] [MaxLength(50)] public string FirstMidName { get; set; } [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)] [Required(ErrorMessage = "Hire date is required.")] [Display(Name = "Hire Date")] public DateTime? HireDate { get; set; } public string FullName { get { return LastName + ", " + FirstMidName; } } public virtual ICollection<Course> Courses { get; set; } public virtual OfficeAssignment OfficeAssignment { get; set; } } } Notice that several properties are the same in the Student and Instructor entities. In the Implementing Inheritance tutorial later in this series, you'll refactor using inheritance to eliminate this redundancy.

The Required and Display Attributes


The attributes on the LastName property specify that it's a required field, that the caption for the text box should be "Last Name" (instead of the property name, which would be "LastName" with no space), and that the value can't be longer than 50 characters.

[Required(ErrorMessage = "Last name is required.")] [Display(Name="Last Name")] [MaxLength(50)] public string LastName { get; set; }

The FullName Calculated Property


FullName is a calculated property that returns a value that's created by concatenating two other properties. Therefore it has only a get accessor, and no FullName column will be generated in the database. public string FullName { get { return LastName + ", " + FirstMidName; } }

The Courses and OfficeAssignment Navigation Properties


The Courses and OfficeAssignment properties are navigation properties. As was explained earlier, they are typically defined as virtual so that they can take advantage of an Entity Framework feature called lazy loading. In addition, if a navigation property can hold multiple entities, its type must be ICollection. An instructor can teach any number of courses, so Courses is defined as a collection of Course entities. On the other hand, an instructor can only have one office, so OfficeAssignment is defined as a single OfficeAssignmententity (which may be null if no office is assigned). public virtual ICollection<Course> Courses { get; set; } public virtual OfficeAssignment OfficeAssignment { get; set; }

Creating the OfficeAssignment Entity

Create Models\OfficeAssignment.cs, replacing the existing code with the following code: using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models { public class OfficeAssignment { [Key] public int InstructorID { get; set; } [MaxLength(50)] [Display(Name = "Office Location")] public string Location { get; set; } public virtual Instructor Instructor { get; set; } } }

The Key Attribute


There's a one-to-zero-or-one relationship between the Instructor and the OfficeAssignment entities. An office assignment only exists in relation to the instructor it's assigned to, and therefore its primary key is also its foreign key to the Instructor entity. But the Entity Framework can't automatically recognize InstructorID as the primary key of this entity because its name doesn't follow the ID or classnameID naming convention. Therefore, the Keyattribute is used to identify it as the key: [Key] public int InstructorID { get; set; } You can also use the Key attribute if the entity does have its own primary key but you want to name the property something different than classnameID or ID.

The Instructor Navigation Property

The Instructor entity has a nullable OfficeAssignment navigation property (because an instructor might not have an office assignment), and the OfficeAssignment entity has a nonnullable Instructor navigation property (because an office assignment can't exist without an instructor). When an Instructor entity has a relatedOfficeAssignment entity, each entity will have a reference to the other one in its navigation property.

Modifying the Course Entity

In Models\Course.cs, replace the code you added earlier with the following code: using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Course { [DatabaseGenerated(DatabaseGeneratedOption.None)] [Display(Name = "Number")] public int CourseID { get; set; } [Required(ErrorMessage = "Title is required.")] [MaxLength(50)] public string Title { get; set; } [Required(ErrorMessage = "Number of credits is required.")] [Range(0,5,ErrorMessage="Number of credits must be between 0 and 5.")] public int Credits { get; set; } [Display(Name = "Department")] public int DepartmentID { get; set; } public virtual Department Department { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } public virtual ICollection<Instructor> Instructors { get; set; } } }

The DatabaseGenerated Attribute


The DatabaseGenerated attribute with the None parameter on the CourseID property specifies that primary key values are provided by the user rather than generated by the database.

[DatabaseGenerated(DatabaseGeneratedOption.None)] [Display(Name = "Number")] public int CourseID { get; set; } By default, the Entity Framework assumes that primary key values are generated by the database. That's what you want in most scenarios. However, for Course entities, you'll use a user-specified course number such as a 1000 series for one department, a 2000 series for another department, and so on.

Foreign Key and Navigation Properties


The foreign key properties and navigation properties in the Course entity reflect the following relationships: A course is assigned to one department, so there's a DepartmentID foreign key and a Department navigation property: public int DepartmentID { get; set; } public virtual Department Department { get; set; } A course can have any number of students enrolled in it, so there's an Enrollments navigation property: public virtual ICollection Enrollments { get; set; } A course may be taught by multiple instructors, so there's an Instructors navigation property: public virtual ICollection<Instructor> Instructors { get; set; }

Creating the Department Entity

Create Models\Department.cs, replacing the existing code with the following code: using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Department

{ public int DepartmentID { get; set; } [Required(ErrorMessage = "Department name is required.")] [MaxLength(50)] public string Name { get; set; } [DisplayFormat(DataFormatString="{0:c}")] [Required(ErrorMessage = "Budget is required.")] [Column(TypeName="money")] public decimal? Budget { get; set; } [DisplayFormat(DataFormatString="{0:d}", ApplyFormatInEditMode=true)] [Required(ErrorMessage = "Start date is required.")] public DateTime StartDate { get; set; } [Display(Name="Administrator")] public int? InstructorID { get; set; } public virtual Instructor Administrator { get; set; } public virtual ICollection<Course> Courses { get; set; } } }

The Column Attribute


Earlier you used the Column attribute to change column name mapping. In the code for the Department entity, theColumn attribute is being used to change SQL data type mapping so that the column will be defined using the SQL Server money type in the database: [Column(TypeName="money")] public decimal? Budget { get; set; } This is normally not required, because the Entity Framework chooses the appropriate SQL Server data type based on the CLR type that you define for the property. The CLR decimal type would normally map to a SQL Serverdecimal type. But in this case you know that the column will be holding currency amounts, and the money data type is more appropriate for that.

Foreign Key and Navigation Properties

The foreign key and navigation properties reflect the following relationships: A department may or may not have an administrator, and an administrator is always an instructor. Therefore the InstructorID property is included as the foreign key to the Instructor entity, and a question mark is added after the int type designation to mark the property as nullable. The navigation property is namedAdministrator but holds an Instructor entity: public int? InstructorID { get; set; } public virtual Instructor Administrator { get; set; } A department may have many courses, so there's a Courses navigation property:

public virtual ICollection Courses { get; set; } Note By convention, the Entity Framework enables cascade delete for non-nullable foreign keys and for many-to-many relationships. This can result in circular cascade delete rules, which will cause an exception when your initializer code runs. For example, if you didn't define theDepartment.InstructorID property as nullable, you'd get the following exception message when the initializer runs: "The referential relationship will result in a cyclical reference that's not allowed."

Modifying the Student Entity

In Models\Student.cs, replace the code you added earlier with the following code: using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Student { public int StudentID { get; set; } [Required(ErrorMessage = "Last name is required.")] [Display(Name="Last Name")] [MaxLength(50)] public string LastName { get; set; } [Required(ErrorMessage = "First name is required.")] [Column("FirstName")] [Display(Name = "First Name")] [MaxLength(50)] public string FirstMidName { get; set; } [Required(ErrorMessage = "Enrollment date is required.")] [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]

[Display(Name = "Enrollment Date")] public DateTime? EnrollmentDate { get; set; } public string FullName { get { return LastName + ", " + FirstMidName; } } public virtual ICollection<Enrollment> Enrollments { get; set; } } } This code just adds attributes that you've now already seen in the other classes.

Modifying the Enrollment Entity

In Models\Enrollment.cs, replace the code you added earlier with the following code: using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Enrollment { public int EnrollmentID { get; set; } public int CourseID { get; set; } public int StudentID { get; set; }

[DisplayFormat(DataFormatString="{0:#.#}",ApplyFormatInEditMode=true,NullDisplayText= "No grade")] public decimal? Grade { get; set; } public virtual Course Course { get; set; } public virtual Student Student { get; set; } } }

Foreign Key and Navigation Properties


The foreign key properties and navigation properties reflect the following relationships: An enrollment record is for a single course, so there's a CourseID foreign key property and a Coursenavigation property: public int CourseID { get; set; } public virtual Course Course { get; set; } An enrollment record is for a single student, so there's a StudentID foreign key property and a Studentnavigation property: public int StudentID { get; set; } public virtual Student Student { get; set; }

Many-to-Many Relationships
There's a many-to-many relationship between the Student and Course entities, and the Enrollment entity corresponds to a many-to-many join table with payload in the database. This means that the Enrollment table contains additional data besides foreign keys for the joined tables (in this case, a primary key and a Gradeproperty). The following illustration shows what these relationships look like in an entity diagram. (This diagram was generated using the Entity Framework designer; creating the diagram isn't part of the tutorial, it's just being used here as an illustration.)

Each relationship line has a 1 at one end and an asterisk (*) at the other, indicating a one-to-many relationship. If the Enrollment table didn't include grade information, it would only need to contain the two foreign keysCourseID and StudentID. In that case, it would correspond to a many-to-many join table without payload (or a pure join table) in the database, and you wouldn't have to create a model class for it at all. The Instructor and Courseentities have that kind of many-to-many relationship, and as you can see, there is no entity class between them:

A join table is required in the database, however, as shown in the following database diagram:

The Entity Framework automatically creates the CourseInstructor table, and you read and update it indirectly by reading and updating the Instructor.Courses and Course.Instructors navigation properties.

The DisplayFormat Attribute


The DisplayFormat attribute on the Grade property specifies how the data will be formatted: [DisplayFormat(DataFormatString="{0:#.#}",ApplyFormatInEditMode=true,NullDisplayText= "No grade")] public decimal? Grade { get; set; } The grade displays as two digits separated by a period for example, "3.5" or "4.0". The grade is also displayed this way in edit mode (in a text box). If there's no grade (the question mark after decimal indicates that the property is nullable), the text "No grade" is displayed.

Entity Diagram Showing Relationships


The following illustration shows the diagram that the Entity Framework Database First designer creates for the School model.

Besides the many-to-many relationship lines (* to *) and the one-to-many relationship lines (1 to *), you can see here the one-to-zero-or-one relationship line (1 to 0..1) between the Instructor and OfficeAssignment entities and the zero-or-one-to-many relationship line (0..1 to *) between the Instructor and Department entities.

Customizing the Database Context

Next you'll add the new entities to the SchoolContext class and customize some of the mapping using fluent API calls. (The API is "fluent" because it's often used by stringing a series of method calls together into a single statement.) In some cases you need to use methods rather than attributes because there's no attribute for a particular function. In other cases you can choose to use a method when both methods and attributes are available. (Some people prefer not to use attributes.) Replace the code in DAL\SchoolContext.cs with the following code: using using using using using System; System.Collections.Generic; System.Data.Entity; ContosoUniversity.Models; System.Data.Entity.ModelConfiguration.Conventions;

namespace ContosoUniversity.Models { public class SchoolContext : DbContext { public DbSet<Course> Courses { get; set; } public DbSet<Department> Departments { get; set; public DbSet<Enrollment> Enrollments { get; set; public DbSet<Instructor> Instructors { get; set; public DbSet<Student> Students { get; set; } public DbSet<OfficeAssignment> OfficeAssignments

} } } { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Entity<Instructor>() .HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor); modelBuilder.Entity<Course>() .HasMany(c => c.Instructors).WithMany(i => i.Courses) .Map(t => t.MapLeftKey("CourseID") .MapRightKey("InstructorID") .ToTable("CourseInstructor")); modelBuilder.Entity<Department>() .HasOptional(x => x.Administrator); } } } The new statements in the OnModelCreating method specify the following relationships: A one-to-zero-or-one relationship between the Instructor and OfficeAssignment entities: modelBuilder.Entity<Instructor>() .HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor); A many-to-many relationship between the Instructor and Course entities. The code specifies the table and column names for the join table. Code First can configure the many-to-many relationship

for you without this code, but if you don't call it, you will get default names such as InstructorInstructorID for theInstructorID column. modelBuilder.Entity<Course>() .HasMany(c => c.Instructors).WithMany(i => i.Courses) .Map(t => t.MapLeftKey("CourseID") .MapRightKey("InstructorID") .ToTable("CourseInstructor")); A zero-or-one-to-many relationship between the Instructor and Department tables. In other words, a department may or may not have an instructor assigned to it as administrator; the assigned administrator is represented by the Department.Administrator navigation property: modelBuilder.Entity<Department>() .HasOptional(x => x.Administrator); For more details about what these "fluent API" statements are doing behind the scenes, see the Fluent API blog post on the ASP.NET User Education Team's blog.

Initializing the Database with Test Data


Earlier you created DAL\SchoolInitializer.cs to initialize your database with test data. Now replace the code in that file with the following code in order to provide test data for the new entities you've created. using using using using using using System; System.Collections.Generic; System.Linq; System.Web; System.Data.Entity; ContosoUniversity.Models;

namespace ContosoUniversity.DAL { public class SchoolInitializer : DropCreateDatabaseIfModelChanges<SchoolContext> { protected override void Seed(SchoolContext context) { var students = new List<Student> { new Student { FirstMidName = "Carson", LastName = "Alexander", EnrollmentDate = DateTime.Parse("2005-09-01") }, new Student { FirstMidName = "Meredith", LastName = "Alonso", EnrollmentDate = DateTime.Parse("2002-09-01") }, new Student { FirstMidName = "Arturo", LastName = "Anand", EnrollmentDate = DateTime.Parse("2003-09-01") }, new Student { FirstMidName = "Gytis", LastName = "Barzdukas", EnrollmentDate = DateTime.Parse("2002-09-01") }, new Student { FirstMidName = "Yan", LastName = "Li", EnrollmentDate = DateTime.Parse("2002-09-01") }, new Student { FirstMidName = "Peggy", LastName = "Justice",

EnrollmentDate = DateTime.Parse("2001-09-01") }, new Student { FirstMidName = "Laura", LastName = "Norman", EnrollmentDate = DateTime.Parse("2003-09-01") }, new Student { FirstMidName = "Nino", LastName = "Olivetto", EnrollmentDate = DateTime.Parse("2005-09-01") } }; students.ForEach(s => context.Students.Add(s)); context.SaveChanges(); var instructors = new List<Instructor> { new Instructor { FirstMidName = "Kim", LastName HireDate = DateTime.Parse("1995-03-11") }, new Instructor { FirstMidName = "Fadi", LastName HireDate = DateTime.Parse("2002-07-06") }, new Instructor { FirstMidName = "Roger", LastName HireDate = DateTime.Parse("1998-07-01") }, new Instructor { FirstMidName = "Candace", LastName HireDate = DateTime.Parse("2001-01-15") }, new Instructor { FirstMidName = "Roger", LastName HireDate = DateTime.Parse("2004-02-12") } }; instructors.ForEach(s => context.Instructors.Add(s)); context.SaveChanges();

= "Abercrombie", = "Fakhouri", = "Harui", = "Kapoor", = "Zheng",

var departments = new List<Department> { new Department { Name = "English", Budget = 350000, DateTime.Parse("2007-09-01"), InstructorID = 1 }, new Department { Name = "Mathematics", Budget = 100000, DateTime.Parse("2007-09-01"), InstructorID = 2 }, new Department { Name = "Engineering", Budget = 350000, DateTime.Parse("2007-09-01"), InstructorID = 3 }, new Department { Name = "Economics", Budget = 100000, DateTime.Parse("2007-09-01"), InstructorID = 4 } }; departments.ForEach(s => context.Departments.Add(s)); context.SaveChanges();

StartDate = StartDate = StartDate = StartDate =

var courses = new List<Course> { new Course { CourseID = 1050, Title = "Chemistry", Credits = 3, DepartmentID = 3, Instructors = new List<Instructor>() }, new Course { CourseID = 4022, Title = "Microeconomics", Credits = 3, DepartmentID = 4, Instructors = new List<Instructor>() }, new Course { CourseID = 4041, Title = "Macroeconomics", Credits = 3, DepartmentID = 4, Instructors = new List<Instructor>() },

DepartmentID = DepartmentID = DepartmentID =

DepartmentID = }; courses.ForEach(s => context.Courses.Add(s)); context.SaveChanges(); courses[0].Instructors.Add(instructors[0]); courses[0].Instructors.Add(instructors[1]); courses[1].Instructors.Add(instructors[2]); courses[2].Instructors.Add(instructors[2]); courses[3].Instructors.Add(instructors[3]); courses[4].Instructors.Add(instructors[3]); courses[5].Instructors.Add(instructors[3]); courses[6].Instructors.Add(instructors[3]); context.SaveChanges();

new Course { CourseID = 1045, Title = "Calculus", 2, Instructors = new List<Instructor>() }, new Course { CourseID = 3141, Title = "Trigonometry", 2, Instructors = new List<Instructor>() }, new Course { CourseID = 2021, Title = "Composition", 1, Instructors = new List<Instructor>() }, new Course { CourseID = 2042, Title = "Literature", 1, Instructors = new List<Instructor>() }

Credits = 4, Credits = 4, Credits = 3, Credits = 4,

var enrollments = new List<Enrollment> { new Enrollment { StudentID = 1, CourseID = 1050, Grade new Enrollment { StudentID = 1, CourseID = 4022, Grade new Enrollment { StudentID = 1, CourseID = 4041, Grade new Enrollment { StudentID = 2, CourseID = 1045, Grade new Enrollment { StudentID = 2, CourseID = 3141, Grade new Enrollment { StudentID = 2, CourseID = 2021, Grade new Enrollment { StudentID = 3, CourseID = 1050 new Enrollment { StudentID = 4, CourseID = 1050, new Enrollment { StudentID = 4, CourseID = 4022, Grade new Enrollment { StudentID = 5, CourseID = 4041, Grade new Enrollment { StudentID = 6, CourseID = 1045 new Enrollment { StudentID = 7, CourseID = 3141, Grade }; enrollments.ForEach(s => context.Enrollments.Add(s)); context.SaveChanges();

= = = = = =

}, }, }, }, }, }, }, }, = 4 }, = 3 }, }, = 2 },

1 3 1 2 4 4

var officeAssignments = new List<OfficeAssignment> { new OfficeAssignment { InstructorID = 1, Location = "Smith 17" }, new OfficeAssignment { InstructorID = 2, Location = "Gowan 27" }, new OfficeAssignment { InstructorID = 3, Location = "Thompson 304" }, }; officeAssignments.ForEach(s => context.OfficeAssignments.Add(s));

context.SaveChanges(); } } } As you saw in the first tutorial, most of this code simply creates new entity objects and loads sample data into properties as required for testing. However, notice how the Course entity, which has a many-to-many relationship with the Instructor entity, is handled: var courses = new List { new Course { CourseID = 1050, Title = "Chemistry", = 3, Instructors = new List() }, ... }; courses.ForEach(s => context.Courses.Add(s)); context.SaveChanges(); courses[0].Instructors.Add(instructors[0]); ... context.SaveChanges(); When you create a Course object, you initialize the Instructors navigation property as an empty collection using the code Instructors = new List(). This makes it possible to add Instructor entities that are related to thisCourse by using the Instructors.Add method. If you didn't create an empty list, you wouldn't be able to add these relationships, because the Instructors property would be null and wouldn't have an Add method. Note Remember that when you deploy an application to a production web server, you must remove any code you've added to seed the database.

Credits = 3, DepartmentID

Dropping and Re-Creating the Database


Now run the site and select the Student Index page.

The page looks the same as it did before, but behind the scenes the database has been re-created. If you don't see the Student Index page and instead you get an error that indicates that the School.sdf file is in use (see the following illustration), you need to reopen Server Explorer and close the connection to the database. Then try displaying the Student Index page again.

After viewing the Student Index page, open the database in Server Explorer as you did earlier, and expand theTables node to see that all of the tables have been created.

Besides EdmMetadata, you see one table you didn't create a model class for: CourseInstructor. As explained earlier, this is a join table for the many-to-many relationship between the Instructor and Course entities. Right-click the CourseInstructor table and select Show Table Data to verify that it has data in it as a result of theInstructor entities you added to the Course.Instructors navigation property.

You now have a more complex data model and corresponding database. In the following tutorial you'll learn more about different ways to access related data. Links to other Entity Framework resources can be found at the end of the last tutorial in this series.

Reading Related Data with the Entity Framework in an ASP.NET MVC Application (5 of 10)
By Tom Dykstra|April 11, 2011 The Contoso University sample web application demonstrates how to create ASP.NET MVC applications using the Entity Framework. The sample application is a website for a fictional Contoso University. It includes functionality such as student admission, course creation, and instructor assignments. This tutorial series explains the steps taken to build the Contoso University sample application. You candownload the completed application or create it by following the steps in the tutorial. The tutorial shows examples in C#. The downloadable sample contains code in both C# and Visual Basic. If you have questions that are not directly related to the tutorial, you can post them to the ASP.NET Entity Framework forum or theEntity Framework and LINQ to Entities forum. This tutorial series assumes you know how to work with ASP.NET MVC in Visual Studio. If you dont, a good place to start is a basic ASP.NET MVC Tutorial. If you prefer to work with the ASP.NET Web Forms

model, see the Getting Started with the Entity Framework and Continuing with the Entity Framework tutorials. Before you start, make sure you have the following software installed on your computer: Visual Studio 2010 SP1 or Visual Web Developer Express 2010 SP1 (If you use one of these links, the following items will be installed automatically.) ASP.NET MVC 3 Tools Update Microsoft SQL Server Compact 4.0 Microsoft Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0 In the previous tutorial you completed the School data model. In this tutorial you'll read and display related data that is, data that the Entity Framework loads into navigation properties. The following illustrations show the pages that you'll work with.

Lazy, Eager, and Explicit Loading of Related Data

There are several ways that the Entity Framework can load related data into the navigation properties of an entity: Lazy loading. When the entity is first read, related data isn't retrieved. However, the first time you attempt to access a navigation property, the data required for that navigation property is automatically retrieved. This results in multiple queries sent to the database one for the entity itself and one each time that related data for the entity must be retrieved.

Eager loading. When the entity is read, related data is retrieved along with it. This typically results in a single join query that retrieves all of the data that's needed. You specify eager loading by using the Include method.

Explicit loading. This is similar to lazy loading, except that you explicitly retrieve the related data in code; it doesn't happen automatically when you access a navigation property. You load related data manually by getting the object state manager entry for an entity and calling the Collection.Load method for collections or the Reference.Load method for properties that hold a single entity. (In the following example, if you wanted to load the Administrator navigation property, you'd replace Collection(x => x.Courses) withReference(x => x.Administrator).)

Because they don't immediately retrieve the property values, lazy loading and explicit loading are also both known as deferred loading. In general, if you know you need related data for every entity retrieved, eager loading offers the best performance, because a single query sent to the database is typically more efficient than separate queries for each entity retrieved. For example, in the above examples, suppose that each department has ten

related courses. The eager loading example would result in just a single (join) query. The lazy loading and explicit loading examples would both result in eleven queries. On the other hand, if you need to access an entity's navigation properties only infrequently or only for a small portion of a set of entities you're processing, lazy loading may be more efficient, because eager loading would retrieve more data than you need. Typically you'd use explicit loading only when you've turned lazy loading off. One scenario when you might turn lazy loading off is during serialization, when you know you don't need all navigation properties loaded. If lazy loading were on, all navigation properties would all be loaded automatically, because serialization accesses all properties. The database context class performs lazy loading by default. There are two ways to turn off lazy loading: For specific navigation properties, omit the virtual keyword when you declare the property. For all navigation properties, set LazyLoadingEnabled to false. Lazy loading can mask code that causes performance problems. For example, code that doesn't specify eager or explicit loading but processes a high volume of entities and uses several navigation properties in each iteration might be very inefficient (because of many round trips to the database), but it would work without errors if it relies on lazy loading. Temporarily disabling lazy loading is one way to discover where the code is relying on lazy loading, because without it the navigation properties will be null and the code will fail.

Creating a Courses Index Page That Displays Department Name


The Course entity includes a navigation property that contains the Department entity of the department that the course is assigned to. To display the name of the assigned department in a list of courses, you need to get theName property from the Department entity that is in the Course.Department navigation property. Create a controller for the Course entity type, using the same options that you did earlier for the Studentcontroller, as shown in the following illustration:

Open Controllers\CourseController.cs and look at the Index method: public ViewResult Index() { var courses = db.Courses.Include(c => c.Department); return View(courses.ToList()); } The automatic scaffolding has specified eager loading for the Department navigation property by using theInclude method. Open Views\Course\Index.cshtml and replace the existing code with the following code: @model IEnumerable<ContosoUniversity.Models.Course> @{ ViewBag.Title = "Courses"; } <h2>Courses</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th></th> <th>Number</th>

<th>Title</th> <th>Credits</th> <th>Department</th> </tr> @foreach (var item in Model) { <tr> <td> @Html.ActionLink("Edit", "Edit", new { id=item.CourseID }) | @Html.ActionLink("Details", "Details", new { id=item.CourseID }) | @Html.ActionLink("Delete", "Delete", new { id=item.CourseID }) </td> <td> @Html.DisplayFor(modelItem => item.CourseID) </td> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.Credits) </td> <td> @Html.DisplayFor(modelItem => item.Department.Name) </td> </tr> } </table> You've made the following changes to the scaffolded code: Changed the heading from Index to Courses. Moved the row links to the left.

Added a column under the heading Number that shows the CourseID property value. (Primary keys aren't scaffolded because normally they are meaningless. However, in this case the primary key is meaningful and you want to show it.) Changed the last column heading from DepartmentID (the name of the foreign key to the Department entity) to Department. Notice that for the last column, the scaffolded code displays the Name property of the Department entity that's loaded into the Department navigation property: <td> @Html.DisplayFor(modelItem => item.Department.Name) </td> Run the page (select the Courses tab on the Contoso University home page) to see the list with department names.

Creating an Instructors Index Page That Shows Courses and Enrollments


In this section you'll create a controller and view for the Instructor entity in order to display the Instructors Index page:

This page reads and displays related data in the following ways:

The list of instructors displays related data from the OfficeAssignment entity. The Instructor andOfficeAssignment entities are in a one-to-zero-or-one relationship. You'll use eager loading for theOfficeAssignment entities. As explained earlier, eager loading is typically more efficient when you need the related data for all retrieved rows of the primary table. In this case, you want to display office assignments for all displayed instructors. When the user selects an instructor, related Course entities are displayed. The Instructor and Course entities are in a many-to-many relationship. You will use eager loading for the Course entities and their relatedDepartment entities. In this case, lazy loading might be more efficient because you need courses only for the selected instructor. However, this example shows how to use eager loading for navigation properties within entities that are themselves in navigation properties. When the user selects a course, related data from the Enrollments entity set is displayed. The Course andEnrollment entities are in a one-to-many relationship. You'll add explicit loading for Enrollment entities and their related Student entities. (Explicit loading isn't necessary because lazy loading is enabled, but this shows how to do explicit loading.)

Creating a View Model for the Instructor Index View


The Instructor Index page shows three different tables. Therefore, you'll create a view model that includes three properties, each holding the data for one of the tables. In the ViewModels folder, create InstructorIndexData.cs and replace the existing code with the following code: using System; using System.Collections.Generic; using ContosoUniversity.Models; namespace ContosoUniversity.ViewModels { public class InstructorIndexData { public IEnumerable<Instructor> Instructors { get; set; } public IEnumerable<Course> Courses { get; set; } public IEnumerable<Enrollment> Enrollments { get; set; } } }

Adding a Style for Selected Rows


To mark selected rows you need a different background color. To provide a style for this UI, add the following code to the section marked MISC in Content\Site.css, as shown in the following example: /* MISC ----------------------------------------------------------*/ .selectedrow { background-color: #EEEEEE; }

Creating the Instructor Controller and Views


Create a controller for the Instructor entity type, using the same options that you did earlier for the Studentcontroller, as shown in the following illustration:

Open Controllers\InstructorController.cs and add a using statement for the ViewModels namespace: using ContosoUniversity.ViewModels; The scaffolded code in the Index method specifies eager loading only for the OfficeAssignment navigation property: public ViewResult Index() { var instructors = db.Instructors.Include(i => i.OfficeAssignment); return View(instructors.ToList()); } Replace the Index method with the following code to load additional related data and put it in the view model: public ActionResult Index(Int32? id, Int32? courseID) { var viewModel = new InstructorIndexData(); viewModel.Instructors = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses.Select(c => c.Department)) .OrderBy(i => i.LastName);

if (id != null) { ViewBag.InstructorID = id.Value; viewModel.Courses = viewModel.Instructors.Where(i => i.InstructorID == id.Value).Single().Courses; } if (courseID != null) { ViewBag.CourseID = courseID.Value; viewModel.Enrollments = viewModel.Courses.Where(x => x.CourseID == courseID).Single().Enrollments; } return View(viewModel); } The method accepts optional query string parameters that provide the ID values of the selected instructor and selected course, and passes all of the required data to the view. The query string parameters are provided by theSelect hyperlinks on the page. The code begins by creating an instance of the view model and putting in it the list of instructors: var viewModel = new InstructorIndexData(); viewModel.Instructors = db.Instructors .Include(i => i.OfficeAssignment); .Include(i => i.Courses.Select(c => c.Department)) .OrderBy(i => i.LastName); This statement specifies eager loading for the Instructor.OfficeAssignment and the Instructor.Coursesnavigation property. For the related Course entities, eager loading is specified for the Course.Departmentnavigation property by using the Select method within the Include method. The results are sorted by last name. If an instructor was selected, the selected instructor is retrieved from the list of instructors in the view model. The view model's Courses property is then loaded with the Course entities from that instructor's Courses navigation property. if (id != null) { ViewBag.InstructorID = id.Value; viewModel.Courses = viewModel.Instructors.Where(i => i.InstructorID == id.Value).Single().Courses; } The Where method returns a collection, but in this case the criteria passed to that method result in only a singleInstructor entity being returned. The Single method converts the collection into a single Instructor entity, which gives you access to that entity's Courses property. You use the Single method on a collection when you know the collection will have only one item. The Singlemethod throws an exception if the collection passed to it is empty or if there's more than one item. An alternative isSingleOrDefault, which returns null if the collection is empty. However, in this case that would still result in an exception (from trying to find a Courses property on a null reference),

and the exception message would less clearly indicate the cause of the problem. When you call the Single method, you can also pass in the Wherecondition instead of calling the Where method separately: .Single(i => i.InstructorID == id.Value) Instead of: .Where(I => i.InstructorID == id.Value).Single() Next, if a course was selected, the selected course is retrieved from the list of courses in the view model. Then the view model's Enrollments property is loaded with the Enrollment entities from that course's Enrollmentsnavigation property. if (courseID != null) { ViewBag.CourseID = courseID.Value; viewModel.Enrollments = viewModel.Courses.Where(x => x.CourseID == courseID).Single().Enrollments; } Finally, the view model is returned to the view: return View(viewModel);

Modifying the Instructor Index View


In Views\Instructor\Index.cshtml, replace the existing code with the following code: @model ContosoUniversity.ViewModels.InstructorIndexData @{ ViewBag.Title = "Instructors"; } <h2>Instructors</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th></th> <th>Last Name</th> <th>First Name</th> <th>Hire Date</th> <th>Office</th> </tr> @foreach (var item in Model.Instructors) { string selectedRow = "";

if (item.InstructorID == ViewBag.InstructorID) { selectedRow = "selectedrow"; } <tr class="@selectedRow" valign="top"> <td> @Html.ActionLink("Select", "Index", new { id = item.InstructorID }) | @Html.ActionLink("Edit", "Edit", new { id = item.InstructorID }) | @Html.ActionLink("Details", "Details", new { id = item.InstructorID }) | @Html.ActionLink("Delete", "Delete", new { id = item.InstructorID }) </td> <td> @item.LastName </td> <td> @item.FirstMidName </td> <td> @String.Format("{0:d}", item.HireDate) </td> <td> @if (item.OfficeAssignment != null) { @item.OfficeAssignment.Location } </td> </tr> } </table> You've made the following changes to the existing code: Changed the page title from Index to Instructors. Moved the row link columns to the left. Removed the FullName column. Added an Office column that displays item.OfficeAssignment.Location only if item.OfficeAssignment is not null. (Because this is a one-to-zero-or-one relationship, there might not be a related OfficeAssignmententity.) <td> @if (item.OfficeAssignment != null) { @item.OfficeAssignment.Location } </td> Added code that will dynamically add class="selectedrow" to the tr element of the selected instructor. This sets a background color for the selected row using the CSS class that you created

earlier. (The valignattribute will be useful in the following tutorial when you add a multirow column to the table.) string selectedRow = ""; if (item.InstructorID == ViewBag.InstructorID) { selectedRow = "selectedrow"; } <tr class="@selectedRow" valign="top"> Added a new ActionLink labeled Select immediately before the other links in each row, which causes the selected instructor ID to be sent to the Index method. Run the page to see the list of instructors. The page displays the Location property of related OfficeAssignmententities and an empty table cell when there's no related OfficeAssignment entity.

While you still have Views\Instructor\Index.cshtml open, after the table element, add the following code. This displays a list of courses related to an instructor when an instructor is selected. @if (Model.Courses != null) {

<h3>Courses Taught by Selected Instructor</h3> <table> <tr> <th></th> <th>ID</th> <th>Title</th> <th>Department</th> </tr> @foreach (var item in Model.Courses) { string selectedRow = ""; if (item.CourseID == ViewBag.CourseID) { selectedRow = "selectedrow"; } <tr class="@selectedRow"> <td> @Html.ActionLink("Select", "Index", new { courseID = item.CourseID }) </td> <td> @item.CourseID </td> <td> @item.Title </td> <td> @item.Department.Name </td> </tr> } </table> } This code reads the Courses property of the view model to display a list of courses. It also provides a Selecthyperlink that sends the ID of the selected course to the Index action method. Run the page and select an instructor. Now you see a grid that displays courses assigned to the selected instructor, and for each course you see the name of the assigned department.

Note If the selected row isn't highlighted, click the Refresh button on your browser; this is sometimes required in order to reload the .css file. After the code block you just added, add the following code. This displays a list of the students who are enrolled in a course when that course is selected. @if (Model.Enrollments != null) { <h3>

Students Enrolled in Selected Course</h3> <table> <tr> <th>Name</th> <th>Grade</th> </tr> @foreach (var item in Model.Enrollments) { <tr> <td> @item.Student.FullName </td> <td> @Html.DisplayFor(modelItem => item.Grade) </td> </tr> } </table> } This code reads the Enrollments property of the view model in order to display a list of students enrolled in the course. The DisplayFor helper is used so that null grades will display as "No grade", as specified in theDisplayFormat data annotation attribute for that field. Run the page and select an instructor. Then select a course to see the list of enrolled students and their grades.

Adding Explicit Loading


Open InstructorController.cs and look at how the Index method gets the list of enrollments for a selected course: if (courseID != null) { ViewBag.CourseID = courseID.Value; viewModel.Enrollments = viewModel.Courses.Where(x => x.CourseID == courseID).Single().Enrollments; } When you retrieved the list of instructors, you specified eager loading for the Courses navigation property and for the Department property of each course. Then you put the Courses collection in the view model, and now you're accessing the Enrollments navigation property from one entity in that collection. Because you didn't specify eager loading for the Course.Enrollments navigation property, the data from that property is appearing in the page as a result of lazy loading. If you disabled lazy loading without changing the code in any other way, the Enrollments property would be null regardless of how many enrollments the course actually had. In that case, to load the Enrollments property, you'd have to specify either eager loading or explicit loading. You've already seen how to do eager loading. In order to see an example of explicit loading, replace the Index method with the following code, which explicitly loads theEnrollments property: public ActionResult Index(Int32? id, Int32? courseID) { var viewModel = new InstructorIndexData(); viewModel.Instructors = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses.Select(c => c.Department)) .OrderBy(i => i.LastName); if (id != null) { ViewBag.InstructorID = id.Value; viewModel.Courses = viewModel.Instructors.Where(i => i.InstructorID == id.Value).Single().Courses; }

if (courseID != null) { ViewBag.CourseID = courseID.Value; var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single(); db.Entry(selectedCourse).Collection(x => x.Enrollments).Load(); foreach (Enrollment enrollment in selectedCourse.Enrollments) { db.Entry(enrollment).Reference(x => x.Student).Load();

} viewModel.Enrollments = selectedCourse.Enrollments; } return View(viewModel); } After getting the selected Course entity, the new code explicitly loads that course's Enrollments navigation property: db.Entry(selectedCourse).Collection(x => x.Enrollments).Load(); Then it explicitly loads each Enrollment entity's related Student entity: db.Entry(enrollment).Reference(x => x.Student).Load(); Notice that you use the Collection method to load a collection property, but for a property that holds just one entity, you use the Reference method. You can run the Instructor Index page now and you'll see no difference in what's displayed on the page, although you've changed how the data is retrieved. You've now used all three ways (lazy, eager, and explicit) to load related data into navigation properties. In the next tutorial you'll learn how to update related data. Links to other Entity Framework resources, can be found at the end of the last tutorial in this series.

Updating Related Data with the Entity Framework in an ASP.NET MVC Application (6 of 10)
By Tom Dykstra|April 11, 2011 The Contoso University sample web application demonstrates how to create ASP.NET MVC applications using the Entity Framework. The sample application is a website for a fictional Contoso University. It includes functionality such as student admission, course creation, and instructor assignments. This tutorial series explains the steps taken to build the Contoso University sample application. You candownload the completed application or create it by following the steps in the tutorial. The tutorial shows examples in C#. The downloadable sample contains code in both C# and Visual Basic. If you have questions that are not directly related to the tutorial, you can post them to the ASP.NET Entity Framework forum or theEntity Framework and LINQ to Entities forum. This tutorial series assumes you know how to work with ASP.NET MVC in Visual Studio. If you dont, a good place to start is a basic ASP.NET MVC Tutorial. If you prefer to work with the ASP.NET Web Forms model, see the Getting Started with the Entity Framework and Continuing with the Entity Framework tutorials. Before you start, make sure you have the following software installed on your computer: Visual Studio 2010 SP1 or Visual Web Developer Express 2010 SP1 (If you use one of these links, the following items will be installed automatically.) ASP.NET MVC 3 Tools Update Microsoft SQL Server Compact 4.0

Microsoft Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0 In the previous tutorial you displayed related data; in in this tutorial you'll update related data. For most relationships, this can be done by updating the appropriate foreign key fields. For many-to-many relationships, the Entity Framework doesn't expose the join table directly, so you must explicitly add and remove entities to and from the appropriate navigation properties. The following illustrations show the pages that you'll work with.

Customizing the Create and Edit Pages for Courses


When a new course entity is created, it must have a relationship to an existing department. To facilitate this, the scaffolded code includes controller methods and Create and Edit views that include a drop-down list for selecting the department. The drop-down list sets the Course.DepartmentID foreign key

property, and that is all the Entity Framework needs in order to load the Department navigation property with the appropriate Department entity. You'll use the scaffolded code, but change it slightly to add error handling and sort the drop-down list. In CourseController.cs, delete the four Edit and Create methods and replace them with the following code: public ActionResult Create() { PopulateDepartmentsDropDownList(); return View(); } [HttpPost] public ActionResult Create(Course course) { try { if (ModelState.IsValid) { db.Courses.Add(course); db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); } PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); } public ActionResult Edit(int id) { Course course = db.Courses.Find(id); PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); } [HttpPost] public ActionResult Edit(Course course) { try { if (ModelState.IsValid) {

db.Entry(course).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); } PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); } private void PopulateDepartmentsDropDownList(object selectedDepartment = null) { var departmentsQuery = from d in db.Departments orderby d.Name select d; ViewBag.DepartmentID = new SelectList(departmentsQuery, "DepartmentID", "Name", selectedDepartment); } The PopulateDepartmentsDropDownList method gets a list of all departments sorted by name, creates aSelectList collection for a drop-down list, and passes the collection to the view in a ViewBag property. The method accepts a parameter that allows the caller to optionally specify the item that will be selected initially when the drop-down list is rendered. The HttpGet Create method calls the PopulateDepartmentsDropDownList method without setting the selected item, because for a new course the department is not established yet: public ActionResult Create() { PopulateDepartmentsDropDownList(); return View(); } The HttpGet Edit method sets the selected item, based on the ID of the department that is already assigned to the course being edited: public ActionResult Edit(int id) { Course course = db.Courses.Find(id); PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); } The HttpPost methods for both Create and Edit also include code that sets the selected item when they redisplay the page after an error:

catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); } PopulateDepartmentsDropDownList(course.DepartmentID); return View(course); This code ensures that when the page is redisplayed to show the error message, whatever department was selected stays selected. In Views\Course\Create.cshtml, add a new field before the Title field to allow the user to enter the course number. As explained in an earlier tutorial, primary key fields aren't scaffolded, but this primary key is meaningful, so you want the user to be able to enter the key value. <div class="editor-label"> @Html.LabelFor(model => model.CourseID) </div> <div class="editor-field"> @Html.EditorFor(model => model.CourseID) @Html.ValidationMessageFor(model => model.CourseID) </div> In Views\Course\Edit.cshtml, Views\Course\Delete.cshtml, and Views\Course\Details.cshtml, add a new field before theTitle field to display the course number. Because it's the primary key, it's displayed, but it can't be changed. <div class="editor-label"> @Html.LabelFor(model => model.CourseID) </div> <div class="editor-field"> @Html.DisplayFor(model => model.CourseID) </div> Run the Create page (display the Course Index page and click Create New) and enter data for a new course:

Click Create. The Course Index page is displayed with the new course added to the list. The department name in the Index page list comes from the navigation property, showing that the relationship was established correctly.

Run the Edit page (display the Course Index page and click Edit on a course).

Change data on the page and click Save. The Course Index page is displayed with the updated course data.

Adding an Edit Page for Instructors


When you edit an instructor record, you want to be able to update the instructor's office assignment. TheInstructor entity has a one-to-zero-or-one relationship with the OfficeAssignment entity, which means you must handle the following situations: If the user clears the office assignment and it originally had a value, you must remove and delete theOfficeAssignment entity. If the user enters an office assignment value and it originally was empty, you must create a newOfficeAssignment entity. If the user changes the value of an office assignment, you must change the value in an existingOfficeAssignment entity. Open InstructorController.cs and look at the HttpGet Edit method: public ActionResult Edit(int id) { Instructor instructor = db.Instructors.Find(id); ViewBag.InstructorID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", instructor.InstructorID); return View(instructor); } The scaffolded code here isn't what you want. It's setting up data for a drop-down list, but you what you need is a text box. Replace this method with the following code: public ActionResult Edit(int id) { Instructor instructor = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses) .Where(i => i.InstructorID == id) .Single(); return View(instructor); } This code drops the ViewBag statement and adds eager loading for associated OfficeAssignment and Courseentities. (You don't need Courses now, but you'll need it later.) You can't perform eager loading with the Findmethod, so the Where and Single methods are used instead to select the instructor. Replace the HttpPost Edit method with the following code. which handles office assignment updates: [HttpPost] public ActionResult Edit(int id, FormCollection formCollection) { var instructorToUpdate = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses) .Where(i => i.InstructorID == id) .Single();

if (TryUpdateModel(instructorToUpdate, "", null, new string[] { "Courses" })) { try { if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location)) { instructorToUpdate.OfficeAssignment = null; } db.Entry(instructorToUpdate).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); return View(); } } return View(instructorToUpdate); } The code does the following: Gets the current Instructor entity from the database using eager loading for the OfficeAssignment andCourses navigation properties. This is the same as what you did in the HttpGet Edit method. Updates the retrieved Instructor entity with values from the model binder, excluding the Courses navigation property: If (TryUpdateModel(instructorToUpdate, "", null, new string[] { "Courses" })) (The second and third parameters specify no prefix on the property names and no list of properties to include.) If validation fails, TryUpdateModel returns false, and the code falls through to the return Viewstatement at the end of the method. If the office location is blank, sets the Instructor.OfficeAssignment property to null so that the related row in the OfficeAssignment table will be deleted. if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location)) { instructorToUpdate.OfficeAssignment = null; } Saves the changes to the database. In Views\Instructor\Edit.cshtml, after the div elements for the Hire Date field, add a new field for editing the office location:

<div class="editor-label"> @Html.LabelFor(model => model.OfficeAssignment.Location) </div> <div class="editor-field"> @Html.EditorFor(model => model.OfficeAssignment.Location) @Html.ValidationMessageFor(model => model.OfficeAssignment.Location) </div> Run the page (select the Instructors tab and then click Edit on an instructor).

Change the Office Location and click Save.

The new location appears on the Index page, and you can see the table row when you open the OfficeAssignmenttable in Server Explorer.

Return to the Edit page, clear the Office Location and click Save. The Index page shows a blank office location andServer Explorer shows that the row has been deleted.

Return to the Edit page, enter a new value in the Office Location and click Save. The Index page shows the new location, and Server Explorer shows that a row has been created.

Adding Course Assignments to the Instructor Edit Page


Instructors may teach any number of courses. You'll now enhance the Instructor Edit page by adding the ability to change course assignments using a group of check boxes, as shown in the following screen shot:

The relationship between the Course and Instructor entities is many-to-many, which means you do not have direct access to the join table or foreign key fields. Instead, you will add and remove entities to and from theInstructor.Courses navigation property. The UI that enables you to change which courses an instructor is assigned to is a group of check boxes. A check box for every course in the database is displayed, and the ones that the instructor is currently assigned to are selected. The user can select or clear check boxes to change course assignments. If the

number of courses were much greater, you probably would want to use a different method of presenting the data in the view, but you'd use the same method of manipulating navigation properties in order to create or delete relationships. To provide data to the view for the list of check boxes, you'll use a view model class. Create AssignedCourseData.csin the ViewModels folder and replace the existing code with the following code: using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.ViewModels { public class AssignedCourseData { public int CourseID { get; set; } public string Title { get; set; } public bool Assigned { get; set; } } } In InstructorController.cs, in the HttpGet Edit method, call a new method that provides information for the check box array using the new view model class, as shown in the following example: public ActionResult Edit(int id) { Instructor instructor = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses) .Where(i => i.InstructorID == id) .Single(); PopulateAssignedCourseData(instructor); return View(instructor); } private void PopulateAssignedCourseData(Instructor instructor) { var allCourses = db.Courses; var instructorCourses = new HashSet<int>(instructor.Courses.Select(c => c.CourseID)); var viewModel = new List<AssignedCourseData>(); foreach (var course in allCourses) { viewModel.Add(new AssignedCourseData { CourseID = course.CourseID, Title = course.Title, Assigned = instructorCourses.Contains(course.CourseID)

}); } ViewBag.Courses = viewModel; } The code in the new method reads through all Course entities in order to load a list of courses using the view model class. For each course, the code checks whether the course exists in the instructor's Courses navigation property. To create efficient lookup when checking whether a course is assigned to the instructor, the courses assigned to the instructor are put into a HashSet collection. The Assigned property of courses that are assigned to the instructor is set to true. The view will use this property to determine which check boxes must be displayed as selected. Finally, the list is passed to the view in a ViewBag property. Next, add the code that's executed when the user clicks Save. Replace the HttpPost Edit method with the following code, which calls a new method that updates the Courses navigation property of the Instructor entity. [HttpPost] public ActionResult Edit(int id, FormCollection formCollection, string[] selectedCourses) { var instructorToUpdate = db.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.Courses) .Where(i => i.InstructorID == id) .Single(); if (TryUpdateModel(instructorToUpdate, "", null, new string[] { "Courses" })) { try { if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location)) { instructorToUpdate.OfficeAssignment = null; } UpdateInstructorCourses(selectedCourses, instructorToUpdate); db.Entry(instructorToUpdate).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator."); }

} PopulateAssignedCourseData(instructorToUpdate); return View(instructorToUpdate); } private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate) { if (selectedCourses == null) { instructorToUpdate.Courses = new List<Course>(); return; } var selectedCoursesHS = new HashSet<string>(selectedCourses); var instructorCourses = new HashSet<int> (instructorToUpdate.Courses.Select(c => c.CourseID)); foreach (var course in db.Courses) { if (selectedCoursesHS.Contains(course.CourseID.ToString())) { if (!instructorCourses.Contains(course.CourseID)) { instructorToUpdate.Courses.Add(course); } } else { if (instructorCourses.Contains(course.CourseID)) { instructorToUpdate.Courses.Remove(course); } } } } If no check boxes were selected, the code in UpdateInstructorCourses initializes the Courses navigation property with an empty collection: if (selectedCourses == null) { instructorToUpdate.Courses = new List(); return; } The code then loops through all courses in the database. If the check box for a course was selected but the course isn't in the Instructor.Courses navigation property, the course is added to the collection in the navigation property.

if (selectedCoursesHS.Contains(course.CourseID.ToString())) { if (!instructorCourses.Contains(course.CourseID)) { instructorToUpdate.Courses.Add(course); } } If a course wasn't selected, but the course is in the Instructor.Courses navigation property, the course is removed from the navigation property. else { if (instructorCourses.Contains(course.CourseID)) { instructorToUpdate.Courses.Remove(course); } } In Views\Instructor\Edit.cshtml, add a Courses field with an array of check boxes by adding the following code immediately after the div elements for the OfficeAssignment field: <div class="editor-field"> <table> <tr> @{ int cnt = 0; List<ContosoUniversity.ViewModels.AssignedCourseData> courses = ViewBag.Courses; foreach (var course in courses) { if (cnt++ % 3 == 0) { @: </tr> <tr> } @: <td> <input type="checkbox" name="selectedCourses" value="@course.CourseID" @(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) /> @course.CourseID @: @:</td> } @: </tr> } </table> </div> This code creates an HTML table that has three columns. In each column is a check box followed by a caption that consists of the course number and title. The check boxes all have the same name @course.Title

("selectedCourses"), which informs the model binder that they are to be treated as a group. The value attribute of each check box is set to the value of CourseID. When the page is posted, the model binder passes an array to the controller that consists of theCourseID values for only the check boxes which are selected. When the check boxes are initially rendered, those that are for courses already assigned to the instructor havechecked attributes, which selects them. After changing course assignments, you'll want to be able to verify the changes when the site returns to the Index page. Therefore, you need to add a column to the table in that page. In this case you don't need to use theViewBag object, because the information you want to display is already in the Courses navigation property of theInstructor entity that you're passing to the page as the model. In Views\Instructor\Index.cshtml, add a <th>Courses</th> heading cell immediately following the <th>Office</th>heading, as shown in the following example: <tr> <th></th> <th>Last Name</th> <th>First Name</th> <th>Hire Date</th> <th>Office</th> <th>Courses</th> </tr> Then add a new detail cell immediately following the office location detail cell: <td> @{ foreach (var course in item.Courses) { @course.CourseID @: @course.Title <br /> } } </td> Run the Instructor Index page to see the courses assigned to each instructor:

Click Edit on an instructor to see the Edit page.

Change some course assignments and click Save. The changes you make are reflected on the Index page. You have now completed this introduction to working with related data. So far in these tutorials you've done a full range of CRUD operations, but you haven't dealt with concurrency issues. The next tutorial will introduce the topic of concurrency, explain options for handling it, and add concurrency handling to the CRUD code you've already written for one entity type.

Links to other Entity Framework resources, can be found at the end of the last tutorial in this series.

Handling Concurrency with the Entity Framework in an ASP.NET MVC Application (7 of 10)
By Tom Dykstra|April 11, 2011 The Contoso University sample web application demonstrates how to create ASP.NET MVC applications using the Entity Framework. The sample application is a website for a fictional Contoso University. It includes functionality such as student admission, course creation, and instructor assignments. This tutorial series explains the steps taken to build the Contoso University sample application. You candownload the completed application or create it by following the steps in the tutorial. The tutorial shows examples in C#. The downloadable sample contains code in both C# and Visual Basic. If you have questions that are not directly related to the tutorial, you can post them to the ASP.NET Entity Framework forum or theEntity Framework and LINQ to Entities forum. This tutorial series assumes you know how to work with ASP.NET MVC in Visual Studio. If you dont, a good place to start is a basic ASP.NET MVC Tutorial. If you prefer to work with the ASP.NET Web Forms model, see the Getting Started with the Entity Framework and Continuing with the Entity Framework tutorials. Before you start, make sure you have the following software installed on your computer: Visual Studio 2010 SP1 or Visual Web Developer Express 2010 SP1 (If you use one of these links, the following items will be installed automatically.) ASP.NET MVC 3 Tools Update Microsoft SQL Server Compact 4.0 Microsoft Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0 In the previous two tutorials you worked with related data. This tutorial shows how to handle concurrency. You'll create web pages that work with the Department entity, and the pages that edit and delete Department entities will handle concurrency errors. The following illustrations show the Index and Delete pages, including some messages that are displayed if a concurrency conflict occurs.

Concurrency Conflicts
A concurrency conflict occurs when one user displays an entity's data in order to edit it, and then another user updates the same entity's data before the first user's change is written to the database. If you don't set up the Entity Framework to detect such conflicts, whoever updates the database last overwrites the other user's changes. In many applications, this risk is acceptable: if there are few users, or few updates, or if isn't really critical if some changes are overwritten, the cost of programming for concurrency might outweigh the benefit. In that case, you don't have to configure the application to handle concurrency conflicts.

Pessimistic Concurrency (Locking)


If your application does need to prevent accidental data loss in concurrency scenarios, one way to do that is to use database locks. This is called pessimistic concurrency. For example, before you read a row from a database, you request a lock for read-only or for update access. If you lock a row for update access, no other users are allowed to lock the row either for read-only or update access, because they would get a copy of data that's in the process of being changed. If you lock a row for read-only access, others can also lock it for read-only access but not for update. Managing locks has some disadvantages. It can be complex to program. It requires significant database management resources, and it can cause performance problems as the number of users of an application increases (that is, it doesn't scale well). For these reasons, not all database management systems support pessimistic concurrency. The Entity Framework provides no built-in support for it, and this tutorial doesn't show you how to implement it.

Optimistic Concurrency
The alternative to pessimistic concurrency is optimistic concurrency. Optimistic concurrency means allowing concurrency conflicts to happen, and then reacting appropriately if they do. For example, John runs the Departments Edit page, changes the Budget amount for the English department from $350,000.00 to $100,000.00. (John administers a competing department and wants to free up money for his own department.)

Before John clicks Save, Jane runs the same page and changes the Start Date field from 9/1/2007 to 1/1/1999. (Jane administers the History department and wants to give it more seniority.)

John clicks Save first and sees his change when the browser returns to the Index page, then Jane clicks Save. What happens next is determined by how you handle concurrency conflicts. Some of the options include the following: You can keep track of which property a user has modified and update only the corresponding columns in the database. In the example scenario, no data would be lost, because different properties were updated by the two users. The next time someone browses the English department, they'll see both John's and Jane's changes a start date of 1/1/999 and a budget of $100,000.00. This method of updating can reduce the number of conflicts that could result in data loss, but it can't avoid data loss if competing changes are made to the same property of an entity. Whether the Entity Framework works this way depends on how you implement your update code. It's often not practical in a web application, because it can require that you maintain large amounts of state in order to keep track of all original values as well as new values. Maintaining large amounts of state can affect application performance because it either requires server resources or must be included in the web page itself (for example, in hidden fields). You can let Jane's change overwrite John's change. The next time someone browses the English department, they'll see 1/1/1999 and the restored $350,000.00 value. This is called a Client Wins or Last in Wins scenario. (The client's values take precedence over what's in the data store.) As noted in the introduction to this section, if you don't do any coding for concurrency handling, this will happen automatically. You can prevent Jane's change from being updated in the database. Typically, you would display an error message, show her the current state of the data, and allow her to reapply her changes if she still wants to make them. This is called a Store Wins scenario. (The data-store values take precedence over the values submitted by the client.) You'll implement the Store Wins scenario in this tutorial. This method ensures that no changes are overwritten without a user being alerted to what's happening.

Detecting Concurrency Conflicts


You can resolve conflicts by handling OptimisticConcurrencyException exceptions that the Entity Framework throws. In order to know when to throw these exceptions, the Entity Framework must be able to detect conflicts. Therefore, you must configure the database and the data model appropriately. Some options for enabling conflict detection include the following: In the database table, include a tracking column that can be used to determine when a row has been changed. You can then configure the Entity Framework to include that column in the Where clause of SQL Update orDelete commands. The data type of the tracking column is typically timestamp, but it doesn't actually contain a date or time value. Instead, the value is a sequential number that's incremented each time the row is updated. (Therefore the same type can be called rowversion in recent versions of SQL Server.) In an Update or Delete command, theWhere clause includes the original value of the tracking column. If the row being updated has been changed by another user, the value in that column is different than the original value, so the Update or Delete statement can't find the row to update because of the Where clause. When the Entity Framework finds that no rows have been updated by the Update or Delete command (that is, when the number of affected rows is zero), it interprets that as a concurrency conflict. Configure the Entity Framework to include the original values of every column in the table in the Where clause of Update and Delete commands.

As in the first option, if anything in the row has changed since the row was first read, the Where clause won't return a row to update, which the Entity Framework interprets as a concurrency conflict. This method is as effective as using a tracking column. However, for database tables that have many columns, this approach can result in very large Where clauses, and can require that you maintain large amounts of state. As noted earlier, maintaining large amounts of state can affect application performance because it either requires server resources or must be included in the web page itself. Therefore this approach generally not recommended, and it isn't the method used in this tutorial. In the remainder of this tutorial you'll add a tracking property to the Department entity, create a controller and views, and test to verify that everything works correctly. Note If you were implementing concurrency without a tracking column, you would have to mark all nonprimary-key properties in the entity for concurrency tracking by adding theConcurrencyCheck attribute to them. That change would enable the Entity Framework to include all columns in the SQL WHERE clause of UPDATE statements.

Adding a Tracking Property to the Department Entity


In Models\Department.cs, add a tracking property: [Timestamp] public Byte[] Timestamp { get; set; } The Timestamp attribute specifies that this column will be included in the Where clause of Update and Deletecommands sent to the database.

Creating a Department Controller

Create a Department controller and views the same way you did the other controllers, using the following settings:

In Controllers\DepartmentController.cs, add a using statement: using System.Data.Entity.Infrastructure; Change "LastName" to "FullName" everywhere in this file (four occurrences) so that the department administrator drop-down lists will contain the full name of the instructor rather than just the last name. Replace the existing code for the HttpPost Edit method with the following code: [HttpPost] public ActionResult Edit(Department department) { try { if (ModelState.IsValid) { db.Entry(department).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } } catch (DbUpdateConcurrencyException ex) { var entry = ex.Entries.Single(); var databaseValues = (Department)entry.GetDatabaseValues().ToObject(); var clientValues = (Department)entry.Entity; if (databaseValues.Name != clientValues.Name)

ModelState.AddModelError("Name", "Current value: " + databaseValues.Name); if (databaseValues.Budget != clientValues.Budget) ModelState.AddModelError("Budget", "Current value: " + String.Format("{0:c}", databaseValues.Budget)); if (databaseValues.StartDate != clientValues.StartDate) ModelState.AddModelError("StartDate", "Current value: " + String.Format("{0:d}", databaseValues.StartDate)); if (databaseValues.InstructorID != clientValues.InstructorID) ModelState.AddModelError("InstructorID", "Current value: " + db.Instructors.Find(databaseValues.InstructorID).FullName); ModelState.AddModelError(string.Empty, "The record you attempted to edit " + "was modified by another user after you got the original value. The " + "edit operation was canceled and the current values in the database " + "have been displayed. If you still want to edit this record, click " + "the Save button again. Otherwise click the Back to List hyperlink."); department.Timestamp = databaseValues.Timestamp; } catch (DataException) { //Log the error (add a variable name after Exception) ModelState.AddModelError(string.Empty, "Unable to save changes. Try again, and if the problem persists contact your system administrator."); } ViewBag.InstructorID = new SelectList(db.Instructors, "InstructorID", "FullName", department.InstructorID); return View(department); } The view will store the original timestamp value in a hidden field. When the model binder creates the departmentinstance, that object will have the original Timestamp property value and the new values for the other properties, as entered by the user on the Edit page. Then when the Entity Framework creates a SQL UPDATE command, that command will include a WHERE clause that looks for a row that has the original Timestamp value. If zero rows are affected by the UPDATE command, the Entity Framework throws a DbUpdateConcurrencyExceptionexception, and the code in the catch block gets the affected Department entity from the exception object. This entity has both the values read from the database and the new values entered by the user: var entry = ex.Entries.Single(); var databaseValues = (Department)entry.GetDatabaseValues().ToObject(); var clientValues = (Department)entry.Entity; Next, the code adds a custom error message for each column that has database values different from what the user entered on the Edit page:

if (databaseValues.Name != currentValues.Name) ModelState.AddModelError("Name", "Current value: " + databaseValues.Name); // ... A longer error message explains what happened and what to do about it: ModelState.AddModelError(string.Empty, "The record you attempted to edit " + "was modified by another user after you got the original value. The" + "edit operation was canceled and the current values in the database " + "have been displayed. If you still want to edit this record, click " + "the Save button again. Otherwise click the Back to List hyperlink."); Finally, the code sets the Timestamp value of the Department object to the new value retrieved from the database. This new Timestamp value will be stored in the hidden field when the Edit page is redisplayed, and the next time the user clicks Save, only concurrency errors that happen since the redisplay of the Edit page will be caught. In Views\Department\Edit.cshtml, add a hidden field to save the Timestamp property value, immediately following the hidden field for the DepartmentID property: @Html.HiddenFor(model => model.Timestamp) In Views\Department\Index.cshtml, replace the existing code with the following code to move row links to the left and change the page title and column headings to display FullName instead of LastName in the Administrator column: @model IEnumerable<ContosoUniversity.Models.Department> @{ ViewBag.Title = "Departments"; } <h2>Departments</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th></th> <th>Name</th> <th>Budget</th> <th>Start Date</th> <th>Administrator</th> </tr> @foreach (var item in Model) { <tr> <td> @Html.ActionLink("Edit", "Edit", new { id=item.DepartmentID }) | @Html.ActionLink("Details", "Details", new { id=item.DepartmentID }) |

@Html.ActionLink("Delete", </td> <td> @Html.DisplayFor(modelItem </td> <td> @Html.DisplayFor(modelItem </td> <td> @Html.DisplayFor(modelItem </td> <td> @Html.DisplayFor(modelItem </td> </tr> } </table>

"Delete", new { id=item.DepartmentID })

=> item.Name)

=> item.Budget)

=> item.StartDate)

=> item.Administrator.FullName)

Testing Optimistic Concurrency Handling


Run the site and click Departments:

Click an Edit hyperlink and then open a new browser window and go to the same URL in that window. The windows display the same information.

Change a field in the first browser window and click Save.

The browser shows the Index page with the changed value.

Change the same field to a different value in the second browser window.

Click Save in the second browser window. You see an error message:

Click Save again. The value you entered in the second browser is saved in the database and you see that value when the Index page appears.

Adding a Delete Page


For the Delete page, the Entity Framework detects concurrency conflicts in a similar manner. When the HttpGetDelete method displays the confirmation view, the view includes the original Timestamp value in a hidden field. That value is then available to the HttpPost Delete method that's called when the user confirms the deletion. When the Entity Framework creates the SQL DELETE command, it includes a WHERE clause with the original Timestampvalue. If the command results in zero rows affected (meaning the row was changed after the Delete confirmation page was displayed), a concurrency exception is thrown, and the HttpGet Delete method is called with an error flag set to true in order to redisplay the confirmation page with an error message. In DepartmentController.cs, replace the HttpGet Delete method with the following code: public ActionResult Delete(int id, bool? concurrencyError) { if (concurrencyError.GetValueOrDefault()) { ViewBag.ConcurrencyErrorMessage = "The record you attempted to delete "

+ + + + + }

"was modified by another user after you got the original values. " "The delete operation was canceled and the current values in the " "database have been displayed. If you still want to delete this " "record, click the Delete button again. Otherwise " "click the Back to List hyperlink.";

Department department = db.Departments.Find(id); return View(department); } The method accepts an optional parameter that indicates whether the page is being redisplayed after a concurrency error. If this flag is true, error message text is sent to the view using a ViewBag property. Replace the code in the HttpPost Delete method (named DeleteConfirmed) with the following code: [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(Department department) { try { db.Entry(department).State = EntityState.Deleted; db.SaveChanges(); return RedirectToAction("Index"); } catch (DbUpdateConcurrencyException) { return RedirectToAction("Delete", new System.Web.Routing.RouteValueDictionary { { "concurrencyError", true } }); } catch (DataException) { //Log the error (add a variable name after Exception) ModelState.AddModelError(string.Empty, "Unable to save changes. Try again, and if the problem persists contact your system administrator."); return View(department); } } In the scaffolded code that you just replaced, this method accepted only a record ID: public ActionResult DeleteConfirmed(int id) You've changed this parameter to a Department entity instance created by the model binder. This gives you access to the Timestamp property value in addition to the record key. public ActionResult DeleteConfirmed(Department department) If a concurrency error is caught, the code redisplays the Delete confirmation page and provides a flag that indicates it should display a concurrency error message.

In Views\Department\Delete.cshtml, replace the scaffolded code with the following code to make some formatting changes and add an error message field: @model ContosoUniversity.Models.Department @{ ViewBag.Title = "Delete"; } <h2>Delete</h2> <p class="error">@ViewBag.ConcurrencyErrorMessage</p> <h3>Are you sure you want to delete this?</h3> <fieldset> <legend>Department</legend> <div class="display-label"> @Html.LabelFor(model => model.Name) </div> <div class="display-field"> @Html.DisplayFor(model => model.Name) </div> <div class="display-label"> @Html.LabelFor(model => model.Budget) </div> <div class="display-field"> @Html.DisplayFor(model => model.Budget) </div> <div class="display-label"> @Html.LabelFor(model => model.StartDate) </div> <div class="display-field"> @Html.DisplayFor(model => model.StartDate) </div> <div class="display-label"> @Html.LabelFor(model => model.InstructorID) </div> <div class="display-field"> @Html.DisplayFor(model => model.Administrator.FullName) </div> </fieldset> @using (Html.BeginForm()) { @Html.HiddenFor(model => model.DepartmentID) @Html.HiddenFor(model => model.Timestamp)

<p> <input type="submit" value="Delete" /> | @Html.ActionLink("Back to List", "Index") </p> } This code adds an error message between the h2 and h3 headings: <p class="error">@ViewBag.ConcurrencyErrorMessage</p> It replaces LastName with FullName in the Administrator field: <div class="display-label"> @Html.LabelFor(model => model.InstructorID) </div> <div class="display-field"> @Html.DisplayFor(model => model.Administrator.FullName) </div> Finally, it adds hidden fields for the DepartmentID and Timestamp properties after the Html.BeginForm statement: @Html.HiddenFor(model => model.DepartmentID) @Html.HiddenFor(model => model.Timestamp) Run the Departments Index page and open a second browser window with the same URL. In the first window, click Edit on a department and change one of the values, but don't click Save yet:

In the second window, select Delete on the same department. The Delete confirmation page appears.

Click Save in the first browser window. The Index page confirms the change.

Now click Delete in the second browser. You see the concurrency error message, and the Department values are refreshed with what's currently in the database.

If you click Delete again, you're redirected to the Index page, which shows that the department has been deleted. This completes the introduction to handling concurrency conflicts. For information about other ways to handle various concurrency scenarios, see Optimistic Concurrency Patterns and Working with Property Values on the Entity Framework team blog. The next tutorial shows how to implement table-per-hierarchy inheritance for theInstructor and Student entities. Links to other Entity Framework resources can be found at the end of the last tutorial in this series.

Advanced Entity Framework Scenarios for an MVC Web Application (10 of 10)
By Tom Dykstra|April 11, 2011 The Contoso University sample web application demonstrates how to create ASP.NET MVC applications using the Entity Framework. The sample application is a website for a fictional Contoso University. It includes functionality such as student admission, course creation, and instructor assignments. This tutorial series explains the steps taken to build the Contoso University sample application. You candownload the completed application or create it by following the steps in the tutorial. The tutorial shows examples in C#. The downloadable sample contains code in both C# and Visual Basic. If you have questions that are not directly related to the tutorial, you can post them to the ASP.NET Entity Framework forum or theEntity Framework and LINQ to Entities forum. This tutorial series assumes you know how to work with ASP.NET MVC in Visual Studio. If you dont, a good place to start is a basic ASP.NET MVC Tutorial. If you prefer to work with the ASP.NET Web Forms model, see the Getting Started with the Entity Framework and Continuing with the Entity Framework tutorials. Before you start, make sure you have the following software installed on your computer: Visual Studio 2010 SP1 or Visual Web Developer Express 2010 SP1 (If you use one of these links, the following items will be installed automatically.) ASP.NET MVC 3 Tools Update Microsoft SQL Server Compact 4.0 Microsoft Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0 In the previous tutorial you implemented the repository and unit of work patterns. This tutorial covers the following topics: Performing raw SQL queries. Performing no-tracking queries. Examining queries sent to the database. Working with proxy classes. Disabling automatic detection of changes. Disabling validation when saving changes.

For most of these you will work with pages that you already created. To use raw SQL to do bulk updates you'll create a new page that updates the number of credits of all courses in the database:

And to use a no-tracking query you'll add new validation logic to the Department Edit page:

Performing Raw SQL Queries


The Entity Framework Code First API includes methods that enable you to pass SQL commands directly to the database. You have the following options: Use the DbSet.SqlQuery method for queries that return entity types. The returned objects must be of the type expected by the DbSet object, and they are automatically tracked by the database context unless you turn tracking off. (See the following section about the AsNoTracking method.) Use the DbDatabase.SqlQuery method for queries that return types that aren't entities. The returned data isn't tracked by the database context, even if you use this method to retrieve entity types. Use the DbDatabase.SqlCommand for non-query commands. One of the advantages of using the Entity Framework is that it avoids tying your code too closely to a particular method of storing data. It does this by generating SQL queries and commands for you, which also frees you from having to write them yourself. But there are exceptional scenarios when you need to run specific SQL queries that you have manually created, and these methods make it possible for you to handle such exceptions. As is always true when you execute SQL commands in a web application, you must take precautions to protect your site against SQL injection attacks. One way to do that is to use parameterized queries to make sure that strings submitted by a web page can't be interpreted as SQL commands. In this tutorial you'll use parameterized queries when integrating user input into a query.

Calling a Query that Returns Entities


Suppose you want the GenericRepository class to provide additional filtering and sorting flexibility without requiring that you create a derived class with additional methods. One way to achieve that would be to add a method that accepts a SQL query. You could then specify any kind of filtering or sorting you want in the controller, such as a Where clause that depends on a joins or subquery. In this section you'll see how to implement such a method. Create the GetWithRawSql method by adding the following code to GenericRepository.cs: public virtual IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters) { return dbSet.SqlQuery(query, parameters).ToList(); } In CourseController.cs, call the new method from the Details method, as shown in the following example: public ActionResult Details(int id) { var query = "SELECT * FROM Course WHERE CourseID = @p0"; return View(unitOfWork.CourseRepository.GetWithRawSql(query, id).Single()); } In this case you could have used the GetByID method, but you're using the GetWithRawSql method to verify that the GetWithRawSQL method works.

Run the Details page to verify that the select query works (select the Course tab and then Details for one course).

Calling a Query that Returns Other Types of Objects


Earlier you created a student statistics grid for the About page that showed the number of students for each enrollment date. The code that does this in HomeController.cs uses LINQ: var data = from student in db.Students group student by student.EnrollmentDate into dateGroup select new EnrollmentDateGroup() { EnrollmentDate = dateGroup.Key, StudentCount = dateGroup.Count() }; Suppose you want to write the code that retrieves this data directly in SQL rather than using LINQ. To do that you need to run a query that returns something other than entity objects, which means you need to use theDatabase.SqlQuery method. In HomeController.cs, replace the LINQ statement in the About method with the following code: var query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount " + "FROM Person " + "WHERE EnrollmentDate IS NOT NULL " + "GROUP BY EnrollmentDate"; var data = db.Database.SqlQuery<EnrollmentDateGroup>(query); Run the About page. It displays the same data it did before.

Calling an Update Query


Suppose Contoso University administrators want to be able to perform bulk changes in the database, such as changing the number of credits for every course. If the university has a large number of courses, it would be inefficient to retrieve them all as entities and change them individually. In this section you'll implement a web page that allows the user to specify a factor by which to change the number of credits for all courses, and you'll make the change by executing a SQL UPDATE statement. The web page will look like the following illustration:

In the previous tutorial you used the generic repository to read and update Course entities in the Course controller. For this bulk update operation, you need to create a new repository method that isn't in the generic repository. To do that, you'll create a dedicated CourseRepository class that derives from the GenericRepository class. In the DAL folder, create CourseRepository.cs and replace the existing code with the following code: using System; using ContosoUniversity.Models; namespace ContosoUniversity.DAL { public class CourseRepository : GenericRepository<Course> { public CourseRepository(SchoolContext context) : base(context) { } public int UpdateCourseCredits(int multiplier) { return context.Database.ExecuteSqlCommand("UPDATE Course SET Credits = Credits * {0}", multiplier); } } }

In UnitOfWork.cs, change the Course repository type from GenericRepository<Course> to CourseRepository: private CourseRepository courseRepository; public CourseRepository CourseRepository { get { if (this.courseRepository == null) { this.courseRepository = new CourseRepository(context); } return courseRepository; } } In CourseContoller.cs, add an UpdateCourseCredits method: public ActionResult UpdateCourseCredits(int? multiplier) { if (multiplier != null) { ViewBag.RowsAffected = unitOfWork.CourseRepository.UpdateCourseCredits(multiplier.Value); } return View(); } This method will be used for both HttpGet and HttpPost. When the HttpGet UpdateCourseCredits method runs, the multiplier variable will be null and the view will display an empty text box and a submit button, as shown in the preceding illustration. When the Update button is clicked and the HttpPost method runs, multiplier will have the value entered in the text box. The code then calls the repository UpdateCourseCredits method, which returns the number of affected rows, and that value is stored in the ViewBag object. When the view receives the number of affected rows in theViewBag object, it displays that number instead of the text box and submit button, as shown in the following illustration:

Create a view in the Views\Course folder for the Update Course Credits page:

In Views\Course\UpdateCourseCredits.cshtml, replace the existing code with the following code: @model ContosoUniversity.Models.Course @{ ViewBag.Title = "UpdateCourseCredits"; } <h2>Update Course Credits</h2> @if (ViewBag.RowsAffected == null) { using (Html.BeginForm()) { <p> Enter a number to multiply every course's credits by: @Html.TextBox("multiplier")

</p> <p> <input type="submit" value="Update" /> </p> } } @if (ViewBag.RowsAffected != null) { <p> Number of rows updated: @ViewBag.RowsAffected </p> } <div> @Html.ActionLink("Back to List", "Index") </div> Run the page by selecting the Courses tab, then adding "/UpdateCourseCredits" to the end of the URL in the browser's address bar (for example: http://localhost:50205/Course/UpdateCourseCredits). Enter a number in the text box:

Click Update. You see the number of rows affected:

Click Back to List to see the list of courses with the revised number of credits.

For more information about raw SQL queries, see Raw SQL Queries on the Entity Framework team blog.

No-Tracking Queries
When a database context retrieves database rows and creates entity objects that represent them, by default it keeps track of whether the entities in memory are in sync with what's in the database. The data in memory acts as a cache and is used when you update an entity. This caching is often unnecessary in a web application because context instances are typically short-lived (a new one is created and disposed for each request) and the context that reads an entity is typically disposed before that entity is used again.

You can specify whether the context tracks entity objects for a query by using the AsNoTracking method. Typical scenarios in which you might want to do that include the following: The query retrieves such a large volume of data that turning off tracking might noticeably enhance performance. You want to attach an entity in order to update it, but you earlier retrieved the same entity for a different purpose. Because the entity is already being tracked by the database context, you can't attach the entity that you want to change. One way to prevent this from happening is to use the AsNoTracking option with the earlier query. In this section you'll implement business logic that illustrates the second of these scenarios. Specifically, you'll enforce a business rule that says that an instructor can't be the administrator of more than one department. In DepartmentController.cs, add a new method that you can call from the Edit and Create methods to make sure that no two departments have the same administrator: private void ValidateOneAdministratorAssignmentPerInstructor(Department department) { if (department.PersonID != null) { var duplicateDepartment = db.Departments .Include("Administrator") .Where(d => d.PersonID == department.PersonID) .FirstOrDefault(); if (duplicateDepartment != null && duplicateDepartment.DepartmentID != department.DepartmentID) { var errorMessage = String.Format( "Instructor {0} {1} is already administrator of the {2} department.", duplicateDepartment.Administrator.FirstMidName, duplicateDepartment.Administrator.LastName, duplicateDepartment.Name); ModelState.AddModelError(string.Empty, errorMessage); } } } Add code in the try block of the HttpPost Edit method to call this new method if there are no validation errors. The try block now looks like the following example: if (ModelState.IsValid) { ValidateOneAdministratorAssignmentPerInstructor(department); } if (ModelState.IsValid) { db.Entry(department).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); }

Run the Department Edit page and try to change a department's administrator to an instructor who is already the administrator of a different department. You get the expected error message:

Now run the Department Edit page again and this time change the Budget amount. When you click Save, you see an error page:

The exception error message is "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key." This happened because of the following sequence of events: The Edit method calls the ValidateOneAdministratorAssignmentPerInstructor method, which retrieves all departments that have Kim Abercrombie as their administrator. That causes the English department to be read. Because that's the department being edited, no error is reported. As a result of this read operation, however, the English department entity that was read from the database is now being tracked by the database context. The Edit method tries to set the Modified flag on the English department entity created by the MVC model binder, but that fails because the context is already tracking an entity for the English department. One solution to this problem is to keep the context from tracking in-memory department entities retrieved by the validation query. There's no disadvantage to doing this, because you won't be updating this entity or reading it again in a way that would benefit from it being cached in memory. In DepartmentController.cs, in the ValidateOneAdministratorAssignmentPerInstructor method, specify no tracking, as shown in the following example: var duplicateDepartment = db.Departments .Include("Administrator") .Where(d => d.PersonID == department.PersonID)

.AsNoTracking() .FirstOrDefault(); Repeat your attempt to edit the Budget amount of a department. This time the operation is successful, and the site returns as expected to the Departments Index page, showing the revised budget value.

Examining Queries Sent to the Database


Sometimes it's helpful to be able to see the actual SQL queries that are sent to the database. To do this, you can examine a query variable in the debugger or call the query's ToString method. To try this out, you'll look at a simple query and then look at what happens to it as you add options such eager loading, filtering, and sorting. In Controllers/CourseController, replace the Index method with the following code: public ViewResult Index() { var courses = unitOfWork.CourseRepository.Get(); return View(courses.ToList()); } Now set a breakpoint in GenericRepository.cs on the return query.ToList(); and the return orderBy(query).ToList(); statements of the Get method. Run the project in debug mode and select the Course Index page. When the code reaches the breakpoint, examine the query variable. You see the query that's sent to SQL Server Compact. It's a simple Select statement: {SELECT [Extent1].[CourseID] AS [CourseID], [Extent1].[Title] AS [Title], [Extent1].[Credits] AS [Credits], [Extent1].[DepartmentID] AS [DepartmentID] FROM [Course] AS [Extent1]} Queries can be too long to display in the debugging windows in Visual Studio. To see the entire query, you can copy the variable value and paste it into a text editor:

Now you'll add a drop-down list to the Course Index page so that users can filter for a particular department. You'll sort the courses by title, and you'll specify eager loading for the Department navigation property. InCourseController.cs, replace the Index method with the following code:

public ActionResult Index(int? SelectedDepartment) { var departments = unitOfWork.DepartmentRepository.Get( orderBy: q => q.OrderBy(d => d.Name)); ViewBag.SelectedDepartment = new SelectList(departments, "DepartmentID", "Name", SelectedDepartment); int departmentID = SelectedDepartment.GetValueOrDefault(); return View(unitOfWork.CourseRepository.Get( filter: d => !SelectedDepartment.HasValue || d.DepartmentID == departmentID, orderBy: q => q.OrderBy(d => d.CourseID), includeProperties: "Department")); } The method receives the selected value of the drop-down list in the SelectedDepartment parameter. If nothing is selected, this parameter will be null. A SelectList collection containing all departments is passed to the view for the drop-down list. The parameters passed to the SelectList constructor specify the value field name, the text field name, and the selected item. For the Get method of the Course repository, the code specifies a filter expression, a sort order, and eager loading for the Department navigation property. The filter expression always returns true if nothing is selected in the drop-down list (that is, SelectedDepartment is null). In Views\Course\Index.cshtml, immediately before the opening table tag, add the following code to create the drop-down list and a submit button: @using (Html.BeginForm()) { <p>Select Department: @Html.DropDownList("SelectedDepartment","All") <input type="submit" value="Filter" /></p> } With the breakpoints still set in the GenericRepository class, run the Course Index page. Continue through the first two times that the code hits a breakpoint, so that the page is displayed in the browser. Select a department from the drop-down list and click Filter:

This time the first breakpoint will be for the departments query for the drop-down list. Skip that and view the queryvariable the next time the code reaches the breakpoint in order to see what the Course query now looks like. You'll see something like the following: {SELECT [Extent1].[CourseID] AS [CourseID],

[Extent1].[Title] AS [Title], [Extent1].[Credits] AS [Credits], [Extent1].[DepartmentID] AS [DepartmentID], [Extent2].[DepartmentID] AS [DepartmentID1], [Extent2].[Name] AS [Name], [Extent2].[Budget] AS [Budget], [Extent2].[StartDate] AS [StartDate], [Extent2].[PersonID] AS [PersonID], [Extent2].[Timestamp] AS [Timestamp] FROM [Course] AS [Extent1] INNER JOIN [Department] AS [Extent2] ON [Extent1].[DepartmentID] = [Extent2].[DepartmentID] WHERE (@p__linq__0 IS NULL) OR ([Extent1].[DepartmentID] = @p__linq__1)} You can see that the query is now a JOIN query that loads Department data along with the Course data, and that it includes a WHERE clause.

Working with Proxy Classes

When the Entity Framework creates entity instances (for example, when you execute a query), it often creates them as instances of a dynamically generated derived type that acts as a proxy for the entity. This proxy overrides some virtual properties of the entity to insert hooks for performing actions automatically when the property is accessed. For example, this mechanism is used to support lazy loading of relationships. Most of the time you don't need to be aware of this use of proxies, but there are exceptions: In some scenarios you might want to prevent the Entity Framework from creating proxy instances. For example, serializing non-proxy instances might be more efficient than serializing proxy instances.

When you instantiate an entity class using the new operator, you don't get a proxy instance. This means you don't get functionality such as lazy loading and automatic change tracking. This is typically okay; you generally don't need lazy loading, because you're creating a new entity that isn't in the database, and you generally don't need change tracking if you're explicitly marking the entity as Added. However, if you do need lazy loading and you need change tracking, you can create new entity instances with proxies using the Createmethod of the DbSet class. You might want to get an actual entity type from a proxy type. You can use the GetObjectType method of theObjectContext class to get the actual entity type of a proxy type instance. For more information, see Working with Proxies on the Entity Framework team blog.

Disabling Automatic Detection of Changes


The Entity Framework determines how an entity has changed (and therefore which updates need to be sent to the database) by comparing the current values of an entity with the original values. The original values are stored when the entity was queried or attached. Some of the methods that cause automatic change detection are the following: DbSet.Find

DbSet.Local DbSet.Remove DbSet.Add DbSet.Attach DbContext.SaveChanges DbContext.GetValidationErrors DbContext.Entry DbChangeTracker.Entries If you're tracking a large number of entities and you call one of these methods many times in a loop, you might get significant performance improvements by temporarily turning off automatic change detection using theAutoDetectChangesEnabled property. For more information, see Automatically Detecting Changes on the Entity Framework team blog.

Disabling Validation When Saving Changes


When you call the SaveChanges method, by default the Entity Framework validates the data in all properties of all changed entities before updating the database. If you've updated a large number of entities and you've already validated the data, this work is unnecessary and you could make the process of saving the changes take less time by temporarily turning off validation. You can do that using the ValidateOnSaveEnabled property. For more information, see Validation on the Entity Framework team blog.

Links to Entity Framework Resources


This completes this series of tutorials on using the Entity Framework in an ASP.NET MVC application. For more information about the Entity Framework, see the following resources: Introduction to the Entity Framework 4.1 (Code First) The Entity Framework Code First Class Library API Reference Entity Framework FAQ The Entity Framework Team Blog Entity Framework in the MSDN Library Entity Framework in the MSDN Data Developer Center Entity Framework Forums on MSDN Julie Lerman's blog Code First DataAnnotations Attributes Maximizing Performance with the Entity Framework in an ASP.NET Web Application Profiling Database Activity in the Entity Framework Entity Framework Power Tools The following posts on the Entity Framework Team Blog provide more information about some of the topics covered in these tutorials: Fluent API Samples. How to customize mapping using fluent API method calls. Connections and Models. How to connect to different types of databases. Pluggable Conventions. How to change conventions. Finding Entities. How to use the Find method with composite keys. Loading Related Entities. Additional options for eager, lazy, and explicit loading. Load and AsNoTracking. More on explicit loading.

Many of the blog posts listed here are for the CTP5 version of Entity Framework Code First. Most of the information in them remains accurate, but there are some changes between CTP5 and the officially released version of Code First. For information about how to use LINQ with the Entity Framework, see LINQ to Entities in the MSDN Library. For a tutorial that uses more MVC features and uses the Entity Framework, see MVC Music Store. For information about how to deploy your web application after you've built it, see ASP.NET Deployment Content Map in the MSDN Library.

ASP.NET MVC Controller Overview (C#)


By Stephen Walther|February 16, 2008 In this tutorial, Stephen Walther introduces you to ASP.NET MVC controllers. You learn how to create new controllers and return different types of action results. This tutorial explores the topic of ASP.NET MVC controllers, controller actions, and action results. After you complete this tutorial, you will understand how controllers are used to control the way a visitor interacts with an ASP.NET MVC website.

Understanding Controllers
MVC controllers are responsible for responding to requests made against an ASP.NET MVC website. Each browser request is mapped to a particular controller. For example, imagine that you enter the following URL into the address bar of your browser: http://localhost/Product/Index/3 In this case, a controller named ProductController is invoked. The ProductController is responsible for generating the response to the browser request. For example, the controller might return a particular view back to the browser or the controller might redirect the user to another controller. Listing 1 contains a simple controller named ProductController. Listing1 - Controllers\ProductController.cs using using using using using using System; System.Collections.Generic; System.Linq; System.Web; System.Web.Mvc; System.Web.Mvc.Ajax;

namespace MvcApplication1.Controllers { public class ProductController : Controller

{ // // GET: /Products/ public ActionResult Index() { // Add action logic here return View(); } } } As you can see from Listing 1, a controller is just a class (a Visual Basic .NET or C# class). A controller is a class that derives from the base System.Web.Mvc.Controller class. Because a controller inherits from this base class, a controller inherits several useful methods for free (We discuss these methods in a moment).

Understanding Controller Actions


A controller exposes controller actions. An action is a method on a controller that gets called when you enter a particular URL in your browser address bar. For example, imagine that you make a request for the following URL: http://localhost/Product/Index/3 In this case, the Index() method is called on the ProductController class. The Index() method is an example of a controller action. A controller action must be a public method of a controller class. C# methods, by default, are private methods. Realize that any public method that you add to a controller class is exposed as a controller action automatically (You must be careful about this since a controller action can be invoked by anyone in the universe simply by typing the right URL into a browser address bar). There are some additional requirements that must be satisfied by a controller action. A method used as a controller action cannot be overloaded. Furthermore, a controller action cannot be a static method. Other than that, you can use just about any method as a controller action.

Understanding Action Results


A controller action returns something called an action result. An action result is what a controller action returns in response to a browser request. The ASP.NET MVC framework supports several types of action results including: 1. 2. 3. ViewResult - Represents HTML and markup. EmptyResult - Represents no result. RedirectResult - Represents a redirection to a new URL.

4. 5. 6. 7. 8. 9.

JsonResult - Represents a JavaScript Object Notation result that can be used in an AJAX application. JavaScriptResult - Represents a JavaScript script. ContentResult - Represents a text result. FileContentResult - Represents a downloadable file (with the binary content). FilePathResult - Represents a downloadable file (with a path). FileStreamResult - Represents a downloadable file (with a file stream).

All of these action results inherit from the base ActionResult class. In most cases, a controller action returns a ViewResult. For example, the Index controller action in Listing 2 returns a ViewResult. Listing 2 - Controllers\BookController.cs using using using using using using System; System.Collections.Generic; System.Linq; System.Web; System.Web.Mvc; System.Web.Mvc.Ajax;

namespace MvcApplication1.Controllers { public class BookController : Controller { public ActionResult Index() { // Add action logic here return View(); } } } When an action returns a ViewResult, HTML is returned to the browser. The Index() method in Listing 2 returns a view named Index to the browser. Notice that the Index() action in Listing 2 does not return a ViewResult(). Instead, the View() method of the Controller base class is called. Normally, you do not return an action result directly. Instead, you call one of the following methods of the Controller base class: 1. 2. 3. 4. View - Returns a ViewResult action result. Redirect - Returns a RedirectResult action result. RedirectToAction - Returns a RedirectToRouteResult action result. RedirectToRoute - Returns a RedirectToRouteResult action result.

5. 6. 7. 8.

Json - Returns a JsonResult action result. JavaScriptResult - Returns a JavaScriptResult. Content - Returns a ContentResult action result. File - Returns a FileContentResult, FilePathResult, or FileStreamResult depending on the parameters passed to the method.

So, if you want to return a View to the browser, you call the View() method. If you want to redirect the user from one controller action to another, you call the RedirectToAction() method. For example, the Details() action in Listing 3 either displays a view or redirects the user to the Index() action depending on whether the Id parameter has a value. Listing 3 - CustomerController.cs using System.Web.Mvc; namespace MvcApplication1.Controllers { public class CustomerController : Controller { public ActionResult Details(int? id) { if (!id.HasValue) return RedirectToAction("Index"); return View(); } public ActionResult Index() { return View(); } } } The ContentResult action result is special. You can use the ContentResult action result to return an action result as plain text. For example, the Index() method in Listing 4 returns a message as plain text and not as HTML. Listing 4 - Controllers\StatusController.cs using System.Web.Mvc; namespace MvcApplication1.Controllers { public class StatusController : Controller {

public ActionResult Index() { return Content("Hello World!"); } } } When the StatusController.Index() action is invoked, a view is not returned. Instead, the raw text "Hello World!" is returned to the browser. If a controller action returns a result that is not an action result - for example, a date or an integer - then the result is wrapped in a ContentResult automatically. For example, when the Index() action of the WorkController in Listing 5 is invoked, the date is returned as a ContentResult automatically. Listing 5 - WorkController.cs using System; using System.Web.Mvc; namespace MvcApplication1.Controllers { public class WorkController : Controller { public DateTime Index() { return DateTime.Now; } } } The Index() action in Listing 5 returns a DateTime object. The ASP.NET MVC framework converts the DateTime object to a string and wraps the DateTime value in a ContentResult automatically. The browser receives the date and time as plain text.

Summary
The purpose of this tutorial was to introduce you to the concepts of ASP.NET MVC controllers, controller actions, and controller action results. In the first section, you learned how to add new controllers to an ASP.NET MVC project. Next, you learned how public methods of a controller are exposed to the universe as controller actions. Finally, we discussed the different types of action results that can be returned from a controller action. In particular, we discussed how to return a ViewResult, RedirectToActionResult, and ContentResult from a controller action.

Creating Custom Routes (C#)


By Microsoft ASP.NET Team|February 16, 2009|C# Learn how to add custom routes to an ASP.NET MVC application. In this tutorial, you learn how to modify the default route table in the Global.asax file. In this tutorial, you learn how to add a custom route to an ASP.NET MVC application. You learn how to modify the default route table in the Global.asax file with a custom route. For many simple ASP.NET MVC applications, the default route table will work just fine. However, you might discover that you have specialized routing needs. In that case, you can create a custom route. Imagine, for example, that you are building a blog application. You might want to handle incoming requests that look like this: /Archive/12-25-2009 When a user enters this request, you want to return the blog entry that corresponds to the date 12/25/2009. In order to handle this type of request, you need to create a custom route. The Global.asax file in Listing 1 contains a new custom route, named Blog, which handles requests that look like /Archive/entry date. Listing 1 - Global.asax (with custom route) using System.Web.Mvc; using System.Web.Routing; namespace MvcApplication1 { public class MvcApplication : System.Web.HttpApplication { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Blog", "Archive/{entryDate}", parameters new { controller = "Archive", action = "Entry" } defaults ); routes.MapRoute( "Default", "{controller}/{action}/{id}", parameters new { controller = "Home", action = "Index", id = defaults

// Route name // URL with // Parameter

// Route name // URL with "" } // Parameter

); } protected void Application_Start() { RegisterRoutes(RouteTable.Routes); } } } The order of the routes that you add to the route table is important. Our new custom Blog route is added before the existing Default route. If you reversed the order, then the Default route always will get called instead of the custom route. The custom Blog route matches any request that starts with /Archive/. So, it matches all of the following URLs: /Archive/12-25-2009 /Archive/10-6-2004 /Archive/apple The custom route maps the incoming request to a controller named Archive and invokes the Entry() action. When the Entry() method is called, the entry date is passed as a parameter named entryDate. You can use the Blog custom route with the controller in Listing 2. Listing 2 - ArchiveController.cs using System; using System.Web.Mvc; namespace MvcApplication1.Controllers { public class ArchiveController : Controller { public string Entry(DateTime entryDate) { return "You requested the entry from " + entryDate.ToString(); } } } Notice that the Entry() method in Listing 2 accepts a parameter of type DateTime. The MVC framework is smart enough to convert the entry date from the URL into a DateTime value automatically. If the entry date parameter from the URL cannot be converted to a DateTime, an error is raised (see Figure 1). Figure 1 - Error from converting parameter

Figure 01: Error from converting parameter (Click to view full-size image)

Summary
The goal of this tutorial was to demonstrate how you can create a custom route. You learned how to add a custom route to the route table in the Global.asax file that represents blog entries. We discussed how to map requests for blog entries to a controller named ArchiveController and a controller action named Entry().

Creating a Route Constraint (C#)


By Stephen Walther|February 16, 2009 In this tutorial, Stephen Walther demonstrates how you can control how browser requests match routes by creating route constraints with regular expressions. You use route constraints to restrict the browser requests that match a particular route. You can use a regular expression to specify a route constraint. For example, imagine that you have defined the route in Listing 1 in your Global.asax file. Listing 1 - Global.asax.cs

routes.MapRoute( "Product", "Product/{productId}", new {controller="Product", action="Details"} ); Listing 1 contains a route named Product. You can use the Product route to map browser requests to the ProductController contained in Listing 2. Listing 2 - Controllers\ProductController.cs using System.Web.Mvc; namespace MvcApplication1.Controllers { public class ProductController : Controller { public ActionResult Details(int productId) { return View(); } } } Notice that the Details() action exposed by the Product controller accepts a single parameter named productId. This parameter is an integer parameter. The route defined in Listing 1 will match any of the following URLs: /Product/23 /Product/7

Unfortunately, the route will also match the following URLs: /Product/blah /Product/apple

Because the Details() action expects an integer parameter, making a request that contains something other than an integer value will cause an error. For example, if you type the URL /Product/apple into your browser then you will get the error page in Figure 1.

Figure 01: Seeing a page explode (Click to view full-size image) What you really want to do is only match URLs that contain a proper integer productId. You can use a constraint when defining a route to restrict the URLs that match the route. The modified Product route in Listing 3 contains a regular expression constraint that only matches integers. Listing 3 - Global.asax.cs routes.MapRoute(

"Product", "Product/{productId}", new {controller="Product", action="Details"}, new {productId = @"\d+" } ); The regular expression \d+ matches one or more integers. This constraint causes the Product route to match the following URLs: /Product/3 /Product/8999

But not the following URLs: /Product/apple /Product

These browser requests will be handled by another route or, if there are no matching routes, a The resource could not be found error will be returned.

Creating a Custom Route Constraint (C#)


By Stephen Walther|February 16, 2009 Stephen Walther demonstrates how you can create a custom route constraint. We implement a simple custom constraint that prevents a route from being matched when a browser request is made from a remote computer. The goal of this tutorial is to demonstrate how you can create a custom route constraint. A custom route constraint enables you to prevent a route from being matched unless some custom condition is matched. In this tutorial, we create a Localhost route constraint. The Localhost route constraint only matches requests made from the local computer. Remote requests from across the Internet are not matched. You implement a custom route constraint by implementing the IRouteConstraint interface. This is an extremely simple interface which describes a single method:

bool Match( HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection ) The method returns a Boolean value. If you return false, the route associated with the constraint wont match the browser request.

The Localhost constraint is contained in Listing 1. Listing 1 - LocalhostConstraint.cs using System.Web; using System.Web.Routing; namespace MvcApplication1.Constraints { public class LocalhostConstraint : IRouteConstraint { public bool Match ( HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection ) { return httpContext.Request.IsLocal; } } } The constraint in Listing 1 takes advantage of the IsLocal property exposed by the HttpRequest class. This property returns true when the IP address of the request is either 127.0.0.1 or when the IP of the request is the same as the servers IP address. You use a custom constraint within a route defined in the Global.asax file. The Global.asax file in Listing 2 uses the Localhost constraint to prevent anyone from requesting an Admin page unless they make the request from the local server. For example, a request for /Admin/DeleteAll will fail when made from a remote server. Listing 2 - Global.asax using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using MvcApplication1.Constraints; namespace MvcApplication1 { public class MvcApplication : System.Web.HttpApplication { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute( "Admin", "Admin/{action}", new {controller="Admin"}, new {isLocal=new LocalhostConstraint()} ); //routes.MapRoute( // "Default", name // parameters "{controller}/{action}/{id}",

// Route // URL with

// new { controller = "Home", action = "Index", id = "" } // Parameter defaults //); } protected void Application_Start() { RegisterRoutes(RouteTable.Routes); } } } The Localhost constraint is used in the definition of the Admin route. This route wont be matched by a remote browser request. Realize, however, that other routes defined in Global.asax might match the same request. It is important to understand that a constraint prevents a particular route from matching a request and not all routes defined in the Global.asax file. Notice that the Default route has been commented out from the Global.asax file in Listing 2. If you include the Default route, then the Default route would match requests for the Admin controller. In that case, remote users could still invoke actions of the Admin controller even though their requests wouldnt match the Admin route.

Anda mungkin juga menyukai