LoginSignup
2
2

More than 1 year has passed since last update.

PostgreSQL EF CoreでのJsonの処理

Posted at

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を利用する。
  • 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の例

image.png

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シリアル化・逆シリアル化を、改めて考えてみて整理しました。今まで、煩雑な処理をしていたのを、改善できました。
  • こうして記事にまとめておかないと、自分でも忘れてしまう。

参考

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2