When you scaffold an existing Entity Framework model, using MVC5 scaffolding in Visual Studio 2013, you can easily run into the issue of “The model backing the <DbContextName> context has changed since the database was created” as shown below.
For example, in an MVC project, add the following model.
public class Product { public int Id { get; set; } public string Name { get; set; } }
Scaffold the Product model using “MVC 5 Controller with views, using Entity Framework” scaffolder in Visual Studio 2013. View the generated pages, Index/Edit/Details/Create, to verify things are working properly.
Now, suppose we need to modify the Product model to add more fields, like Description and Category.
public class Product { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Category { get; set; } }
Scaffold the Product model again and view a scaffold page, you will see the error message mentioned above. The exception is caused by the model change, not actually a scaffolding issue. However, because of the order in which users do things, it might sometimes appear to be related to scaffolding.
There are a few ways to workaround this error; each has some pros and cons, depending on what you need.
1) Code First Migration with Entity Framework.
Following the instructions at Code First Migration requires a number of steps.
First, you need to bring up “Package Manager Console”, Tools –> Library Package Manger –> Package Manager Console, and run the Enable-Migrations command in the console as the first step to enable migrations for your context.
PM> Enable-Migrations -ContextTypeName WebApplication7.Models.WebApplication7Context
Checking if the context targets an existing database...
Detected database created with a database initializer. Scaffolded migration '201309271941079_InitialCreate' corresponding to existing database. To use an automatic migration instead, delete the Migrations folder and re-run Enable-Migrations specifying the -EnableAutomaticMigrations parameter.
Code First Migrations enabled for project WebApplication7.
From now on, each time you modify the model and re-run the scaffolding, you need to run the Add-Migration, and Update-Database commands. In our example above, after adding Description and Category, we run those commands as follows.
PM> Add-Migration AddDescriptionCategory
Scaffolding migration 'AddDescriptionCategory'.
The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration AddDescriptionCategory' again.
PM> Update-Database
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Applying explicit migrations: [201309280107120_AddDescriptionCategory].
Applying explicit migration: 201309280107120_AddDescriptionCategory.
Running Seed method.
PM>
Now you can test view the scaffolded pages and won’t see the error anymore.
The good thing about this approach is you just modify the exact table representing the model that you’re changing, and leave the rest of the database intact. However, if you do a lot of changes on the model and want some quick verification of the scaffold pages, going through those steps each time can add a considerable amount of time. Also, those steps will add a number of files into your projects that can grow quickly.
Migration is usually the best choice when you use it to deploy to production the initial time and when you’re deploying updates to production later. For more information about migrations for deployment, see http://www.asp.net/web-forms/tutorials/deployment/visual-studio-web-deployment/introduction.
For rapid development, option 2 and 3 below might be a better choice.
2) Modify the Database name in the connectionString
A quick workaround to the existing database issue without going through all the database migration steps would be to modify the connection string in web.config. Each time you change the code first model and scaffold it, you can give the connection string a new “Initial Catalog” and “AttachDbFilename” value.
This approach will create a new database each time you give a new database name in the connection string and leave the old database unused. This might not be ideal if you already have a large existing database. However, it could be useful if you just want to verify something quick once or twice.
3) Database Initializer with Entity Framework
With the second workaround, you still need to remember to modify the connection string each time the model is changed. If you want to make one setting, and forget about it when you’re building your app, you can achieve this with a custom initializer class. EF will drop, recreate and re-seed the database each time the model changes, and you set up this action only once in your project.
Following is an example of a custom initializer class.
public class SchoolInitializer : DropCreateDatabaseIfModelChanges<SchoolContext>
{
protected override void Seed(WebApplication3Context 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();
}
}
Then add the following code in Global.asax.cs
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { Database.SetInitializer<SchoolContext>(new SchoolInitializer()); } }
From now on, you can just focus on building the right model for your application, scaffold, and test it as many times as you need, and don’t have to do anything extra in order to avoid the error mentioned at the beginning of this blog.
This approach will drop and create a new database only when the model is changed, and it won’t leave behind a lot of unused databases, which option 2 will do.
Another way to tell Entity Framework to use your initializer class is to add an element to the entityFramework element in the application’s Web.config file, instead of modifying the Global.asax.cs file. For more information about how to do it, you can read Tom’s fabulous blog at Creating an Entity Framework Data Model for an ASP.NET MVC Application. Setting up the initializer in the Web.Config file is sometimes preferable because you can turn it on or off or change it without changing code.
0 comments