How to seed in Entity Framework Core 3.0?

Claudiu A picture Claudiu A · Feb 7, 2020 · Viewed 12.8k times · Source

I am trying seed the database with some data, using ASP.NET CORE 3.0 and EF Core.

I've created my DbContext and according to documentation, online sources, or even EF Core 2.1 questions (I could not find any breaking changes on this topic).

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Band>().HasData(
            new Band()
            {
                Id = Guid.Parse("e96bf6d6-3c62-41a9-8ecf-1bd23af931c9"),
                Name = "SomeName",
                CreatedOn = new DateTime(1980, 2, 13),
                Description = "SomeDescription"
            });

        base.OnModelCreating(modelBuilder);           
    }

This does not do what I expect: nothing is seeded on starting the application (even if during debug the method is called from somewhere).

However, if I add a migration, the migration contains the corresponding insert statement (which is not the kind of seeding I am looking for).

Question: What is the right way to have the seed of the database being performed on application start?

By seed the database, I mean that I expect some data to be ensured in some tables everytime the application is started.


I have the alternative to create a seeding class and handle it after the Database.Migrate with custom code, but this seems like a workaround, because the documentation specifies that OnModelCreating should be used to seed data).


So to my understanding after reading the answers and re-reading the documentation, what they mean by "seed" is an "initialization" which can take place right next to the data model (which is why it felt strange - mixing the model creation with the data seeding part).

Answer

AliReza picture AliReza · Feb 7, 2020

if you have complex seed data default EF core feature is not a good idea to use. for example, you can't add your seed data depending on your configurations or system environment.

I'm using a custom service and dependency injection to add my seed data and apply any pending migrations for the context when the application starts. ill share my custom service hopes it helps :

IDbInitializer.cs

    public interface IDbInitializer
    {
        /// <summary>
        /// Applies any pending migrations for the context to the database.
        /// Will create the database if it does not already exist.
        /// </summary>
        void Initialize();

        /// <summary>
        /// Adds some default values to the Db
        /// </summary>
        void SeedData();
    }

DbInitializer.cs

    public class DbInitializer : IDbInitializer {
        private readonly IServiceScopeFactory _scopeFactory;

        public DbInitializer (IServiceScopeFactory scopeFactory) {
            this._scopeFactory = scopeFactory;
        }

        public void Initialize () {
            using (var serviceScope = _scopeFactory.CreateScope ()) {
                using (var context = serviceScope.ServiceProvider.GetService<AppDbContext> ()) {
                    context.Database.Migrate ();
                }
            }
        }

        public void SeedData () {
            using (var serviceScope = _scopeFactory.CreateScope ()) {
                using (var context = serviceScope.ServiceProvider.GetService<AppDbContext> ()) {

                    //add admin user
                    if (!context.Users.Any ()) {
                        var adminUser = new User {
                            IsActive = true,
                            Username = "admin",
                            Password = "admin1234", // should be hash
                            SerialNumber = Guid.NewGuid ().ToString ()
                        };
                        context.Users.Add (adminUser);
                    }

                    context.SaveChanges ();
                }
            }
        }
    }

for using this service you can add it to your service collection :

 // StartUp.cs -- ConfigureServices method
 services.AddScoped<IDbInitializer, DbInitializer> ()

because i want to use this service every time my program starts i'm using injected service this way :

 // StartUp.cs -- Configure method
         var scopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory> ();
         using (var scope = scopeFactory.CreateScope ()) {
            var dbInitializer = scope.ServiceProvider.GetService<IDbInitializer> ();
            dbInitializer.Initialize ();
            dbInitializer.SeedData ();
         }