連載:Entity Framework 4.1入門

第2回 EF 4.1の規約とデータベースの初期化方法

WINGSプロジェクト 土井 毅(監修:山田 祥寛)
2011/07/13
Page1 Page2 Page3

データベースの初期化

 コード・ファーストの特徴の1つは、データベースの自動生成である。前回も確認したとおり、対応するデータベースが存在しない場合、EF 4.1はデータベースの自動生成を行う。

 ここでは、データベースの自動生成の方法や、初期データの投入について解説する。データベースの自動生成は便利な機能ではあるが、1つ間違えばデータを一瞬で失う危険性も伴うため、しっかり理解しておこう。

データベースを自動生成する方法

 EF 4.1において、実際のデータベースの作成、削除、存在チェックなどの管理をつかさどるのはDatabaseクラス(System.Data.Entity名前空間)である。

 Databaseクラスの、データベースの初期化方法を設定するためのSetInitializerメソッドにより、データベースの自動生成方法をカスタマイズできる。なお、Databaseクラスを使用するには、図7のようにSystem.Data.Entityアセンブリへの参照を追加する必要がある。

図7 System.Data.Entityアセンブリへの参照を追加

 SetInitializerメソッドの引数には、初期化方法を示すIDatabaseInitializerインターフェイスを実装したクラスを指定する。あらかじめ表1のようないくつかのクラスが用意されている。

クラス名 意味
CreateDatabaseIfNotExists データベースが存在しない場合にのみ自動生成を行う
DropCreateDatabaseAlways 常にデータベースを再生成する
DropCreateDatabaseIfModelChanges データベースが存在しない場合およびモデルが変更された場合に、自動的にデータベースを削除して再生成する
表1 データベースの再生成の方法

 リスト14では、SetInitializerメソッドの引数にDropCreateDatabaseIfModelChangesオブジェクトを指定し、モデル変更時の再生成を行うよう指定している。

