Entity Frameworkで非公開のプロパティをテーブルに追加する方法


案外EntityTypeConfiguration<T>クラスの応用的な使い方について言及している日本語記事が少ないようなのでコードファースト布教のためここに記す。

導入

Entity Frameworkをコードファーストで使う場合、チュートリアルを見ながら作ったモデルクラスはたいていpublicなプロパティをいかにもな感じの命名規則にしたがって並べたものになるだろう。Entity Frameworkにはそれだけのコードでリレーションシップの解決を含めたデータベース定義を生成する能力がある。

しかし現実には徐々に不満が出てくるものだ。たとえばURLプロパティの型をUriクラスにする事はできない。SQL Server Compact 3.5を使用している場合、string型のプロパティはデータベースではntext型となりクエリ内での使用が制限される。運悪くデータベースの命名規則がC#のそれと矛盾する場合、マッピングに不都合が出るかもしれない。このままではコードファーストなどORマッパーと割り切るにも微妙なプロトタイピング用の機能にすぎない。しかしEntity Frameworkにはデータベースのマッピング方法をコードで定義する手段も存在する。Fluent APIだ。

EntityTypeConfiguration<T>クラスとは

Fluent APIは適用したいDbContextのOnModelCreatingメソッドで使うものだが、このとき第一引数に渡されるDbModelBuilderオブジェクトには型ごとのモデル構成を格納するConfigurationsというプロパティがある。

モデル構成クラスはエンティティ型用のEntityTypeConfiguraion<T>と複合型用のComplexTypeConfiguration<T>がある。これらのモデル構成クラスも当然Fluent APIの一部で、DbModelBuilderと似たようなメソッドが使える。このクラスを継承したカスタム構成クラスを対象のクラスの内部に定義することで、非公開メンバをモデルに追加することが出来る。

これでURLプロパティの型をこんなふうにUriクラスにすることが可能だ。

using System;
using System.Data.Entity.ModelConfiguration;

public class Bookmark {
    public virtual int ID { get; private set; }
    public virtual string Title { get; set; }
    public virtual string Description { get; set; }
    protected virtual string RawURL { get; set; }
    public Uri URL
    {
        get { return new Uri(RawURL); }
        set { RawURL = value.ToString(); }
    }

    public class Configuration : EntityTypeConfiguration<Bookmark> {
        public Configuration()
        {
            Property(entity => entity.ID);
            Property(entity => entity.Title).HasColumnType("nvarchar").HasMaxLength(1024);
            Property(entity => entity.Description);
            Property(entity => entity.RawURL).HasColumnName("URL");
            Ignore(entity => entity.URL);
            HasKey(entity => entity.ID);
        }
    }
}

備考

  • この方法で追加されたURLプロパティがEntity Frameworkから無視(Ignore)されていることに注意されたし。このプロパティはクエリに使用できないので、多くの場合RawURLのゲッターのみをクエリのために公開したほうが実用的である。
  • 最後にコンテキストクラスのOnModelCreatingメソッドを継承して、modelBuilder.Configurations.Add(new Bookmark.Configuration());を追加するのを忘れないこと。
  • コードファーストは生成されるデータベースのスキーマの確認が面倒くさいと思うかもしれない。コードファーストマイグレーションを使用して移行コードを観察しよう。マイグレーションコードではインデックスの追加なども可能なのでチューニングにも有益だ。
  • この程度でデータベースアプリを書く上での実用性のハードルを完全に越えられるわけではない。しかしSQL Server Compactでデスクトップアプリを作成するような用途ならコードファーストは十分あなたの期待に応える働きをできると思う。