How to seed in Entity Framework Core 3.0?

asp.net-core-3.0 c# entity-framework-core

Question

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).

1
2
2/10/2020 6:49:37 PM

Accepted Answer

If you want to seed on application start, in your applications starting method you can do a check for the data you want using conditional checks and if no returns, add those classes to the context and save the changes.

The seeding in EF Core is designed for migration, its initialised data for the database, not for an applications runtime. If you want a set of data to be unchanged then consider utilising an alternative method? Like keeping it in xml/json format with in memory caching via properties with field checks.

You could use the deletion/creation syntax on application start but its generally frowned upon as state lacks permanence.

Unfortunately for what you want, it has to be a work around as its not in the expected line of EF's functioning.

2
2/7/2020 3:46:28 PM

Popular Answer

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 ();
         }



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow