How can I get my database to seed using Entity Framework CodeFirst?
Solution 1
This is what my DbContext classes all look like and they seed just fine:
public class MyDbContext : DbContext
{
public DbSet<MyClass> MyClasses { get; set; }
protected override void OnModelCreating (DbModelBuilder modelBuilder)
{
base.OnModelCreating (modelBuilder);
modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention> ();
// Add any configuration or mapping stuff here
}
public void Seed (MyDbContext Context)
{
#if DEBUG
// Create my debug (testing) objects here
var TestMyClass = new MyClass () { ... };
Context.MyClasses.Add (TestMyClass);
#endif
// Normal seeding goes here
Context.SaveChanges ();
}
public class DropCreateIfChangeInitializer : DropCreateDatabaseIfModelChanges<MyDbContext>
{
protected override void Seed (MyDbContext context)
{
context.Seed (context);
base.Seed (context);
}
}
public class CreateInitializer : CreateDatabaseIfNotExists<MyDbContext>
{
protected override void Seed (MyDbContext context)
{
context.Seed (context);
base.Seed (context);
}
}
static MyDbContext ()
{
#if DEBUG
Database.SetInitializer<MyDbContext> (new DropCreateIfChangeInitializer ());
#else
Database.SetInitializer<MyDbContext> (new CreateInitializer ());
#endif
}
}
I have used this pattern a few times and it has worked out very well for me.
Solution 2
My Seed
method was not invoked even with proper call to Database.SetInitializer
in Application_Start
... The reason for it was really simple: initializer may not be invoked at all if you don't yet have any code that actually uses database context.
Solution 3
This is my sad little tale.
First, lessons learned:
- The seed method won't be called until the context is used.
- The Global.asax.cs won't hit a breakpoint on first run bc it runs before the debugger is attached. To hit a breakpoint on Global.asax.cs, you have can add some white space to Web.config and hit a page; then it will get hit.
- If there are VS connections to the db, the seeding won't happen. The app will throw an error.
So, to avoid the sadness:
- Disconnect your VS connection.
- Switch the base class DropCreateDatabaseAlways for one go.
- Hit a page that uses the context.
Now, the sadness:
- I had my custom Initializer class in my Global.asax.cs file. I had a break point on my Initializer Seed method; I started the application and the method never got hit. :(
- I point a break point in my Database.SetInitializer call in Application_Start. That never got hit. :(
- I realized that I had no db schema changes, so then I changed DropCreateDatabaseIfModelChanges to DropCreateDatabaseAlways. Still, nothing. :(
- I finally went to a page that uses the context, and it worked. :/
Solution 4
You can call update-database
to manually run the seed method inside the Configuration
class. This requires enable-migrations
to be on as well.
PM> update-database
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
No pending code-based migrations.
Running Seed method.
internal sealed class Configuration : DbMigrationsConfiguration<ProjectManager.Data.Database.ProjectDb>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(ProjectManager.Data.Database.ProjectDb context)
{
context.Status.AddOrUpdate(
new Status() { Id = 1, Text = "New" },
new Status() { Id = 2, Text = "Working" },
new Status() { Id = 3, Text = "Completed" },
new Status() { Id = 4, Text = "Skipped" }
);
}
}
Solution 5
The following change in the Global.asax file worked for me:
Old Code:
protected void Application_Start()
{
Database.SetInitializer<mycontextclassname>(new DropCreateDatabaseAlways<mycontextclassname>());
...
}
New Code:
protected void Application_Start()
{
Database.SetInitializer<mycontextclassname>(new DropCreateDatabaseAlways<mycontextclassname>());
Database.SetInitializer(new Initializer());
...
}
Alec
Updated on July 08, 2022Comments
-
Alec almost 2 years
The database is created successfully (as are the tables) but is not seeded. I have spent several hours and read tons of articles but have not been able to get it. Any suggestions?
On a side note, is it possible to call the initializer without having a reference to my DatabaseContext in the client?
I have included all the relevant code I could think of. If anything else would be helpful, please let me know.
Things I've Tried:
- I deleted my connection string (since it defaults to sqlexpress anyways, just the name changed)
- I changed DropCreateDatabaseIfModelChanges to DropCreateDatabaseAlways, still the same.
Edit: The really weird thing is it worked once, but I have no idea how or why it broke again. I am assuming connection strings, but who knows.
DatabaseInitializer.cs
public class DatabaseInitializer : DropCreateDatabaseIfModelChanges<DatabaseContext> { protected override void Seed(DatabaseContext context) { // Seeding data here context.SaveChanges(); } }
DatabaseContext.cs
public class DatabaseContext : DbContext { protected override void OnModelCreating(DbModelBuilder mb) { // Random mapping code } public DbSet<Entity1> Entities1 { get; set; } public DbSet<Entity2> Entities2 { get; set; } }
Global.asax.cs - Application_Start()
protected void Application_Start() { Database.SetInitializer<DatabaseContext>(new DatabaseInitializer()); AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); }
Client web.config
<connectionStrings> <add name="DatabaseContext" connectionString="data source=.\SQLEXPRESS;Database=Database;Integrated Security=SSPI;" providerName="System.Data.SqlClient" /> </connectionStrings>
SOLUTION
For the sake of documentation, I am sharing my solution here. Navigating all the comments would be a pain anyways. In the end I had DatabaseInitializer and DatabaseContext in separate classes. I don't really understand while these tiny changes fixed it, but here it is.
DatabaseInitializer.cs
public class DatabaseInitializer : CreateDatabaseIfNotExists<DatabaseContext> { protected override void Seed(DatabaseContext context) { // Seed code here } }
DatabaseContext.cs
public class DatabaseContext : DbContext { public DatabaseContext() : base("MyDatabase") { } protected override void OnModelCreating(DbModelBuilder mb) { // Code here } public DbSet<Entity> Entities { get; set; } // Other DbSets }
Global.asax.cs - Application_Start()
protected void Application_Start() { Database.SetInitializer(new DatabaseInitializer()); AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); }
-
Alec almost 13 yearsI tried switching. No luck. Still created the database and all the tables but did not seed.
-
Richard Forrest almost 13 yearsI call the Database.SetInitializer<DatabaseContext>(new DatabaseInitializer()); in the constructor of the DatabaseContext which works for me
-
Alec almost 13 yearsI can't reference DatabaseInitializer in DatabaseContext as that would be a circular reference. Also it should not need to go in the DatabaseContext constructor, as it should be called by the DatabaseInitializer constructor in App_Start (as far as my understanding goes).
-
Alec almost 13 yearsThis is not the solution I exactly implemented, I have detailed my solution in the original question above. Just posting this comment for ease of use for future users. Thanks to jdangelo for all the help!
-
Rasshme Chawla over 11 yearsthis works and thanks for this useful info. I closed the database connection and then switched the base classto DropCreateDatabaseAlways. It worked well .
-
xkingpin about 10 yearsThis is a great method for doing this because it doesn't require any changes to Application_Start() and therefore re-usable
-
Piotr Kula over 9 yearsthe Global only runs the first time. So close IISExpress. Set your break point in global and run. It will hit it, once, because its IIS application level.
-
JaredReisinger over 7 yearsThe base class's default constructor is automatically called any time you don't call a specific variant of it directly. See stackoverflow.com/questions/13166019/… for further details.
-
Jon over 7 yearsYes, I would have to agree with you. The only thing is, when I wrote that three years ago I seemed to have noticed that omitting the (redundant) base() call led to a different result than including it. I might dig that code out at some point, but it's probably safest to assume that there was some other effect exerting itself there.