class Program
{
  static void Main(string[] args)
  {
    // データベース初期化方法の指定
    Database.SetInitializer(
      // ItemCatalogコンテキスト・クラスに基づき、モデル変更時に再生成
      new DropCreateDatabaseIfModelChanges<ItemCatalog>);
……省略……
Module Module1

  Sub Main()

    ' データベース初期化方法の指定
    Database.SetInitializer(
      ' ItemCatalogコンテキスト・クラスに基づき、モデル変更時に再生成
      New DropCreateDatabaseIfModelChanges(Of ItemCatalog))

……省略……
リスト14 データベース初期化方法の指定(上:Program.cs、下:Module1.vb)

 MSDNによると、「デフォルトではモデル変更時にデータベースの再生成を行うDropCreateDatabaseIfModelChangesクラスが使用される」とあるが、実際にはモデル変更時の再生成は行われないようである。従って、デフォルトではデータベースが存在しない場合のみ自動生成を行うCreateDatabaseIfNotExistsクラスが使用されている可能性が高い。

 なお、モデル変更時にデータベースの再生成を行わず、そのままコンテキスト・クラスからデータベースにアクセスしようとすると、図8のような例外が表示される。

図8 モデルとデータベースの整合性エラー

このような場合には、データベースを明示的に削除して再生成するなど、モデルとの整合性を取る必要がある。

初期データの投入方法

 「データベースを作成する際に、同時に初期データを投入する」というのはよく使われるシナリオである。

 EF 4.1では、前述のSetInitializerメソッド呼び出し時に、データベースの初期データの投入を行える。実際には、前述のIDatabaseInitializerインターフェイスを実装した3つのクラスのいずれかを継承したクラスを定義し、Seedメソッドをオーバーライドして初期データを投入するコードを記述する*3

*3 IDatabaseInitializerインターフェイスを実装したクラスを自分で定義することも可能だが、あらかじめ準備されている3つのクラスから継承する方が簡単である。

 リスト15は、DropCreateDatabaseIfModelChangesクラスを継承したCustomSeedInitializerというクラスで、Seedメソッドでデータベースの初期データを作成するコードである。

---CustomSeedInitializer.cs

// 自作のデータベース初期化クラス
// DropCreateDatabaseIfModelChangesを継承
public class CustomSeedInitializer :
  DropCreateDatabaseIfModelChanges<ItemCatalog>
{

  // Seedメソッドをオーバーライド
  protected override void Seed(ItemCatalog context)
  {
    // 基底クラスのSeedメソッド呼び出し
    base.Seed(context);

    ……以降はデータの作成。一部省略……

    // 複合主キーのデータを作成
    new List<CompositeKeyEntity>
      {
        new CompositeKeyEntity()
        {
          Key1 = 1,
          Key2 = "foo",
          Data = "foofoo"
        },
        new CompositeKeyEntity()
        {
          Key1 = 2,
          Key2 = "bar",
          Data = "barbar"
        }
      }.ForEach(u => context.CompositeKeyEntities.Add(u));
      // ↑ListのForEachメソッドとラムダ式でエンティティを一括追加

    // 変更をデータベースに反映
    int recordsAffected = context.SaveChanges();
  }
}
---Program.cs
class Program
{
  static void Main(string[] args)
  {
    ' 自作のCustomSeedInitializerを指定
    Database.SetInitializer(new CustomSeedInitializer());
……省略……
---CustomSeedInitializer.vb

' 自作のデータベース初期化クラス
' DropCreateDatabaseIfModelChangesを継承
Public Class CustomSeedInitializer
  Inherits DropCreateDatabaseIfModelChanges(Of ItemCatalog)

  ' Seedメソッドをオーバーライド
  Protected Overrides Sub Seed(context As ItemCatalog)

    ' 基底クラスのSeedメソッド呼び出し
    MyBase.Seed(context)

    ……以降はデータの作成。一部省略……

    ' 複合主キーのデータを作成
    Dim list As New List(Of CompositeKeyEntity) From {
        New CompositeKeyEntity() With {
          .Key1 = 1,
          .Key2 = "foo",
          .Data = "foofoo"
        },
        New CompositeKeyEntity() With {
          .Key1 = 2,
          .Key2 = "bar",
          .Data = "barbar"
        }
      }

    ' ListのForEachメソッドとラムダ式でエンティティを一括追加
    list.ForEach(Function(u As CompositeKeyEntity) context.CompositeKeyEntities.Add(u))

    ' 変更をデータベースに反映
    Dim recordsAffected = context.SaveChanges()

  End Sub
End Class
---Module1.vb
Module Module1

  Sub Main()

    ' 自作のCustomSeedInitializerを指定
    Database.SetInitializer(New CustomSeedInitializer())
……省略……
リスト15 (上1:CustomSeedInitializer.cs、上2:Program.cs、下1:CustomSeedInitializer.vb、下2:Module1.vb)

 このSeedメソッドはデータベースの再生成の際に呼び出され、指定されたデータの登録を行うものだ。初期データの作成方法は、通常のデータベースへのデータ保存処理と同じである。Seedメソッドの引数にDbContextのインスタンスが渡されているため、そこからデータベースへの追加処理を行っている。

[コラム]データベースのマイグレーションについて

 Ruby on Railsなどのフレームワークにおいては、「マイグレーション」と呼ばれる、データベースの構造を徐々に変更しながら開発する方法がサポートされており、スキーマの変更時に、データベースを削除せず、フィールドの追加、削除を個々に行える。これにより、開発開始時点でスキーマを確定させず、データを残したまま、徐々にデータベースを成長させていくことができる。

 残念ながら現在のEF 4.1は、このようなマイグレーションに対応しておらず、モデル変更時にはデータベース全体が削除されてしまう。将来的なマイグレーション(機能名としては「Database Schema Evolution」と呼ばれている)のサポートが予定されているようなので、期待して待ちたい。

まとめ

 次回は、「Fluent API」と呼ばれる、エンティティ・クラス外のソース・コードから、エンティティの構造を指定する方法について解説する。

 また、EF 4.1で扱いやすくなった自己追跡エンティティや同時実行制御を含め、DbContextクラスの使用方法についても解説する。end of article


 INDEX
  [連載]Entity Framework 4.1入門
  第2回 EF 4.1の規約とデータベースの初期化方法
    1.Entity Framework 4.1の規約とカスタマイズ方法(1)
    2.Entity Framework 4.1の規約とカスタマイズ方法(2)
  3.データベースの初期化

インデックス・ページヘ  「連載:Entity Framework 4.1入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間