Skip to content

Commit 78ee2c2

Browse files
authored
Hacky exposing of enum type mappings for EFCore.PG (#5278)
1 parent 0b7aecb commit 78ee2c2

File tree

6 files changed

+76
-3
lines changed

6 files changed

+76
-3
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using Npgsql.Internal;
6+
using Npgsql.PostgresTypes;
7+
using NpgsqlTypes;
8+
9+
namespace Npgsql.Internal;
10+
11+
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
12+
13+
/// <summary>
14+
/// Hacky temporary measure used by EFCore.PG to extract user-configured enum mappings. Accessed via reflection only.
15+
/// </summary>
16+
public sealed class HackyEnumTypeMapping
17+
{
18+
public HackyEnumTypeMapping(Type enumClrType, string pgTypeName, INpgsqlNameTranslator nameTranslator)
19+
{
20+
EnumClrType = enumClrType;
21+
PgTypeName = pgTypeName;
22+
NameTranslator = nameTranslator;
23+
}
24+
25+
public string PgTypeName { get; }
26+
public Type EnumClrType { get; }
27+
public INpgsqlNameTranslator NameTranslator { get; }
28+
}

src/Npgsql/NpgsqlDataSource.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Data.Common;
44
using System.Diagnostics;
55
using System.Diagnostics.CodeAnalysis;
6+
using System.Linq;
67
using System.Net.Security;
78
using System.Security.Cryptography.X509Certificates;
89
using System.Threading;
@@ -79,6 +80,8 @@ private protected readonly Dictionary<Transaction, List<NpgsqlConnector>> _pendi
7980

8081
readonly INpgsqlNameTranslator _defaultNameTranslator;
8182

83+
internal List<HackyEnumTypeMapping>? _hackyEnumTypeMappings;
84+
8285
internal NpgsqlDataSource(
8386
NpgsqlConnectionStringBuilder settings,
8487
NpgsqlDataSourceConfiguration dataSourceConfig)
@@ -99,6 +102,7 @@ internal NpgsqlDataSource(
99102
_periodicPasswordSuccessRefreshInterval,
100103
_periodicPasswordFailureRefreshInterval,
101104
var resolverChain,
105+
_hackyEnumTypeMappings,
102106
_defaultNameTranslator,
103107
ConnectionInitializer,
104108
ConnectionInitializerAsync)

src/Npgsql/NpgsqlDataSourceConfiguration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ sealed record NpgsqlDataSourceConfiguration(
1818
TimeSpan PeriodicPasswordSuccessRefreshInterval,
1919
TimeSpan PeriodicPasswordFailureRefreshInterval,
2020
IEnumerable<IPgTypeInfoResolver> ResolverChain,
21+
List<HackyEnumTypeMapping> HackyEnumMappings,
2122
INpgsqlNameTranslator DefaultNameTranslator,
2223
Action<NpgsqlConnection>? ConnectionInitializer,
2324
Func<NpgsqlConnection, Task>? ConnectionInitializerAsync);

src/Npgsql/NpgsqlSlimDataSourceBuilder.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ _loggerFactory is null
517517
_periodicPasswordSuccessRefreshInterval,
518518
_periodicPasswordFailureRefreshInterval,
519519
Resolvers(),
520+
HackyEnumMappings(),
520521
DefaultNameTranslator,
521522
_syncConnectionInitializer,
522523
_asyncConnectionInitializer);
@@ -535,6 +536,21 @@ IEnumerable<IPgTypeInfoResolver> Resolvers()
535536

536537
return resolvers;
537538
}
539+
540+
List<HackyEnumTypeMapping> HackyEnumMappings()
541+
{
542+
var mappings = new List<HackyEnumTypeMapping>();
543+
544+
if (_userTypeMapper.Items.Count > 0)
545+
foreach (var userTypeMapping in _userTypeMapper.Items)
546+
if (userTypeMapping is UserTypeMapper.EnumMapping enumMapping)
547+
mappings.Add(new(enumMapping.ClrType, enumMapping.PgTypeName, enumMapping.NameTranslator));
548+
549+
if (GlobalTypeMapper.Instance.HackyEnumTypeMappings.Count > 0)
550+
mappings.AddRange(GlobalTypeMapper.Instance.HackyEnumTypeMappings);
551+
552+
return mappings;
553+
}
538554
}
539555

