You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Please provide non-generic methods for creating and mapping enums. #3383
This would allow to loop through properties of CLR types that represent DB entities and create+map all enums automatically, like so:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (Type enumType in
modelBuilder.Model.GetEntityTypes()
.SelectMany(et => et.ClrType.GetProperties().Select(pi => pi.PropertyType).Where(t => t.IsEnum)))
{
//create db type from enum type
modelBuilder.HasPostgresEnum(enumType);
//map enum type to db type
NpgsqlConnection.GlobalTypeMapper.MapEnum(enumType);
}
}
I have achieved the above using reflection, which looks pretty ugly:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
MethodInfo hasPostgresEnumMethodInfo = typeof(NpgsqlModelBuilderExtensions)
.GetMethods()
.Single(mi => mi.Name == nameof(NpgsqlModelBuilderExtensions.HasPostgresEnum) && mi.IsGenericMethod);
INpgsqlTypeMapper typeMapper = NpgsqlConnection.GlobalTypeMapper;
MethodInfo mapEnumMethodInfo = typeMapper.GetType().GetMethod(nameof(typeMapper.MapEnum));
foreach (Type enumType in
modelBuilder.Model.GetEntityTypes()
.SelectMany(et => et.ClrType.GetProperties().Select(pi => pi.PropertyType).Where(t => t.IsEnum)))
{
//create db type from enum type
hasPostgresEnumMethodInfo.MakeGenericMethod(enumType).Invoke(null, new object[] {modelBuilder, null, null, null});
//map enum type to db type
mapEnumMethodInfo.MakeGenericMethod(enumType).Invoke(typeMapper, new object[] {null, null});
}
}
and got stuck on the line that creates new EnumTypeHandlerFactory<TEnum>, because could not quickly find a way to create
non-generic TypeHandlerFactory.
On a different note, as part of this change it would be really handy to also add an extension method .AddEnumsFromClrTypes to the ModelBuilder. Implementation is just the loop in the original question.
and got stuck on the line that creates new EnumTypeHandlerFactory
Ah yes, you're right - the type handler system is (at least currently) generic. It should be OK to use reflection to make a generic type for EnumTypeHandlerFactory, much as you're doing outside of Npgsql. At some point in the future we may change the type handler system to not require this any more.
On a different note, as part of this change it would be really handy to also add an extension method .AddEnumsFromClrTypes to the ModelBuilder. Implementation is just the loop in the original question.
Would that do anything more than loop and call MapEnum? If so, then it doesn't seem like something that should be inside the provider.
Would that do anything more than loop and call MapEnum?
That would call two things in the loop: modelBuilder.HasPostgresEnum(enumType) and NpgsqlConnection.GlobalTypeMapper.MapEnum(enumType).
If so, then it doesn't seem like something that should be inside the provider.
Yeah, not sure... Definitely not in this repo. Maybe could be a class ModelBuilderExtensions in https://github.com/npgsql/efcore.pg. Ability to do the below in your derived DbContext is too tempting :) :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ConfigureEnumsFromEntities(); //creates and maps all enums found in entity CLR types
...the rest of OnModelCreating code
}
Activity
roji commentedon Nov 29, 2020
Makes sense - we have non-generic mapping methods for composite, but not for enum. It should be pretty easy to do if you want to submit a PR.
andrewslavin commentedon Nov 29, 2020
@roji Thank you for prompt response.
I actually started doing that. Looked at this method:
npgsql/src/Npgsql/TypeMapping/TypeMapperBase.cs
Line 45 in d42cc68
and got stuck on the line that creates
new EnumTypeHandlerFactory<TEnum>, because could not quickly find a way to createnon-generic
TypeHandlerFactory.On a different note, as part of this change it would be really handy to also add an extension method
.AddEnumsFromClrTypesto theModelBuilder. Implementation is just the loop in the original question.roji commentedon Nov 29, 2020
Ah yes, you're right - the type handler system is (at least currently) generic. It should be OK to use reflection to make a generic type for EnumTypeHandlerFactory, much as you're doing outside of Npgsql. At some point in the future we may change the type handler system to not require this any more.
Would that do anything more than loop and call MapEnum? If so, then it doesn't seem like something that should be inside the provider.
andrewslavin commentedon Nov 29, 2020
That would call two things in the loop:
modelBuilder.HasPostgresEnum(enumType)andNpgsqlConnection.GlobalTypeMapper.MapEnum(enumType).Yeah, not sure... Definitely not in this repo. Maybe could be a class
ModelBuilderExtensionsin https://github.com/npgsql/efcore.pg. Ability to do the below in your derived DbContext is too tempting :) :roji commentedon Nov 29, 2020
OK. Let's start with non-generic enum mapping, I want to take another look at how we handle enum mappings in EFCore.PG in any case.
BogdanYarotsky commentedon Sep 3, 2022
Hi! @roji, is this issue still relevant and a "good first issue"? I'm looking for a ticket to make my first contribution to npgsql.
roji commentedon Sep 3, 2022
@BogdanYarotsky sure, I guess so - you should be able to do the same thing that the non-generic MapComposite does.
BogdanYarotsky commentedon Sep 3, 2022
@roji Thanks for the quick reply! I will start tomorrow
Support for non-generic Enum mapping (#4852)