Как отобразить перечисление C# в перечисление PostgreSQL с помощью Dapper.FastCRUD?

У меня есть класс Sample одним из свойств которого является перечисление, TargetType, У меня есть соответствующая таблица samples определенный в базе данных PostgreSQL, наряду с соответствующим типом перечисления, targettypes,

С Dapper.FastCRUD я могу успешно извлекать записи из таблицы. Тем не менее, я получаю ошибку во время вставки:

Npgsql.PostgresException (0x80004005): 42804: column "target_type" is of type targettype but expression is of type integer

РЕДАКТИРОВАТЬ 1: MoonStorm - создатель Dapper.FastCRUD - пояснил, что преобразования типов DB-CLR обрабатываются Dapper. Итак, теперь вопрос:

Как мне сказать Dapper для сопоставления C# enum TargetType в PostgreSQL ENUM TYPE targettype ?

Перечисление определяется как:

public enum TargetType
{
    [NpgsqlTypes.PgName("Unknown")]
    UNKNOWN = 0,

    [NpgsqlTypes.PgName("Animal")]
    ANIMAL = 1,

    [NpgsqlTypes.PgName("Car")]
    CAR = 2,

    [NpgsqlTypes.PgName("Truck")]
    TRUCK = 3
}

И класс определяется как:

[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; }

}

РЕДАКТИРОВАТЬ 2: Пересмотренный пример с рабочей вставкой.

Иллюстрация использования:

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
    ...
}

1 ответ

Я столкнулся с этой же проблемой сегодня, и я пришел к выводу, что Даппер в настоящее время просто не поддерживает это. То, что вы хотели бы сделать, это зарегистрировать обработчик пользовательского типа с SqlMapper как это:

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(); 
    }
  }
}

К сожалению, это не работает, потому что Dapper игнорирует специальные обработчики типов для Enums, в частности. См. https://github.com/StackExchange/Dapper/issues/259 для получения подробной информации.

Мой подход в ожидании, чтобы увидеть, будет ли когда-либо решаться этот вопрос, будет писать NpgsqlCommand запросы непосредственно при работе с этими видами перечислений.

Вы, вероятно, должны конвертировать TargetType в целое число.

Не проверено, но что-то вроде

Get 
{
    return (int)this.TargetType;
}
Другие вопросы по тегам