EntityFrameworkCore の7.0プレビューが出たので、PostgreSQL EF Coreでの、Jsonの処理について検証しました。
環境
- Microsoft.EntityFrameworkCore 7.0.0-preview.1.22076.6
- Npgsql 7.0.0-preview.1
- Npgsql.EntityFrameworkCore.PostgreSQL 7.0.0-preview.1
- .NET 6
Npgsql.Json.NET 7.0.0-preview.1 は必要なさそうです。
前提
PostgresSQLの、Entiry Framework CoreでのJSONの取り扱いは、
- A) String mapping
- B) POCO mapping
- C) JsonDocument DOM mapping
の3種類
Aの文字列で処理するのは、自分でJSONへのシリアル化と逆シリアル化(パース)をする。他の2つに比べて、制限された方法である、と解説されています。
メリット・デメリット
以下、AとBとを実際に実装してみて得られた知見。
Aの方法
- 自分でシリアル化・逆シリアル化が必要。 タイミングとして、EFで、ContextをLoadする時に逆シリアル化し、Saveする前にシリアル化する処理、でOK。(他の処理のタイミングとして、プロパティをgetした時、setした時でも、出来そうだ)
- このままで context.SaveChangesが機能する。
-
double.NaN, double.PositiveInfinity, double.NegativeInfinity
は、System.Text.Json(Microsoft)
を利用してJsonとして処理できる。
しかし、Brushなど、System.Text.Json
でも処理できない型があるので、それは文字列にするなどの処理が必要。
Bの方法
- 自分でJsonのシリアライズ・逆シリアル化は不要。
- ただし、プロパティの値を変更しても、このままでは
context.SaveChanges
が機能しない。なんらかの処理が必要。- 例1 Jsonのプロパティを変更したと示す、
IsModified
をtrueにする。
context.Entry(groupB).Property(e => e.CustomerJsonb).IsModified = true;
- 例2 Change Tracker を機能させるために、
ValueComparer
を利用する。
- 例1 Jsonのプロパティを変更したと示す、
-
dobule.NaN
などは、このままでは処理できない(現時点では)。文字列として処理すればOK。
Jsonで処理出来ない型
AもBもJsonとして処理できない型は、文字列にしたりして保存すると良い。
例:Brush
doubleの無限大・非数をJson用に文字列にする例
- Bの方法で利用します。Aの方法で、
System.Text.Json(Microsoft)
を利用するなら、無限大・非数をJsonにできるので、必要ないです。 - テストした限りではこれで機能すると思います。
- アプリ内部では、
Number
を利用。Jsonには、NumberS
を利用。 - (string?とかの?は、C# 8.0以降の、Null 許容参照型を示します)
private double _number;
[JsonIgnore]
public double Number
{
get { return _number; }
set
{
_number = value;
_numberS = value.ToString();
}
}
private string? _numberS;
public string? NumberS
{
get { return _numberS; }
set
{
_numberS = value;
_number = double.Parse(value);
}
}
BrushをJson用に文字列にする例
- アプリ内部では、Colorプロパティを利用。Jsonには、ColorSを利用。
-
SolidColorBrush
のみ対象にしています。
private Brush? _color;
[JsonIgnore]
public Brush? Color
{
get { return _color; }
set
{
_color = value;
_colorS = value?.ToString(); // → value is null ? null : value.ToString();
}
}
private string? _colorS;
public string? ColorS
{
get { return _colorS; }
set
{
_colorS = value;
_color = Utils.GetBrushFromString(value);
}
}
Bの方法でデーターベースに保存されたJsonの例
PostgreSQLの、jsonb
と、json
とは少し違います。ただ、 「\u221E」 は、「∞」 なので、この点は単に表示の違いだと思います。
Bの方法のEntity全体
public class GroupB
{
public int Id { get; set; }
[Column(TypeName = "jsonb")]
public CustomerB? CustomerJsonb { get; set; } //Jsonbで保存
[Column(TypeName = "json")]
public CustomerB? CustomerJson { get; set; } //Jsonで保存
}
public class CustomerB
{
public string? Name { get; set; }
private double _number;
[JsonIgnore]
public double Number
{
get { return _number; }
set
{
_number = value;
_numberS = value.ToString();
}
}
private string? _numberS;
public string? NumberS
{
get { return _numberS; }
set
{
_numberS = value;
_number = double.Parse(value);
}
}
private Brush? _color;
[JsonIgnore]
public Brush? Color
{
get { return _color; }
set
{
_color = value;
_colorS = value?.ToString(); // → value is null ? null : value.ToString();
}
}
private string? _colorS;
public string? ColorS
{
get { return _colorS; }
set
{
_colorS = value;
_color = Utils.GetBrushFromString(value);
}
}
}
public static class Utils
{
public static SolidColorBrush? GetBrushFromString(string? brushString)
{
return string.IsNullOrEmpty(brushString) ? null : (SolidColorBrush?)new BrushConverter().ConvertFromString(brushString);
}
}
備考
- 今まで設定の保存に、XMLを利用していたのですが、Jsonの方が扱いやすいです。
- double、BrushのJsonシリアル化・逆シリアル化を、改めて考えてみて整理しました。今まで、煩雑な処理をしていたのを、改善できました。
- こうして記事にまとめておかないと、自分でも忘れてしまう。