Skip to content

Commit 97329a0

Browse files
authored
Merge pull request #197 from nanotaboada/feature/use-async-seeding
refactor(data): improve seeding with UseAsyncSeeding
2 parents 33f5b67 + 59ae2fc commit 97329a0

File tree

4 files changed

+73
-50
lines changed

4 files changed

+73
-50
lines changed

Dotnet.Samples.AspNetCore.WebApi/Program.cs

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Dotnet.Samples.AspNetCore.WebApi.Data;
33
using Dotnet.Samples.AspNetCore.WebApi.Mappings;
44
using Dotnet.Samples.AspNetCore.WebApi.Services;
5+
using Dotnet.Samples.AspNetCore.WebApi.Utilities;
56
using Microsoft.EntityFrameworkCore;
67
using Microsoft.OpenApi.Models;
78
using Serilog;
@@ -20,26 +21,22 @@
2021
* Logging
2122
* -------------------------------------------------------------------------- */
2223
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(builder.Configuration).CreateLogger();
23-
2424
builder.Host.UseSerilog();
2525

2626
/* -----------------------------------------------------------------------------
2727
* Services
2828
* -------------------------------------------------------------------------- */
29-
3029
builder.Services.AddControllers();
31-
32-
var dataSource =
33-
$"{AppDomain.CurrentDomain.SetupInformation.ApplicationBase}/Data/players-sqlite3.db";
34-
3530
builder.Services.AddDbContextPool<PlayerDbContext>(options =>
3631
{
32+
var dataSource = Path.Combine(AppContext.BaseDirectory, "Data", "players-sqlite3.db");
3733
options.UseSqlite($"Data Source={dataSource}");
38-
3934
if (builder.Environment.IsDevelopment())
4035
{
4136
options.EnableSensitiveDataLogging();
42-
options.LogTo(Console.WriteLine, LogLevel.Information);
37+
options.LogTo(Log.Logger.Information, LogLevel.Information);
38+
// https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-9.0/whatsnew#improved-data-seeding
39+
options.UseAsyncSeeding(DbContextUtils.SeedAsync);
4340
}
4441
});
4542

@@ -84,16 +81,16 @@
8481
* Middlewares
8582
* https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware
8683
* -------------------------------------------------------------------------- */
87-
88-
app.UseSerilogRequestLogging();
89-
9084
if (app.Environment.IsDevelopment())
9185
{
9286
// https://learn.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle
9387
app.UseSwagger();
9488
app.UseSwaggerUI();
9589
}
9690

91+
// https://github.com/serilog/serilog-aspnetcore
92+
app.UseSerilogRequestLogging();
93+
9794
// https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl
9895
app.UseHttpsRedirection();
9996

@@ -103,11 +100,4 @@
103100
// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing#endpoints
104101
app.MapControllers();
105102

106-
/* -----------------------------------------------------------------------------
107-
* Data Seeding
108-
* https://learn.microsoft.com/en-us/ef/core/modeling/data-seeding
109-
* -------------------------------------------------------------------------- */
110-
111-
app.SeedDbContext();
112-
113103
await app.RunAsync();
Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
1-
namespace Dotnet.Samples.AspNetCore.WebApi.Data;
1+
using Dotnet.Samples.AspNetCore.WebApi.Data;
2+
using Microsoft.EntityFrameworkCore;
3+
4+
namespace Dotnet.Samples.AspNetCore.WebApi.Utilities;
25

36
public static class ApplicationBuilderExtensions
47
{
58
/// <summary>
6-
/// Simple extension method to populate the database with an initial set of data.
9+
/// Async extension method to populate the database with initial data
710
/// </summary>
8-
public static void SeedDbContext(this IApplicationBuilder app)
11+
public static async Task SeedDbContextAsync(this IApplicationBuilder app)
912
{
1013
using var scope = app.ApplicationServices.CreateScope();
11-
var dbContext = scope.ServiceProvider.GetService<PlayerDbContext>();
14+
var services = scope.ServiceProvider;
15+
var logger = services.GetRequiredService<ILogger<Program>>();
16+
var dbContext = services.GetRequiredService<PlayerDbContext>();
1217

13-
if (dbContext != null)
18+
try
1419
{
15-
// https://learn.microsoft.com/en-us/ef/core/managing-schemas/ensure-created
16-
dbContext.Database.EnsureCreated();
20+
await dbContext.Database.EnsureCreatedAsync();
1721

18-
if (!dbContext.Players.Any())
22+
if (!await dbContext.Players.AnyAsync())
1923
{
20-
dbContext.Players.AddRange(PlayerData.CreateStarting11());
24+
await dbContext.Players.AddRangeAsync(PlayerData.CreateStarting11());
25+
await dbContext.SaveChangesAsync();
26+
logger.LogInformation("Successfully seeded database with initial data.");
2127
}
2228
}
29+
catch (Exception exception)
30+
{
31+
logger.LogError(exception, "An error occurred while seeding the database");
32+
throw new InvalidOperationException(
33+
"An error occurred while seeding the database",
34+
exception
35+
);
36+
}
2337
}
2438
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Dotnet.Samples.AspNetCore.WebApi.Data;
2+
using Microsoft.EntityFrameworkCore;
3+
4+
namespace Dotnet.Samples.AspNetCore.WebApi.Utilities;
5+
6+
public static class DbContextUtils
7+
{
8+
/// <summary>
9+
/// Seeds the database with initial data if empty.
10+
/// </summary>
11+
/// <remarks>
12+
/// This method checks if the database is empty and seeds it with initial data.
13+
/// It is designed to be used with UseAsyncSeeding.
14+
/// </remarks>
15+
/// <param name="context">The database context to seed.</param>
16+
/// <param name="_">Unused parameter, required by the UseAsyncSeeding API.</param>
17+
/// <param name="cancellationToken">A token to cancel the operation if needed.</param>
18+
/// <returns>A task representing the asynchronous seeding operation.</returns>
19+
public static Task SeedAsync(DbContext context, bool _, CancellationToken cancellationToken)
20+
{
21+
if (context is not PlayerDbContext playerDbContext)
22+
{
23+
throw new ArgumentException(
24+
$"Expected context of type {nameof(PlayerDbContext)}, but got {context.GetType().Name}",
25+
nameof(context)
26+
);
27+
}
28+
return SeedPlayersAsync(playerDbContext, cancellationToken);
29+
}
30+
31+
private static async Task SeedPlayersAsync(
32+
PlayerDbContext dbContext,
33+
CancellationToken cancellationToken
34+
)
35+
{
36+
if (!await dbContext.Players.AnyAsync(cancellationToken))
37+
{
38+
await dbContext.Players.AddRangeAsync(PlayerData.CreateStarting11(), cancellationToken);
39+
await dbContext.SaveChangesAsync(cancellationToken);
40+
}
41+
}
42+
}

Dotnet.Samples.AspNetCore.WebApi/Utilities/PlayerDataExtensions.cs

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)