Using MapEnum doesn't seem to work properly
I all,
I'm not sure wether I'm doing something wrong or this functionality is a bit broken, but I'm setting up my enums like this:
var dataSourceBuilder = new NpgsqlDataSourceBuilder(configuration.ConnectionString);
dataSourceBuilder.EnableDynamicJson();
var dataSource = dataSourceBuilder.Build();
services.AddDbContext<DCMContext>(options => options.UseNpgsql(dataSource,
o => {
o.MapEnum<DeviceType>();
o.MapEnum<DispatchStatus>();
o.MapEnum<DriverStatus>();
o.MapEnum<FuelType>();
o.MapEnum<PaymentStatus>();
o.MapEnum<PaymentType>();
o.MapEnum<RateType>();
o.MapEnum<SettingRight>();
o.MapEnum<SettingType>();
o.MapEnum<Sex>();
o.MapEnum<TransferStatus>();
o.MapEnum<TripCancelReason>();
o.MapEnum<TripScope>();
o.MapEnum<TripStatus>();
}));
And create a migration which results in this:
migrationBuilder.AlterDatabase()
.Annotation("Npgsql:Enum:device_type", "android,ios")
.Annotation("Npgsql:Enum:dispatch_status", "accepted,automatic,canceled,pending,rejected,timeout")
.Annotation("Npgsql:Enum:driver_status", "available,on_break,on_shift,unavailable")
.Annotation("Npgsql:Enum:fuel_type", "cng,diesel,electric,hydrogen,lpg,petrol,unknown")
.Annotation("Npgsql:Enum:payment_status", "awaiting_method,completed,failed,pending,processing,refunded,requires_manual_intervention")
.Annotation("Npgsql:Enum:payment_type", "bank_transfer,cash,credit_card,debit_card,invoice,online_payment")
.Annotation("Npgsql:Enum:rate_type", "distance,starting_rate,time")
.Annotation("Npgsql:Enum:setting_right", "admin,self")
.Annotation("Npgsql:Enum:setting_type", "base64,bool,date,date_time,duration,float,integer,string,time")
.Annotation("Npgsql:Enum:sex", "female,male,other,unknown")
.Annotation("Npgsql:Enum:transfer_status", "canceled,completed,failed,in_progress,pending")
.Annotation("Npgsql:Enum:trip_cancel_reason", "client_cancels,client_gone,contractor_unknown,date_time_wrong,double_booked,van_required")
.Annotation("Npgsql:Enum:trip_scope", "combined,return,standard")
.Annotation("Npgsql:Enum:trip_status", "accepted,canceled,canceled_requested,completed,declined,in_progress,in_transit,new,no_show,no_show_requested");
And the model snapshot:
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "device_type", new[] { "android", "ios" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "dispatch_status", new[] { "accepted", "automatic", "canceled", "pending", "rejected", "timeout" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "driver_status", new[] { "available", "on_break", "on_shift", "unavailable" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "fuel_type", new[] { "cng", "diesel", "electric", "hydrogen", "lpg", "petrol", "unknown" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "payment_status", new[] { "awaiting_method", "completed", "failed", "pending", "processing", "refunded", "requires_manual_intervention" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "payment_type", new[] { "bank_transfer", "cash", "credit_card", "debit_card", "invoice", "online_payment" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "rate_type", new[] { "distance", "starting_rate", "time" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "setting_right", new[] { "admin", "self" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "setting_type", new[] { "base64", "bool", "date", "date_time", "duration", "float", "integer", "string", "time" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "sex", new[] { "female", "male", "other", "unknown" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "transfer_status", new[] { "canceled", "completed", "failed", "in_progress", "pending" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "trip_cancel_reason", new[] { "client_cancels", "client_gone", "contractor_unknown", "date_time_wrong", "double_booked", "van_required" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "trip_scope", new[] { "combined", "return", "standard" });
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "trip_status", new[] { "accepted", "canceled", "canceled_requested", "completed", "declined", "in_progress", "in_transit", "new", "no_show", "no_show_requested" });
...
b1.Property<Sex>("Sex")
.HasColumnType("sex");
Now I'm getting this exception when retrieving data from the database:
System.InvalidCastException: Reading as 'DCM.Entities.Enums.Sex' is not supported for fields having DataTypeName 'public.sex'
---> System.NotSupportedException: Reading and writing unmapped enums requires an explicit opt-in; call 'EnableUnmappedTypes' on 'NpgsqlDataSourceBuilder' or NpgsqlConnection.GlobalTypeMapper....
And this is how the enum looks like:
public enum Sex
{
Unknown,
Male,
Female,
Other
}
As you can see the order is different, but I thought by reading through issue #3390 that it shouldn't matter. But still I'm getting above mentioned exception. Could somebody help me out?
Thank you
Basically with 9.0, if you are using NpgsqlDataSourceBuilder along with UseNpgsql, you need to add calls to MapEnum via your dataSourceBuilder as well. So keep your existing calls to MapEnum, but also add:
dataSourceBuilder.MapEnum<DeviceType>();
dataSourceBuilder.MapEnum<DispatchStatus> ();
...and so on
And that should solve your runtime error.
For a more detailed explanation of why this is required, see the Breaking Changes documentation for v9.0.
Apparently this is broken again in 10.0 RC 😮💨 https://github.com/npgsql/efcore.pg/issues/3653