前口上
PostgreSQLをDapper経由で扱っている場合、テーブルのフィールドが列挙型として定義されている場合の取り扱い方がわかったのでまとめてみようかなって。
環境
環境は以下の通り
- PostgreSQL 12.4
- Dapper 2.0.35
- .NET Core 3.1
Queryの場合
以下のような列挙子とテーブルがあったとして、
CREATE TYPE mood AS enum ('sad','ok','happy');
CREATE TABLE some_table (
id serial PRIMARY KEY,
emotion mood NOT NULL
);
INSERT INTO some_table(emotion)
VALUES ('sad'),
('ok'),
('happy');
C#側でこんな感じでQueryをかけてみる
using System;
using Dapper;
using Npgsql;
namespace Playground
{
public enum Mood
{
Sad,
Ok,
Happy
}
public class SomeTableElement
{
public int? Id { get; set; }
public Mood Emotion { get; set; }
public override string ToString() => $"Id:{Id?.ToString() ?? "N/A"} Emotion:{Emotion}";
}
class MainEntry
{
public static void Main()
{
using var con = new NpgsqlConnection("こねくしょんすとりんぐ");
foreach (var elem in con.Query<SomeTableElement>("select * from some_table")) Console.WriteLine(elem);
}
}
}
じっさい、これは何の問題も無く動いて、以下のような結果を得ることができる。
Id:1 Emotion:Sad
Id:2 Emotion:Ok
Id:3 Emotion:Happy
ココまではさして問題にならない。
Insertしたい
Queryがうまくいったのなら、Insertも普通に書いてうまくいくか試してみた。
素直に書いてみる
まずは普通に書いてみた。
public static void Main()
{
using var con = new NpgsqlConnection("こねくしょんすとりんぐ");
con.Execute("insert into some_table(emotion) values(@emotion)",
new SomeTableElement {Emotion = Mood.Happy});
}
これだと残念ながら、以下のようなエラー吐いて無事死亡する。
Npgsql.PostgresException: '42804: column "emotion" is of type mood but expression is of type integer'
早い話が、mood
なんだから、integer
突っ込むなう゛ぉけといわれている。これは多分Dapperが裏側でC#のEnumを整数化してるんじゃないかなと予想。
文字列ならイケるかしらん?
だったら、文字列ならイケる?ってことで試してみる。
public static void Main()
{
using var con = new NpgsqlConnection("こねくしょんすとりんぐ");
con.Execute("insert into some_table(emotion) values(@emotion)",
new {Emotion="happy"});
}
残念、これも以下のようなエラーを吐いて死んでしまった。
Npgsql.PostgresException: '42804: column "emotion" is of type mood but expression is of type text'
さっきと同じようにmood
なのに文字列入れてくれるなと怒られる。
じゃあどうすりゃいいのか?
だったら、渡すParameterが文字列だから後はmoodに適宜妥当1に変換してくれと書いてみる。
public static void Main()
{
using var con = new NpgsqlConnection("こねくしょんすとりんぐ");
con.Execute("insert into some_table(emotion) values(@emotion::mood)",
new {Emotion="happy"});
}
これはうまく動いた。
Updateはいけるかしらん?
じゃあ、UpdateしてみんなHappyになってもらおうってことで試してみた。
public static void Main()
{
using var con = new NpgsqlConnection("こねくしょんすとりんぐ");
con.Execute("update some_table set emotion=@emotion::mood", new {emotion = "happy"});
foreach (var elem in con.Query<SomeTableElement>("select * from some_table"))
{
Console.WriteLine(elem);
}
}
結果は
Id:1 Emotion:Happy
Id:2 Emotion:Happy
Id:3 Emotion:Happy
Id:4 Emotion:Happy
ってことで、みんなHappyになれた。
注意すべきこと
一般的にPostgreSQL方面はsnail_case、C#はUpperCamelを採用しているかなって。Dapperはその辺大分空気読んでくれてCollationを大体case-insensitiveで取り扱ってくれるのだけど、今回の事例では、stringのparameterをPostgreSQL側でキャストしてもらうことになるので、case-sensitiveになっているので、その辺の調整はC#側でする必要があるのでその点はご注意のほど。
-
略して適当w ↩