diff --git a/src/EFCore.PG/Design/Internal/NpgsqlCSharpRuntimeAnnotationCodeGenerator.cs b/src/EFCore.PG/Design/Internal/NpgsqlCSharpRuntimeAnnotationCodeGenerator.cs index e484cb56e7..d9efdfb2b9 100644 --- a/src/EFCore.PG/Design/Internal/NpgsqlCSharpRuntimeAnnotationCodeGenerator.cs +++ b/src/EFCore.PG/Design/Internal/NpgsqlCSharpRuntimeAnnotationCodeGenerator.cs @@ -205,6 +205,7 @@ public override void Generate(IModel model, CSharpRuntimeAnnotationCodeGenerator annotations.Remove(NpgsqlAnnotationNames.DatabaseTemplate); annotations.Remove(NpgsqlAnnotationNames.Tablespace); annotations.Remove(NpgsqlAnnotationNames.CollationDefinitionPrefix); + annotations.Remove(NpgsqlAnnotationNames.Encoding); #pragma warning disable CS0618 annotations.Remove(NpgsqlAnnotationNames.DefaultColumnCollation); @@ -232,6 +233,7 @@ public override void Generate(IRelationalModel model, CSharpRuntimeAnnotationCod annotations.Remove(NpgsqlAnnotationNames.DatabaseTemplate); annotations.Remove(NpgsqlAnnotationNames.Tablespace); annotations.Remove(NpgsqlAnnotationNames.CollationDefinitionPrefix); + annotations.Remove(NpgsqlAnnotationNames.Encoding); #pragma warning disable CS0618 annotations.Remove(NpgsqlAnnotationNames.DefaultColumnCollation); diff --git a/src/EFCore.PG/Extensions/BuilderExtensions/NpgsqlModelBuilderExtensions.cs b/src/EFCore.PG/Extensions/BuilderExtensions/NpgsqlModelBuilderExtensions.cs index 6650eb824e..5fd0fc2954 100644 --- a/src/EFCore.PG/Extensions/BuilderExtensions/NpgsqlModelBuilderExtensions.cs +++ b/src/EFCore.PG/Extensions/BuilderExtensions/NpgsqlModelBuilderExtensions.cs @@ -653,6 +653,20 @@ public static ModelBuilder UseTablespace( #endregion + #region Encoding + /// + /// Specifies the encoding to use when creating a new database for this model. + /// + public static ModelBuilder UseDatabaseEncoding(this ModelBuilder modelBuilder, string encoding) + { + Check.NotNull(modelBuilder, nameof(modelBuilder)); + Check.NotEmpty(encoding, nameof(encoding)); + + modelBuilder.Model.SetDatabaseEncoding(encoding); + return modelBuilder; + } + #endregion + #region Collation management /// diff --git a/src/EFCore.PG/Extensions/MetadataExtensions/NpgsqlModelExtensions.cs b/src/EFCore.PG/Extensions/MetadataExtensions/NpgsqlModelExtensions.cs index a69ccf8ac7..0699d1e052 100644 --- a/src/EFCore.PG/Extensions/MetadataExtensions/NpgsqlModelExtensions.cs +++ b/src/EFCore.PG/Extensions/MetadataExtensions/NpgsqlModelExtensions.cs @@ -516,4 +516,24 @@ public static IReadOnlyList GetCollations(this IReadOnlyModel => PostgresCollation.GetCollations(model).ToArray(); #endregion Collation management + + #region Encoding + /// + /// 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. + /// + public static string? GetEncoding(this IReadOnlyModel model) + => (string?)model[NpgsqlAnnotationNames.Encoding]; + + /// + /// 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. + /// + public static void SetDatabaseEncoding(this IMutableModel model, string? encoding) + => model.SetOrRemoveAnnotation(NpgsqlAnnotationNames.Encoding, encoding); + #endregion } diff --git a/src/EFCore.PG/Metadata/Conventions/NpgsqlRuntimeModelConvention.cs b/src/EFCore.PG/Metadata/Conventions/NpgsqlRuntimeModelConvention.cs index 341fd802b7..ae06acdf4a 100644 --- a/src/EFCore.PG/Metadata/Conventions/NpgsqlRuntimeModelConvention.cs +++ b/src/EFCore.PG/Metadata/Conventions/NpgsqlRuntimeModelConvention.cs @@ -33,6 +33,7 @@ protected override void ProcessModelAnnotations( annotations.Remove(NpgsqlAnnotationNames.DatabaseTemplate); annotations.Remove(NpgsqlAnnotationNames.Tablespace); annotations.Remove(NpgsqlAnnotationNames.CollationDefinitionPrefix); + annotations.Remove(NpgsqlAnnotationNames.Encoding); #pragma warning disable CS0618 annotations.Remove(NpgsqlAnnotationNames.DefaultColumnCollation); diff --git a/src/EFCore.PG/Metadata/Internal/NpgsqlAnnotationNames.cs b/src/EFCore.PG/Metadata/Internal/NpgsqlAnnotationNames.cs index 0f2dd325ef..8d9db80783 100644 --- a/src/EFCore.PG/Metadata/Internal/NpgsqlAnnotationNames.cs +++ b/src/EFCore.PG/Metadata/Internal/NpgsqlAnnotationNames.cs @@ -40,6 +40,14 @@ public static class NpgsqlAnnotationNames /// public const string DatabaseTemplate = Prefix + "DatabaseTemplate"; + /// + /// 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. + /// + public const string Encoding = Prefix + "Encoding"; + /// /// 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 diff --git a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs index fccab7d188..fc249a6689 100644 --- a/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs +++ b/src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs @@ -1044,6 +1044,14 @@ protected virtual void Generate(NpgsqlCreateDatabaseOperation operation, IModel? .Append(DelimitIdentifier(operation.Collation)); } + if (!string.IsNullOrEmpty(operation.Encoding)) + { + builder + .AppendLine() + .Append("ENCODING ") + .Append(DelimitIdentifier(operation.Encoding)); + } + builder.AppendLine(";"); EndStatement(builder, suppressTransaction: true); diff --git a/src/EFCore.PG/Migrations/Operations/NpgsqlCreateDatabaseOperation.cs b/src/EFCore.PG/Migrations/Operations/NpgsqlCreateDatabaseOperation.cs index 88d92e5253..28f15baca6 100644 --- a/src/EFCore.PG/Migrations/Operations/NpgsqlCreateDatabaseOperation.cs +++ b/src/EFCore.PG/Migrations/Operations/NpgsqlCreateDatabaseOperation.cs @@ -23,4 +23,9 @@ public class NpgsqlCreateDatabaseOperation : DatabaseOperation /// The PostgreSQL tablespace in which to create the database. /// public virtual string? Tablespace { get; set; } + + /// + /// The PostgreSQL encoding to use for the database. + /// + public virtual string? Encoding { get; set; } } diff --git a/src/EFCore.PG/Storage/Internal/NpgsqlDatabaseCreator.cs b/src/EFCore.PG/Storage/Internal/NpgsqlDatabaseCreator.cs index 4034f5ef2d..655ebfa883 100644 --- a/src/EFCore.PG/Storage/Internal/NpgsqlDatabaseCreator.cs +++ b/src/EFCore.PG/Storage/Internal/NpgsqlDatabaseCreator.cs @@ -162,7 +162,8 @@ private IReadOnlyList CreateCreateOperations() Name = connection.DbConnection.Database, Template = designTimeModel.GetDatabaseTemplate(), Collation = designTimeModel.GetCollation(), - Tablespace = designTimeModel.GetTablespace() + Tablespace = designTimeModel.GetTablespace(), + Encoding = designTimeModel.GetEncoding(), } ]); } diff --git a/test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs b/test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs index 2cf1b21d9b..66b196e3ec 100644 --- a/test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs +++ b/test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs @@ -65,6 +65,19 @@ public virtual void CreateDatabaseOperation_with_tablespace() CREATE DATABASE some_db TABLESPACE "MyTablespace"; +"""); + } + + [Fact] + public virtual void CreateDatabaseOperation_with_encoding() + { + Generate(new NpgsqlCreateDatabaseOperation { Name = "Northwind", Encoding = "UTF8" }); + + AssertSql( + """ +CREATE DATABASE "Northwind" +ENCODING "UTF8"; + """); }