Adding a New Field to the Movie Model and Table (C#)
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 newRating
property to the existing Movie
class. Open the Movie.cs file and add the Rating
property like this one:public string Rating { get; set; }The complete
Movie
class now looks like the following code: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; } public string Rating { get; 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 newRating
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:
- 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.
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 an
Application_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; // Database.SetInitialize using MvcMovie.Models; // MovieInitializerThen 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 MovieDBContext
instance 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.
No comments:
Post a Comment