Reputation: 73
I have a class Sample
, one of whose properties is an enum, TargetType
. I have a corresponding table samples
defined in a PostgreSQL database, along with a matching enum type, targettypes
.
With Dapper.FastCRUD, I can retrieve records from the table successfully. However, I get an error during insertion:
Npgsql.PostgresException (0x80004005): 42804: column "target_type" is of type targettype but expression is of type integer
EDIT 1: MoonStorm - creator of Dapper.FastCRUD - clarified that DB-CLR type conversions are handled by Dapper. So, now the question is:
How do I tell Dapper to map the C# enum TargetType
to PostgreSQL ENUM TYPE targettype
?
The enum is defined as:
public enum TargetType
{
[NpgsqlTypes.PgName("Unknown")]
UNKNOWN = 0,
[NpgsqlTypes.PgName("Animal")]
ANIMAL = 1,
[NpgsqlTypes.PgName("Car")]
CAR = 2,
[NpgsqlTypes.PgName("Truck")]
TRUCK = 3
}
And the class is defined as:
[Table("samples")]
public partial class Sample
{
[Column("recording_time")]
public DateTime RecordingTime { get; set; }
[Column("x_position")]
public double X_Position { get; set; }
[Column("x_velocity")]
public double X_Velocity { get; set; }
[Column("y_position")]
public double Y_Position { get; set; }
[Column("y_velocity")]
public double Y_Velocity { get; set; }
[Key]
[Column("id")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public ulong Id { get; set; }
[Column("target_type")] // <--- This is the offending column
public TargetType TargetType { get; set; }
}
EDIT 2: Revised the example with working insert.
Illustration of usage:
using Npgsql;
using Dapper.FastCrud;
...
NpgsqlConnection.MapEnumGlobally<TargetType>("public.targettype"); // ... (1)
OrmConfiguration.DefaultDialect = SqlDialect.PostgreSql;
(using NpgsqlConnection conn = ...) // Connect to database
{
var samples = conn.Find<Sample>(); // <--- This works correctly
foreach (Sample s in samples)
Console.WriteLine(s);
... // Generate new samples
using (var writer = conn.BeginBinaryImport(sql))
{
foreach (Sample s in entities)
{
writer.StartRow();
writer.Write(s.TargetType); // <--- This insert works, due to (1)
...
}
}
foreach (Sample sample in sampleList)
conn.Insert<Sample>(sample); // <--- This throws PostgresException
...
}
Upvotes: 3
Views: 6672
Reputation: 20686
I've been faced with this same problem today, and my conclusion is that Dapper currently simply doesn't support this. What you'd want to do is register a custom type handler with SqlMapper
like this:
public class DbClass
{
static DbClass()
{
SqlMapper.AddTypeHandler(new MyPostgresEnumTypeHandler());
}
class MyPostgresEnumTypeHandler : SqlMapper.TypeHandler<MyPostgresEnum>
{
public override MyPostgresEnum Parse(object value)
{
switch (value)
{
case int i: return (MyPostgresEnum)i;
case string s: return (MyPostgresEnum)Enum.Parse(typeof(MyPostgresEnum),s);
default: throw new NotSupportedException($"{value} not a valid MyPostgresEnum value");
}
}
public override void SetValue(IDbDataParameter parameter, MyPostgresEnum value)
{
parameter.DbType = (DbType)NpgsqlDbType.Unknown;
// assuming the enum case names match the ones defined in Postgres
parameter.Value = Enum.GetName(typeof(MyPostgresEnum), (int)value).ToString().ToLowerInvariant();
}
}
}
Unfortunately, this doesn't work because Dapper ignores custom type handlers for Enums, specifically. See https://github.com/StackExchange/Dapper/issues/259 for details.
My approach while waiting to see if this issue is ever dealt with is going to be to write NpgsqlCommand
queries directly when dealing with these kinds of enums.
Upvotes: 2
Reputation: 87
You probably have to convert TargetType to a integer.
Untested but something like:
Get
{
return (int)this.TargetType;
}
Upvotes: 0