540556
void ValidateMultiHost()

src/Npgsql/TypeMapping/GlobalTypeMapper.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Diagnostics.CodeAnalysis;
4+
using System.Linq;
45
using System.Threading;
56
using Npgsql.Internal;
67
using Npgsql.Internal.Postgres;
@@ -15,6 +16,8 @@ sealed class GlobalTypeMapper : INpgsqlTypeMapper
1516
readonly ReaderWriterLockSlim _lock = new();
1617
IPgTypeInfoResolver[] _typeMappingResolvers = Array.Empty<IPgTypeInfoResolver>();
1718

19+
internal List<HackyEnumTypeMapping> HackyEnumTypeMappings { get; } = new();
20+
1821
internal IEnumerable<IPgTypeInfoResolver> GetPluginResolvers()
1922
{
2023
var resolvers = new List<IPgTypeInfoResolver>();
@@ -154,6 +157,7 @@ public void Reset()
154157
{
155158
_pluginResolvers.Clear();
156159
_userTypeMapper.Items.Clear();
160+
HackyEnumTypeMappings.Clear();
157161
}
158162
finally
159163
{
@@ -175,6 +179,11 @@ public INpgsqlTypeMapper MapEnum<TEnum>(string? pgName = null, INpgsqlNameTransl
175179
try
176180
{
177181
_userTypeMapper.MapEnum<TEnum>(pgName, nameTranslator);
182+
183+
// Temporary hack for EFCore.PG enum mapping compat
184+
if (_userTypeMapper.Items.FirstOrDefault(i => i.ClrType == typeof(TEnum)) is UserTypeMapping userTypeMapping)
185+
HackyEnumTypeMappings.Add(new(typeof(TEnum), userTypeMapping.PgTypeName, nameTranslator ?? DefaultNameTranslator));
186+
178187
return this;
179188
}
180189
finally
@@ -189,7 +198,13 @@ public bool UnmapEnum<TEnum>(string? pgName = null, INpgsqlNameTranslator? nameT
189198
_lock.EnterWriteLock();
190199
try
191200
{
192-
return _userTypeMapper.UnmapEnum<TEnum>(pgName, nameTranslator);
201+
var removed = _userTypeMapper.UnmapEnum<TEnum>(pgName, nameTranslator);
202+
203+
// Temporary hack for EFCore.PG enum mapping compat
204+
if (removed && ((List<UserTypeMapping>)_userTypeMapper.Items).FindIndex(m => m.ClrType == typeof(TEnum)) is > -1 and var index)
205+
HackyEnumTypeMappings.RemoveAt(index);
206+
207+
return removed;
193208
}
194209
finally
195210
{

src/Npgsql/TypeMapping/UserTypeMapper.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,14 +181,23 @@ internal override void Build(TypeInfoMappingCollection mappings)
181181
}
182182
}
183183

184-
sealed class EnumMapping<TEnum> : UserTypeMapping
184+
internal abstract class EnumMapping : UserTypeMapping
185+
{
186+
internal INpgsqlNameTranslator NameTranslator { get; }
187+
188+
public EnumMapping(string pgTypeName, Type enumClrType, INpgsqlNameTranslator nameTranslator)
189+
: base(pgTypeName, enumClrType)
190+
=> NameTranslator = nameTranslator;
191+
}
192+
193+
sealed class EnumMapping<TEnum> : EnumMapping
185194
where TEnum : struct, Enum
186195
{
187196
readonly Dictionary<TEnum, string> _enumToLabel = new();
188197
readonly Dictionary<string, TEnum> _labelToEnum = new();
189198

190199
public EnumMapping(string pgTypeName, INpgsqlNameTranslator nameTranslator)
191-
: base(pgTypeName, typeof(TEnum))
200+
: base(pgTypeName, typeof(TEnum), nameTranslator)
192201
{
193202
foreach (var field in typeof(TEnum).GetFields(BindingFlags.Static | BindingFlags.Public))
194203
{

0 commit comments

Comments
 (0)