-
Notifications
You must be signed in to change notification settings - Fork 236
Unable to save entities with enum with a special string converter #3472
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
services.AddDbContextFactory<MyContext>(options =>
{
options.UseNpgsql(connectionString, o => o.MapEnum<MyEnum>("my_enum");
options.UseSnakeCaseNamingConvention();
}); The above should work, and has been tested successfully in various scenarios; can you please make sure that you're always doing that (for example, when running design-time tools, you may need have different DbContext configuration than in regular operations). Also, the code you posted above is missing a closing parentheses, so it may not be the actual code you're using. If you can't find any issue, please try putting together a minimal, runnable repro; this will likely work, and the difference between that and your real project should help you understand where the problem is in your code. Otherwise, if you get to a minimal repro which shows the problem, you can post it here and I'll investigate. |
Hi @roji, thanks for the quick reply. Sorry I copied parts of the code to simplify it, sorry for the missing brace. I am using database first approach and scaffolded the code years ago, however due to an oversight of me the write to database part was never used, instead the API of a different application that talks with the database was used to create entities. The database belongs to this other application which is using NodeJS. The column is an actual Postges Enum. I tested scaffolding again with dotnet-ef 9.0.2 and the column is just missing after that like the enum docs state. When debugging I can see the entity instance has the enum correctly assigned to the property and the logs show it tries to insert the correct value the converter is giving: The full relevant code is public enum MyEnum
{
[Description("VALUE_1")]
Value1,
[Description("VALUE_2")]
Value2,
}
public partial class MyEntity
{
// ...
[Column("someThing", TypeName = "enum")]
public MyEnum SomeThing { get; set; }
}
public static class EnumDescriptionExtensions
{
public static string? GetEnumDescription(this Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null && Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute descriptionAttribute)
{
return descriptionAttribute.Description;
}
}
return null;
}
}
public partial class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext(> options)
: base(options)
{}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.HasPostgresEnum<MyEnum>(name: "my_entity_my_enum", new[] { "VALUE_1", "VALUE_2" })
.HasPostgresExtension("uuid-ossp");
ValueConverter<MyEnum, string> myEnumTypeConverter = new(
v => v.GetEnumDescription(),
v => GetEnumFromDescriptionString<MyEnum>(v));
modelBuilder.Entity<MyEntity>(entity =>
{
entity.Property(e => e.SomeThing).HasConversion(myEnumTypeConverter);
});
}
private TEnum GetEnumFromDescriptionString<TEnum>(string value)
where TEnum : Enum
{
foreach (TEnum enumValue in Enum.GetValues(typeof(TEnum)))
{
var description = enumValue.GetEnumDescription();
if (description == value)
{
return (TEnum)enumValue;
}
}
throw new InvalidOperationException($"Unable to resolve enum value for string \"{value}\".");
}
}
// DI Setup
public static IServiceCollection ConfigureDatabases(
this IServiceCollection services,
IConfiguration configuration,
IHostEnvironment environment)
{
bool isDev = environment.IsDevelopment();
ConfigureDbContext<MyContext>(
services,
configuration,
"MyContext",
isDev,
(o) => o.MapEnum<MyEnum>("my_entity_my_enum"));
// ...
return services;
}
private static void ConfigureDbContext<TContext>(
IServiceCollection services,
IConfiguration configuration,
string name,
bool enableSensitiveDataLogging = false,
Action<NpgsqlDbContextOptionsBuilder>? npgsqlOptionsAction = null)
where TContext : DbContext
{
string? connectionString = configuration.GetConnectionString(name);
if (string.IsNullOrEmpty(connectionString))
{
throw new InvalidOperationException($"Unable to create a datasource: Connection string '{name}' not found.");
}
services.AddDbContextFactory<TContext>(options =>
{
options.UseNpgsql(connectionString, npgsqlOptionsAction);
options.EnableSensitiveDataLogging(enableSensitiveDataLogging);
options.UseSnakeCaseNamingConvention();
});
} The enum was created with CREATE TYPE "public"."my_entity_my_enum" AS ENUM('VALUE_1', 'VALUE_2');
ALTER TABLE "my_entity"
ADD "someThing" "public"."my_entity_my_enum" NOT NULL DEFAULT 'VALUE_1'; Could you guide me on how to set this up properly? Am I missing something or is this scenario not supported at all? Edit: If I add the following line to the Context I can save the entity when I don't change the enum propertie's value without an error. But when switching to any nnon default value it fails. entity
.Property(e => e.SomeThing)
.HasColumnType("enum")
.HasDefaultValue(MyEnum.Value1)
.HasConversion(myEnumTypeConverter); That's probably because Postgres is then filling in the default value, I guess? |
I am trying to migrate a project to Npgsql (and EF Core) v9, however I cannot get our enums to work.
The enum looks like this:
Previously we had this:
and in the Context we used this:
Following the docs I was trying to set up the following:
Loading entities works, but adding a new one (or changing the enum property of an existing one) fails with the following error:
I tried different things, using DataSource, withtout Datasource, also with adding
but to no avail. What am I missing? I tried some things I saw in other issues around Enum mapping but failed to resolve this somehow.
My Versions are:
The text was updated successfully, but these errors were encountered: