noxi雑記

.NET、Angularまわりの小ネタブログ

EntityFrameworkCoreのValueConverterを試す

ASP.NET Core の All やら App のパッケージを利用すると漏れなく付いてくるという噂の EntityFrameworkCore ですが、2.1からデータベースとやりとりする型とマッピングされるエンティティモデル上のプロパティの型とを変換する ValueConverter が追加されていたようです。とても欲しかった機能なのですが気付くのが遅れました。この値変換機能を試してみた結果です。

docs.microsoft.com


試した環境

  • macOS 10.13.6
  • .NET Core 2.2.1 (2.2.103)
  • EntityFrameworkCore 2.2.1

組み込みのValueConverterを利用する

定義されている ValueConverter

EntityFrameworkCore にはいくつかの良く利用しそうな ValueConverter が実装されています。以下はドキュメントを表にまとめたものです。全て Microsoft.EntityFrameworkCore.Storage.ValueConversion 名前空間にいます。

クラス名 モデルの型 DBの型 説明
BoolToZeroOneConverter bool int short など boolを0または1に変換。変換先はC#組み込みの数値型(intやshort、doubleなど)が指定出来る。
BoolToStringConverter bool string boolを指定した文字に変換。
BoolToTwoValuesConverter bool any boolを指定した値に変換。
BytesToStringConverter byte[] string バイト配列をBase64形式の文字列に変換。
CastingConverter any any ただ型をキャストするだけ。
CharToStringConverter char string charを1文字のstringに変換。
DateTimeOffsetToBinaryConverter DateTimeOffset long 日時をlongのバイナリ値に変換。
DateTimeOffsetToBytesConverter DateTimeOffset byte[] 日時をバイト配列に変換。
DateTimeOffsetToStringConverter DateTimeOffset string 日時を文字列に変換。
DateTimeToBinaryConverter DateTime long DateTimeKindも含んだ日時をlongのバイナリ値に変換。
DateTimeToStringConverter DateTime string 日時を文字列に変換。
DateTimeToTicksConverter DateTime long 日時をTicksに変換。タイムスタンプでは無い。
EnumToNumberConverter Enum int short など Enumを値の数値に変換。変換先はC#組み込みの数値型(intやshort、doubleなど)が指定出来る。
EnumToStringConverter Enum string Enumを名前の文字列に変換。
GuidToBytesConverter Guid byte[] Guidをバイト配列に変換。
GuidToStringConverter Guid string Guidを文字列( "8-4-4-4-12" 形式)に変換。
NumberToBytesConverter int short など byte[] 数値をバイト配列に変換。変換元はC#組み込みの数値型(intやshort、doubleなど)が指定出来る。
NumberToStringConverter int short など string 数値を文字列に変換。
StringToBytesConverter string byte[] 文字列をUTF-8形式のバイト配列に変換。
TimeSpanToStringConverter TimeSpan string TimeSpanを文字列に変換。
TimeSpanToTicksConverter TimeSpan long TimeSpanをTicksに変換。

ValueConverterを使用する

ValueConverterは今のところ対象のプロパティに対して Fluent API で指定することで使用できます。 Attribute を使用して設定することはできません。また特定の型に対して一括で指定するような方法もありません。。

Fluent API で ValueConverter を使用するには HasConvention メソッドを使用します。注意する点として、 HasConvention メソッドに渡すのは ValueConverter の型ではなくインスタンス化したオブジェクトを渡す事です。筆者はここを間違えてしばらく悩みました。。。

// Guidを xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 形式の文字列に変換
var guidToStringConverter = new GuidToStringConverter();

// Enumを対応する数値型に変換
var enumToNumberConverter = new EnumToNumberConverter<DayOfWeek, int>();

// boolを文字列(0/1)に変換
var boolToStringConverter = new BoolToStringConverter("0", "1");

modelBuilder.Entity<SampleEntity>(builder =>
{
    builder
        .ToTable("sample")
        .HasKey(e => e.Id);

    builder
        .Property(e => e.Id)
        .HasColumnType("CHAR(36)")
        .IsUnicode(false)
        .HasConversion(guidToStringConverter);

    builder
        .Property(e => e.DayOfWeek)
        .HasColumnType("CHAR(1)")
        .IsUnicode(false)
        .HasConversion(enumToNumberConverter);

    builder
        .Property(e => e.Deleted)
        .HasColumnType("CHAR(1)")
        .IsUnicode(false)
        .HasConversion(boolToStringConverter);
});