最近のSoma

ちょっとずつ作っているEntity Framework拡張のO/RマッパーSoma(http://soma.codeplex.com/)ですが、更新系SQLの生成をEntity Frameworkにまかせるのをやめました。マッピングの定義はedmxにあるのでパースして自前でSQLを組み立てることにしました。理由はいろいろありますが、まぁ、融通が利かないからです。オブジェクトコンテキストを使いたくないSomaにとっては不要な機能があるんですよね。

これにともなって次の2つができるようになりました。

  • 更新系処理実行前のフック
  • 生成するSQLの微調整(更新系処理へのオプション指定)

更新系処理実行前のフック

たとえば、すべてのエンティティにModifiedDateみたいなプロパティがあるとして、それを毎回自前で設定するのは面倒です。SomaではIObjectListenerというインタフェースを提供します。たとえばこのようなクラスを作って、インスタンスをRepositoryに登録すればOK。

internal class ObjectListener : IObjectListener
{
    public void PreInsert(PreInsertEvent @event)
    {
    }

    public void PreUpdate(PreUpdateEvent @event)
    {
        if (@event.Param != null && @event.IsEntity)
        {
            var entity = new PropertyAccessor(@event.Param);
            entity["ModifiedDate"] = DateTime.Now;
        }
    }

    public void PreDelete(PreDeleteEvent @event)
    {
    }
}

PropertyAccessorは、デフォルトで存在しないプロパティ名を設定してもエラーが起きません。つまり、すべてのエンティティがModifiedDateプロパティをもっていなくても大丈夫なのです。IObjectListenerの登録はRepositoryごとにできますが、ほとんどの場合、ひとつの実装ですべてのRepositoryに適用できます。

クライアントは気にせず普通にUpdateを実行すれば上記のPreUpdateメソッドが呼び出され、ModifiedDateプロパティに値が設定されます(エンティティがModifiedDateプロパティをもたない場合は何もおきません)。

var rows = _employeeRepository.Update(employee);

生成するSQLの微調整(更新系処理へのオプション指定)

普通の更新処理をするときは次のようにRepositoryのUpdateメソッドを実行しますが、プライマリキーに加えてConcurrencyMode == Fixed なプロパティがwhere句で指定されます。

var rows = _employeeRepository.Update(employee);

たとえば、VersionNoプロパティがFixedに指定されている場合こんなSQLが生成されます。VersionNoがwhere句に指定されてます。

update [dbo].[Employee] set [EmployeeName] = 'hoge', [DepartmentId] = 1, [VersionNo] = 0 + 1, [ModifiedDate] = '2010/09/12 22:44:23' where [EmployeeId] = 1 and [VersionNo] = 0

場合によってはConcurrencyMode == Fixed なプロパティを無視したいときがあります。そんなときは次のようにUpdateメソッドの第2引数にUpdateOptionを渡します。UpdateOptionのIgnoreFixedPropertiesプロパティにはtrueを設定します。

var rows = _employeeRepository.Update(employee, new UpdateOption {IgnoreFixedProperties = true});

すると、生成されるSQLのwhere句からVersionNoの指定がなくなります。

update [dbo].[Employee] set [EmployeeName] = 'hoge', [DepartmentId] = 1, [VersionNo] = 0, [ModifiedDate] = '2010/09/12 22:42:42' where [EmployeeId] = 1

UpdateOptionは他にもプロパティを持っています。S2JDBCとかDomaとかを知っている人はなんとなく使い方がわかるんじゃないでしょうか。

public class UpdateOption
{
    public bool IncludeModifiedPropertiesOnly
    {
        get;
        set;
    }

    public bool ExcludeNull
    {
        get;
        set;
    }

    public IEnumerable<string> IncludeProperties
    {
        get;
        set;
    }

    public IEnumerable<string> ExcludeProperties
    {
        get;
        set;
    }

    public bool IgnoreFixedProperties
    {
        get;
        set;
    }

    public bool SuppressOptimisticConcurrencyException
    {
        get;
        set;
    }
}

これらの機能をもったSomaは9月中にリリースしたいなぁと思っています。まだベータ版ですけど。