Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 7 additions & 13 deletions src/EFCore.PG/Storage/Internal/Mapping/NpgsqlEnumTypeMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
/// Translates the CLR member value to the PostgreSQL value label.
/// </summary>
[NotNull] readonly Dictionary<object, string> _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.");
Expand All @@ -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}";

Expand Down
10 changes: 5 additions & 5 deletions src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public NpgsqlTypeMappingSource([NotNull] TypeMappingSourceDependencies dependenc
StoreTypeMappings = new ConcurrentDictionary<string, RelationalTypeMapping[]>(storeTypeMappings, StringComparer.OrdinalIgnoreCase);
ClrTypeMappings = new ConcurrentDictionary<Type, RelationalTypeMapping>(clrTypeMappings);

LoadUserDefinedTypeMappings();
LoadUserDefinedTypeMappings(sqlGenerationHelper);

_userRangeDefinitions = npgsqlOptions?.UserRangeDefinitions ?? new UserRangeDefinition[0];
}
Expand All @@ -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.
/// </summary>
public void LoadUserDefinedTypeMappings()
public void LoadUserDefinedTypeMappings([NotNull] ISqlGenerationHelper sqlGenerationHelper)
{
SetupEnumMappings();
SetupEnumMappings(sqlGenerationHelper);
}

/// <summary>
/// Gets all global enum mappings from the ADO.NET layer and creates mappings for them
/// </summary>
void SetupEnumMappings()
void SetupEnumMappings([NotNull] ISqlGenerationHelper sqlGenerationHelper)
{
foreach (var adoMapping in NpgsqlConnection.GlobalTypeMapper.Mappings.Where(m => m.TypeHandlerFactory is IEnumTypeHandlerFactory))
{
Expand All @@ -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 };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
base.OnModelCreating(modelBuilder, context);

NpgsqlConnection.GlobalTypeMapper.MapEnum<Mood>();
((NpgsqlTypeMappingSource)context.GetService<ITypeMappingSource>()).LoadUserDefinedTypeMappings();
((NpgsqlTypeMappingSource)context.GetService<ITypeMappingSource>()).LoadUserDefinedTypeMappings(context.GetService<ISqlGenerationHelper>());

modelBuilder.HasPostgresExtension("hstore");
modelBuilder.ForNpgsqlHasEnum("mood", new[] { "happy", "sad" });
Expand Down
41 changes: 28 additions & 13 deletions test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingTest.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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))));
}
Expand Down Expand Up @@ -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<string, string> { { "k1", "v1"} },
new Dictionary<string, string> { { "k2", "v2"} },
new Dictionary<string, string> { { "k1", "v1" } },
new Dictionary<string, string> { { "k2", "v2" } },
};

var comparer = GetMapping(typeof(Dictionary<string, string>[])).Comparer;
Expand All @@ -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<string, string>
{
{ "k1", "v1" },
{ "k2", "v2" }
}));
{
{ "k1", "v1" },
{ "k2", "v2" }
}));

[Fact]
public void ValueComparer_hstore()
{
var source = new Dictionary<string, string>
{
{ "k1", "v1"},
{ "k2", "v2"}
{ "k1", "v1" },
{ "k2", "v2" }
};

var comparer = GetMapping("hstore").Comparer;
Expand All @@ -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()
Expand Down