Seeding database during migration fails when using newly added Enum
I have a migration in EF Core 9 (package: Npgsql.EntityFrameworkCore.PostgreSQL 9.0.4) adding new enum type:
migrationBuilder.AlterDatabase()
.Annotation("Npgsql:Enum:event_stack_event_status", "closed,new,taken,unknown")
The enum is registered like so:
services.AddDbContext<DatabaseContext>(options =>
{
options.UseNpgsql(
CreateConnString(variables.DatabaseName),
npsqlOptions =>
{
npsqlOptions.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery);
npsqlOptions.MapEnum<EventStackEventStatus>();
});
options.UseDataSeeding();
options.ConfigureWarnings(c => c.Log((RelationalEventId.CommandExecuted, LogLevel.Debug)));
}, ServiceLifetime.Transient);
The UseDataSeeding is my extension simply adding seeder implementation:
public static void UseDataSeeding(this DbContextOptionsBuilder options)
{
options.UseSeeding(
(ctx, storeManagementPerformed) => SeedAsync(ctx, storeManagementPerformed).Wait());
options.UseAsyncSeeding(SeedAsync);
}
Migration is applied via migration bundle. During the seed process, where the enum is used, I'm getting error:
System.NotSupportedException: The data type name 'event_stack_event_status' isn't present in your database. You may need to install an extension or upgrade to a newer version.
As far as I know this is an issue with type cache which doesn't contain new enum mapping. I tried to call ReloadTypes in data seeder on current database connection from database context but it changed error to:
System.InvalidCastException: Writing values of 'EventStackEventStatus' is not supported for parameters having DataTypeName 'public.event_stack_event_status'
I'm aware of this issue: #292 and PR that solving it #2951, but it's not applying here because seeder is called during Migrate method before ReloadTypes is called: https://github.com/dotnet/efcore/blob/main/src/EFCore.Relational/Migrations/Internal/Migrator.cs#L192
The only workaround I see right now is to move data seeding to application start. Is there a way to solve it so I can still use data seeding during migration?
FWIW, I was able to get a successful migration with custom data type enums (on 9.0.3) by doing:
public class MigrationDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
{
public MyDbContext CreateDbContext(string[] args)
{
// Build configuration
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false)
.AddJsonFile("appsettings.Development.json", true)
.AddEnvironmentVariables()
.Build();
var connectionString = configuration.GetConnectionString("MigrationConnection");
optionsBuilder.UseNpgsql(connectionString, options => {
npgsqlOptions.MapEnum<UserRole>("user_role");
npgsqlOptions.MapEnum<AuthTypes>("auth_types");
})
.UseSeeding((dbContext, _) =>
{
SeedTestDataAsync(dbContext).GetAwaiter().GetResult();
})
.UseAsyncSeeding(async (dbContext, _, cancellationToken) =>
{
await SeedTestDataAsync(dbContext, cancellationToken);
});
}
private async Task SeedTestDataAsync(DbContext dbContext, CancellationToken cancellationToken = default)
{
var conn = dbContext.Database.GetDbConnection();
if (conn is NpgsqlConnection npgsqlConn)
{
try
{
await npgsqlConn.ReloadTypesAsync(cancellationToken);
}
catch
{
Console.WriteLine(
"Failed to reload Postgres types. This will likely cause issues with seeding test data.");
}
}
// seed rows here...
}
}
Did the seeding adds these enums? I've done exactly that and have got InvalidCastException as mentioned in my post. I don't use design time factory, but does it make any difference? How do you run migrations?
The migration did add the enums - they are present in the migration as
migrationBuilder.AlterDatabase()
.Annotation("Npgsql:Enum:auth_types", "api_key,basic_auth,o_auth")
.Annotation("Npgsql:Enum:user_role", "admin,user");
then later on are used in the seed data rows.
I am using dotnet ef migrations bundle to generate a bundler for the actual deployments (that gets run in CI), but it is also working locally for me in aspire / command line with dotnet ef database update
I should provide more debugging information but I don't know what. At least for now. I will try to do it once again.