From 194cad4de0f512a38168f6ac8783bd2e63733771 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Sun, 27 Oct 2024 20:41:38 +0100 Subject: [PATCH] Remove enum/extension conventions when creating migrations history table Fixes #3324 --- .../Internal/NpgsqlHistoryRepository.cs | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/EFCore.PG/Migrations/Internal/NpgsqlHistoryRepository.cs b/src/EFCore.PG/Migrations/Internal/NpgsqlHistoryRepository.cs index 3c8513ec3..1366a1b6e 100644 --- a/src/EFCore.PG/Migrations/Internal/NpgsqlHistoryRepository.cs +++ b/src/EFCore.PG/Migrations/Internal/NpgsqlHistoryRepository.cs @@ -1,4 +1,6 @@ -namespace Npgsql.EntityFrameworkCore.PostgreSQL.Migrations.Internal; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Conventions; + +namespace Npgsql.EntityFrameworkCore.PostgreSQL.Migrations.Internal; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -8,6 +10,8 @@ /// public class NpgsqlHistoryRepository : HistoryRepository, IHistoryRepository { + private IModel? _model; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -87,6 +91,50 @@ protected override string ExistsSql protected override bool InterpretExistsResult(object? value) => (bool?)value == true; + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + protected override IReadOnlyList GetCreateCommands() + { + // TODO: This is all a hack around https://github.com/dotnet/efcore/issues/34991: we have provider-specific conventions which add + // enums and extensions to the model, and the default EF logic causes them to be created at this point, when the history table is + // being created. + var model = EnsureModel(); + + var operations = Dependencies.ModelDiffer.GetDifferences(null, model.GetRelationalModel()); + var commandList = Dependencies.MigrationsSqlGenerator.Generate(operations, model); + return commandList; + } + + private IModel EnsureModel() + { + if (_model == null) + { + var conventionSet = Dependencies.ConventionSetBuilder.CreateConventionSet(); + + conventionSet.Remove(typeof(DbSetFindingConvention)); + conventionSet.Remove(typeof(RelationalDbFunctionAttributeConvention)); + // TODO: this whole method exists only so we can remove this convention (https://github.com/dotnet/efcore/issues/34991) + conventionSet.Remove(typeof(NpgsqlPostgresModelFinalizingConvention)); + + var modelBuilder = new ModelBuilder(conventionSet); + modelBuilder.Entity( + x => + { + ConfigureTable(x); + x.ToTable(TableName, TableSchema); + }); + + _model = Dependencies.ModelRuntimeInitializer.Initialize( + (IModel)modelBuilder.Model, designTime: true, validationLogger: null); + } + + return _model; + } + bool IHistoryRepository.CreateIfNotExists() { // In PG, doing CREATE TABLE IF NOT EXISTS isn't concurrency-safe, and can result a "duplicate table" error or in a unique