How can I get my database to seed using Entity Framework CodeFirst?

68,960

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:

  1. The seed method won't be called until the context is used.
  2. 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.
  3. 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:

  1. 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. :(
  2. I point a break point in my Database.SetInitializer call in Application_Start. That never got hit. :(
  3. I realized that I had no db schema changes, so then I changed DropCreateDatabaseIfModelChanges to DropCreateDatabaseAlways. Still, nothing. :(
  4. 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()); 
        ...
    }
Share:
68,960
Alec
Author by

Alec

Updated on July 08, 2022

Comments

  • Alec
    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:

    1. I deleted my connection string (since it defaults to sqlexpress anyways, just the name changed)
    2. 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
    Alec almost 13 years
    I tried switching. No luck. Still created the database and all the tables but did not seed.
  • Richard Forrest
    Richard Forrest almost 13 years
    I call the Database.SetInitializer<DatabaseContext>(new DatabaseInitializer()); in the constructor of the DatabaseContext which works for me
  • Alec
    Alec almost 13 years
    I 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
    Alec almost 13 years
    This 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
    Rasshme Chawla over 11 years
    this works and thanks for this useful info. I closed the database connection and then switched the base classto DropCreateDatabaseAlways. It worked well .
  • xkingpin
    xkingpin about 10 years
    This is a great method for doing this because it doesn't require any changes to Application_Start() and therefore re-usable
  • Piotr Kula
    Piotr Kula over 9 years
    the 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
    JaredReisinger over 7 years
    The 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
    Jon over 7 years
    Yes, 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.