diff --git a/src/EFCore.PG/Storage/Internal/Mapping/NpgsqlEnumTypeMapping.cs b/src/EFCore.PG/Storage/Internal/Mapping/NpgsqlEnumTypeMapping.cs index dcf7902e8..e9faf4f6c 100644 --- a/src/EFCore.PG/Storage/Internal/Mapping/NpgsqlEnumTypeMapping.cs +++ b/src/EFCore.PG/Storage/Internal/Mapping/NpgsqlEnumTypeMapping.cs @@ -10,27 +10,21 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping { public class NpgsqlEnumTypeMapping : RelationalTypeMapping { - [NotNull] static readonly NpgsqlSqlGenerationHelper SqlGenerationHelper = - new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()); - + [NotNull] readonly ISqlGenerationHelper _sqlGenerationHelper; [NotNull] readonly INpgsqlNameTranslator _nameTranslator; - [CanBeNull] readonly string _storeTypeSchema; - /// /// Translates the CLR member value to the PostgreSQL value label. /// [NotNull] readonly Dictionary _members; - [NotNull] - public override string StoreType => SqlGenerationHelper.DelimitIdentifier(base.StoreType, _storeTypeSchema); - public NpgsqlEnumTypeMapping( [NotNull] string storeType, [CanBeNull] string storeTypeSchema, [NotNull] Type enumType, + [NotNull] ISqlGenerationHelper sqlGenerationHelper, [CanBeNull] INpgsqlNameTranslator nameTranslator = null) - : base(storeType, enumType) + : base(sqlGenerationHelper.DelimitIdentifier(storeType, storeTypeSchema), enumType) { if (!enumType.IsEnum || !enumType.IsValueType) throw new ArgumentException($"Enum type mappings require a CLR enum. {enumType.FullName} is not an enum."); @@ -39,23 +33,23 @@ public NpgsqlEnumTypeMapping( nameTranslator = NpgsqlConnection.GlobalTypeMapper.DefaultNameTranslator; _nameTranslator = nameTranslator; - _storeTypeSchema = storeTypeSchema; + _sqlGenerationHelper = sqlGenerationHelper; _members = CreateValueMapping(enumType, nameTranslator); } protected NpgsqlEnumTypeMapping( RelationalTypeMappingParameters parameters, - [CanBeNull] string storeTypeSchema, + [NotNull] ISqlGenerationHelper sqlGenerationHelper, [NotNull] INpgsqlNameTranslator nameTranslator) : base(parameters) { _nameTranslator = nameTranslator; - _storeTypeSchema = storeTypeSchema; + _sqlGenerationHelper = sqlGenerationHelper; _members = CreateValueMapping(parameters.CoreParameters.ClrType, nameTranslator); } protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) - => new NpgsqlEnumTypeMapping(parameters, _storeTypeSchema, _nameTranslator); + => new NpgsqlEnumTypeMapping(parameters, _sqlGenerationHelper, _nameTranslator); protected override string GenerateNonNullSqlLiteral(object value) => $"'{_members[value]}'::{StoreType}"; diff --git a/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs b/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs index dd713f72f..f1ac717ea 100644 --- a/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs +++ b/src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs @@ -244,7 +244,7 @@ public NpgsqlTypeMappingSource([NotNull] TypeMappingSourceDependencies dependenc StoreTypeMappings = new ConcurrentDictionary(storeTypeMappings, StringComparer.OrdinalIgnoreCase); ClrTypeMappings = new ConcurrentDictionary(clrTypeMappings); - LoadUserDefinedTypeMappings(); + LoadUserDefinedTypeMappings(sqlGenerationHelper); _userRangeDefinitions = npgsqlOptions?.UserRangeDefinitions ?? new UserRangeDefinition[0]; } @@ -253,15 +253,15 @@ public NpgsqlTypeMappingSource([NotNull] TypeMappingSourceDependencies dependenc /// To be used in case user-defined mappings are added late, after this TypeMappingSource has already been initialized. /// This is basically only for test usage. /// - public void LoadUserDefinedTypeMappings() + public void LoadUserDefinedTypeMappings([NotNull] ISqlGenerationHelper sqlGenerationHelper) { - SetupEnumMappings(); + SetupEnumMappings(sqlGenerationHelper); } /// /// Gets all global enum mappings from the ADO.NET layer and creates mappings for them /// - void SetupEnumMappings() + void SetupEnumMappings([NotNull] ISqlGenerationHelper sqlGenerationHelper) { foreach (var adoMapping in NpgsqlConnection.GlobalTypeMapper.Mappings.Where(m => m.TypeHandlerFactory is IEnumTypeHandlerFactory)) { @@ -280,7 +280,7 @@ void SetupEnumMappings() var schema = components.Length > 1 ? components.First() : null; var name = components.Length > 1 ? string.Join(null, components.Skip(1)) : storeType; - var mapping = new NpgsqlEnumTypeMapping(name, schema, clrType, nameTranslator); + var mapping = new NpgsqlEnumTypeMapping(name, schema, clrType, sqlGenerationHelper, nameTranslator); ClrTypeMappings[clrType] = mapping; StoreTypeMappings[mapping.StoreType] = new RelationalTypeMapping[] { mapping }; } diff --git a/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlTest.cs index 159ab0e08..d554d3071 100644 --- a/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BuiltInDataTypesNpgsqlTest.cs @@ -707,7 +707,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con base.OnModelCreating(modelBuilder, context); NpgsqlConnection.GlobalTypeMapper.MapEnum(); - ((NpgsqlTypeMappingSource)context.GetService()).LoadUserDefinedTypeMappings(); + ((NpgsqlTypeMappingSource)context.GetService()).LoadUserDefinedTypeMappings(context.GetService()); modelBuilder.HasPostgresExtension("hstore"); modelBuilder.ForNpgsqlHasEnum("mood", new[] { "happy", "sad" }); diff --git a/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingTest.cs b/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingTest.cs index dfafd779b..29b31680e 100644 --- a/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingTest.cs +++ b/test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingTest.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Collections.Immutable; using System.Net; using System.Net.NetworkInformation; using Microsoft.EntityFrameworkCore; @@ -88,7 +87,7 @@ public void GenerateSqlLiteral_returns_timetz_literal() var mapping = GetMapping("timetz"); Assert.Equal("TIMETZ '04:05:06.123456+3'", mapping.GenerateSqlLiteral(new DateTimeOffset(2015, 3, 12, 4, 5, 6, 123, TimeSpan.FromHours(3)) - .AddTicks(4560))); + .AddTicks(4560))); Assert.Equal("TIMETZ '04:05:06.789+3'", mapping.GenerateSqlLiteral(new DateTimeOffset(2015, 3, 12, 4, 5, 6, 789, TimeSpan.FromHours(3)))); Assert.Equal("TIMETZ '04:05:06-3'", mapping.GenerateSqlLiteral(new DateTimeOffset(2015, 3, 12, 4, 5, 6, TimeSpan.FromHours(-3)))); } @@ -280,8 +279,8 @@ public void ValueComparer_hstore_array() // This exercises array's comparer when the element has its own non-null comparer var source = new[] { - new Dictionary { { "k1", "v1"} }, - new Dictionary { { "k2", "v2"} }, + new Dictionary { { "k1", "v1" } }, + new Dictionary { { "k2", "v2" } }, }; var comparer = GetMapping(typeof(Dictionary[])).Comparer; @@ -300,18 +299,18 @@ public void GenerateSqlLiteral_returns_bytea_literal() public void GenerateSqlLiteral_returns_hstore_literal() => Assert.Equal(@"HSTORE '""k1""=>""v1"",""k2""=>""v2""'", GetMapping("hstore").GenerateSqlLiteral(new Dictionary - { - { "k1", "v1" }, - { "k2", "v2" } - })); + { + { "k1", "v1" }, + { "k2", "v2" } + })); [Fact] public void ValueComparer_hstore() { var source = new Dictionary { - { "k1", "v1"}, - { "k2", "v2"} + { "k1", "v1" }, + { "k2", "v2" } }; var comparer = GetMapping("hstore").Comparer; @@ -333,18 +332,34 @@ public void GenerateSqlLiteral_returns_json_literal() [Fact] public void GenerateSqlLiteral_returns_enum_literal() { - var mapping = new NpgsqlEnumTypeMapping("dummy_enum", null, typeof(DummyEnum), new NpgsqlSnakeCaseNameTranslator()); + var mapping = new NpgsqlEnumTypeMapping( + "dummy_enum", + null, + typeof(DummyEnum), + new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()), + new NpgsqlSnakeCaseNameTranslator()); + Assert.Equal("'sad'::dummy_enum", mapping.GenerateSqlLiteral(DummyEnum.Sad)); } [Fact] public void GenerateSqlLiteral_returns_enum_uppercase_literal() { - var mapping = new NpgsqlEnumTypeMapping("DummyEnum", null, typeof(DummyEnum), new NpgsqlSnakeCaseNameTranslator()); + var mapping = new NpgsqlEnumTypeMapping( + "DummyEnum", + null, + typeof(DummyEnum), + new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()), + new NpgsqlSnakeCaseNameTranslator()); + Assert.Equal("'sad'::\"DummyEnum\"", mapping.GenerateSqlLiteral(DummyEnum.Sad)); } - enum DummyEnum { Happy, Sad }; + enum DummyEnum + { + Happy, + Sad + }; [Fact] public void GenerateSqlLiteral_returns_tid_literal()