.NETで作る!

.NETに関するあれこれ(C#、VB.NET)

ダーティチェックエンジン ver.0.5

これの続き。というか実装したものの紹介。 Kairyu.DCEngine ver.0.5

名前とかバージョンとか

KairyuはCodePlexで公開している自作のORM(現在ver0.3)です。 それをベースにダーティチェック部分だけを抜き出しし、整理をしたものが Kairyu.DCEngine。 DCEngine は Dirty Check Engine の略。

バージョン0.4が欠番なのは互換性がなくなったから。

ダーティチェッカー(ダーティチェック)とは何か

定義するようなことでもないですが、なじみがないので改めて説明。 このサイトでいうところのダーティチェックとは以下の機能をもつものとします。

  1. インスタンスの状態を記憶できること。
  2. 現在のインスタンス*1を「1」の状態と比較できること。また、ダーティな理由(追加、更新、削除)がわかること。

用途

UIが存在するアプリケーションであれば大概利用できます。 一般的な主な使い方は以下のとおり。

  1. ファイル、DBから読み込んだEntityクラスの状態を「記憶」(アーカイブと呼びます)。
  2. 保存時、現在のインスタンスの状況と「1」の状態を比較し、追加、更新、削除処理を行う。

保存前に「ダーティであるかのチェック(ダーティでなければ保存処理自体する必要がない)」をすることも可能です。*2

対象となるクラス

端的に言うとカスタムEntityクラス(データベースの行に相当するようなクラス)が対象。 DataTableクラスは対象ではありません。

同一判定の仕様

方法

キー文字列という独自の文字列でインスタンスが同一かを判定しています。 このためメモリ上の同一性(Equalsメソッド)判定ではありませんので注意してください。

たとえば、以下のようなコードの場合、c1.Equals(c2) は False ですが、ダーティチェック上は同一扱いです。

Dim c1 As New Hoge With {.HogeID = 1, .HogeName = "Hoge"}
Dim c2 As New Hoge With {.HogeID = 1, .HoheName = "HogeHoge"}

その理由はキー文字列の仕様にて説明します。

キー文字列

キー文字列は「型正式名、キープロパティ名とその値」で構成されています。 「型正式名」とはType.FullNameで取得できる文字列です。 「キープロパティ」とはいわゆる主キーに相当するプロパティのことを指し、名前が「クラス名+ID」となっているものがキープロパティとして扱われます。

なお、キー文字列は以下のようなコードで求めることができます。(フレームワーク以外で以下の処理をすることはほぼありませんが)

Dim key = KeyGenerator.Generate(c1)
Debug.Print(key) 'Hoge(HogeID=1)

キープロパティの例外

上述の通り、標準では「クラス名+ID」が自動的にキープロパティだと判定されます。命名規約を左記のとおりとすることで、コーディング量を減らすことができますが、以下のようにすることで任意のプロパティを主キープロパティに判定することも可能です。

  • 規約ファイル(KeyContstraint.config)を変更する
  • Key属性を付ける

規約ファイルには主キープロパティの命名規約を正規表現で指定できるようになっており、初期値は

^(?<myclass>.*)ID$

です。

Key属性で指定する場合は、Entityクラスを以下のように定義します。

Class Page
    <Key>
    Property PageNo As Integer
End Class

基底、継承クラスの仕様

Kairyu.DCEngine はORMへの転用を前提としているため、1クラス=1テーブルと考えます。 このため、以下のように定義したInheritsClassは、

計2テーブルへのマッピングが必要と解釈しています。

Class BaseClass
    Property BaseClassID As Integer
    Property Value1 As Integer
End Class

Class InheritsClass
    Inherits BaseClass
    Property Value2 As Integer
End Class

プロパティの所属

上記のEntityクラス定義であれば、以下のように解釈されます。

  • Value1 は BaseClass に所属し、Value2 はInheritsClass に所属する。
  • 主キープロパティは、BaseClass、InheritsClass、両方に所属する。

たとえば、Value1だけを更新した場合、BaseClassのみがダーティと解釈されます。また、Value2だけを更新した場合、InheritsClassのみがダーティと解釈されます。両方更新した場合、BaseClassとInheritsClassの2つがダーティだと解釈されます。

基底、継承の例外

上記の挙動ではなく、全てのプロパティの所属を InheritsClass にしたい場合、Root属性を付けてください。

Class BaseClass
    Property BaseClassID As Integer
    Property Value1 As Integer
End Class

<Root>
Class InheritsClass
    Inherits BaseClass
    Property Value2 As Integer
End Class

Root属性が付いているクラスを見つけると、それ以上 BaseClass のスキャンは行われなくなります。

仕様詳細

  • インターフェイスの場合、自身が基底です
  • 基底クラスがObjectの場合、自身が基底です
  • 基底クラスが継承必須の場合、自身が基底です
  • Root属性がある場合、自身が基底です
  • それ以外は基底ではありません

対象プロパティの仕様

読み書き可能なプロパティが対象です。

プロパティの種類

プロパティは型によって、リテラル、オブジェクト、コレクションの3つに分けて管理しています。

リテラルプロパティ

プロパティの型が以下の条件を満たす場合、リテラルプロパティとして管理します。

  • String型
  • バイト配列型
  • 列挙体
  • IEnumerableインターフェイスを実装しておらず、かつ、型正式名が「System」で始まる(数値型、日付型など)

特徴

プロパティ値そのものが記憶(アーカイブ)され、ダーティチェックの判定に使用されます。

オブジェクトプロパティ

プロパティの型が以下の条件を満たす場合、オブジェクトプロパティとして管理します。

特徴

プロパティ値のキー文字列が記憶(アーカイブ)され、ダーティチェックの判定に使用されます。 これは、自身のクラス(テーブル)から見た場合、重要なのはリレーションに必要な情報だけでだからです。このため、キープロパティ以外の値が変更されたとしてもダーティ判定はされません。(上層へのカスケードはありません)

コレクションプロパティ

プロパティの型が以下の条件を満たす場合、コレクションプロパティとして管理します。

特徴

コレクションの中身が記録(アーカイブ)され、ダーティチェックの判定に使用されます。(下層カスケードされます)

対象プロパティの例外

ダーティ判定の対象にしたくないプロパティがある場合、IgnoreArchive属性を付けてください。

とりあえず…

仕様の話はこの辺にしておいて、次回は使い方を説明します。

*1:正確にいうとインスタンスは別でもいいのですがややこしいのでインスタンスってことにしておきます。

*2:WPF+MVVMの場合、コマンドの実行可否条件に使ったりします。

. .