Wednesday, December 4, 2024

EF Core 9.0: UseSeeding and UseAsyncSeeding

EF Core 9.0 (EF 9) introduced new methods UseSeeding and UseAsyncSeeding to seed the initial data.

Let's have a look at an example.

Consider the following MyDbContext.

public record Blog
{
    public int Id { getset}

    public string Name { getset}
}

public class MyDbContext : DbContext
{
    public DbSet<Blog> Blogs { getset}

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlServer("<ConnectionString>");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>(x =>
        {
            x.HasData
            (
               new Blog
               {
                   Id = 1,
                   Name = "Some Blog"
               }
            );
        });
    }
}

Here in the above example, some data is seeded through Model-managed data which is handly when we want to include seed data in migrations as well and it was available for as far as I can remember.

Now let's see where UseSeeding and UseAsyncSeeding come in.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer("<ConnectionString>")
        .UseSeeding((context_) =>
        {
            Blogblog = context.Set<Blog>().SingleOrDefault(b => b.Name == "Some Other Blog");
            if (blog is null)
            {
                context.Set<Blog>().Add(new Blog { Name = "Some Other Blog" });
                context.SaveChanges();
            }
        })
        .UseAsyncSeeding(async (context_cancellationToken) =>
        {
            Blogblog = await context.Set<Blog>().SingleOrDefaultAsync(b => b.Name == "Some Other Blog"cancellationToken);
            if (blog is null)
            {
                context.Set<Blog>().Add(new Blog { Name = "Some Other Blog" });
                await context.SaveChangesAsync(cancellationToken);
            }
        });
}

As you can see, the new seeding methods are available as part of configuring DbContextOptions. Unlike Model-managed data, you can seed data for multiple DbSets all from a single place. These methods will get called as part of EnsureCreated operation, Migrate and dotnet ef database update command, even if there are no model changes and no migrations were applied.

Now let's see this in action.

using var context = new MyDbContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

foreach (Blog blog in context.Blogs)
{
    Console.WriteLine(blog.Name);
}

// Output:
//Some Blog
//Some Other Blog

And a note from the official documentation:

UseSeeding is called from the EnsureCreated method, and UseAsyncSeeding is called from the EnsureCreatedAsync method. When using this feature, it is recommended to implement both UseSeeding and UseAsyncSeeding methods using similar logic, even if the code using EF is asynchronous. EF Core tooling currently relies on the synchronous version of the method and will not seed the database correctly if the UseSeeding method is not implemented.

More read:
   EF Core: 
Data Seeding

Happy Coding.

Regards,
Jaliya