Data seeding refers to the practice of populating your database with basic information so that your application can work correctly. This is something that we’ve been capable of doing in Entity Framework Core for a long time. Nevertheless, the old way of doing this was very limiting. For example, we weren’t able to:
- Consult the database while doing the seeding
- Consult an external service
- Allow the database to specify the Id of the record to be inserted
But now, in Entity Framework Core 9, we have two new methods of doing data seeding: UseSeeding and UseDataSeeding. Let’s see.
The old ways
First, let’s see how things were done in the past. In order for us to do data seeding, we had to use the fluent API. For example, imagine we have a Person entity. We want to use seeding to populate the People table. This ensures that we always have a “Felipe” record in the database. For that, we can say:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Person>().HasData(new Person { Id = 1, Name = "Felipe" });
}
The problem with this is that this is using migrations. Migrations allows us to make changes in our database using C# code, like creating tables. Also, we can modify data with it. The problem with this is that they are sort of static. Not allowing us to write too much custom code. Which means that, we were limited to how we were doing data seeding.
UseSeeding and UseAsyncSeeding to the Rescue
Luckily for us, in EF Core 9, we get two new methods to do data seeding: UseSeeding and UseAsyncSeeding. They both do the same, but one is synchronous and the other is asynchronous. We can use them in the program class, or wherever we configure the DbContext. For example, in an ASP.NET Core Web API app, in the program class, we can do the following:
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer("name=DefaultConnection")
.UseSeeding((context, _) =>
{
var felipeExists = context.Set<Person>().Any(x => x.Name == "Felipe");
if (!felipeExists)
{
context.Set<Person>().Add(new Person { Name = "Felipe" });
context.SaveChanges();
}
}));
The code is straightforward. We check for the existence of the record in the database. If it doesn’t exist, we insert it.
The first thing we see is that, while doing our seeding process, we are able to query the database. That by itself opens a lot of possibilities of the things we can do then seeding the database. Notice also that we did not need to specify the Id of the record. That’s handled by the database. Finally, this is all C# code that’s not expected to be converted to SQL (unlike with migrations), so, we have a lot of functionality at our disposal: want to consult another Web API while doing the seeding? go for it.
In order to execute the UseSeeding function, we have several choices, like:
- Do an Update-Database in the Package Manager Console (this is for Visual Studio users, for folks that use the dotnet CLI, you can use the equivalent command: dotnet ef database update)
- Execute the EnsureCreated method
The latter option is fine for production (although, other options exists), and the former is ideal in development.
So, if indeed we do an Update-Database, we are going to see that we can run the UseSeeding method. On the other hand, we can execute the EnsureCreated method. We can do this at the startup of the app. For example, we can do the following in the Program class after initializing the WebApplication:
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var applicationDbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
applicationDbContext.Database.EnsureCreated();
}
Using the EnsureCreated method, we do several things, among them, we run the UseSeeding method.
What about async?
And what about UseAsyncSeeding. As its name implies, this is the asynchronous version of seeding. We can use it basically in the same way as UseSeeding. We are recommended to use them together. For example, here we have both, the sync and async implementations:
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer("name=DefaultConnection")
.UseSeeding((context, _) =>
{
var felipeExists = context.Set<Person>().Any(x => x.Name == "Felipe");
if (!felipeExists)
{
context.Set<Person>().Add(new Person { Name = "Felipe" });
context.SaveChanges();
}
})
.UseAsyncSeeding(async (context, _, cancellationToken) =>
{
var felipeExists = await context.Set<Person>().AnyAsync(x => x.Name == "Felipe");
if (!felipeExists)
{
context.Set<Person>().Add(new Person { Name = "Felipe" });
await context.SaveChangesAsync();
}
}));
As you can see, they basically do the same thing. The only difference is that one is sync and the other one is async.
Conclusion
UseSeeding and UseAsyncSeeding are the new ways of doing data seeding in Entity Framework Core. They are effective when we need to query the DB before doing the seeding. They are also beneficial when we need to do complex logic. The old fashion way is helpful if the data is completely static. It is also useful when we need to guarantee the Id of each record. For everything else, we are advice to use the new way.
If you want to learn more about Entity Framework Core, buy me full course with a discount: https://felipe-gavilan.azurewebsites.net/api/Redireccion?curso=entity-framework-core-eng
Kind regards!