チーム開発のための .NET 開発環境の整備(DBの準備)

.NETでのチーム開発のための環境整備を社内で作成しているところですが、備忘もかねて作業記録や気が付いた点など書いていきます。


今回は本番とテストでのDB切り替えやスキーマの同期について書きます。

今回の概要

  1. CodeFirstでテーブル作成
  2. テスト・本番用のDB切り替え
  3. Seedで初期データの投入

3のSeedで初期データの挿入までは連載:Entity Framework 4.1入門 by @IT に詳細な説明があります。

今回の環境


1. CodeFirstでテーブル作成

従来はDBスキーマを先に作成し、それに合うようにエンティティクラスを作成するのが普通でしたが、 エンティティクラスのコードを先に記述して、それを元にテーブルが自動生成されるという仕組みが コードファーストです。 もちろん先にDB設計ができている場合はあえてコードファーストを採用する必要はありません。 またコード・ファースト開発においてはRailsのようにCoC(convention over configuration)なのでテーブル名やプロパティ名などいくつかの規約があります。 上記で紹介の記事に詳細が記載されてますので詳しくはそちらを参照ください

モデルの作成
Models/Message.cs
using System;

namespace MiniBlogSample.Models
{
    public class Message
    {
        public int MessageID { get; set; }
        public string Text { get; set; }
        public DateTime CreatedAt { get; set; }
        public DateTime UpdatedAt { get; set; }
    }
}
DbContextの作成
Models/MiniBlogSampleDbContext .cs
using System;
using System.Data.Entity;

namespace MiniBlogSample.Models
{
    public class MiniBlogSampleDbContext : DbContext
    {
        public DbSet<Message> Messages { get; set; }
    }
}
モデル変更時のデータベースとの同期

開発中、エンティティモデルが変更になった際に実際のデータベースを自動的に作成しなおすという機能があります。 これを有効にするためにはApprication_Start()などの中でDropCreateDatabaseIfModelChangesを呼び出します。 開発中のみで行うようReleaseでは無効にしたほうがよいかと思います。

Global.asax.cs
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            AuthConfig.RegisterAuth();
#if DEBUG
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MiniBlogSampleDbContext>());
#endif
        }

この機能はMiniBlogSampleDbContextが初期化されるときに機能します。 したがって通常はDbContextを使用するコントローラにアクセスされたときになります。

2. テスト・本番用のDB切り替え

上記のサイトの説明にあるようにアプリケーション構成ファイル(App.config/Web.config)で接続文字列を設定します。 したがって本番用、テスト用のデータベース接続の切り替えにもアプリケーション構成ファイルを用います。

connectionStringsに下記のように接続文字列を設定します。この時nameは上で作成したDbContextクラスと同じ名前にする必要があります。

Web.config
<?xml version="1.0" encoding="utf-8"?>
<!--
  ASP.NET アプリケーションを構成する方法の詳細については、
  http://go.microsoft.com/fwlink/?LinkId=169433 を参照してください
  -->
<configuration>

  以下省略.....

    <add name="MiniBlogSampleDbContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=MiniBlogSampleDb;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\MiniBlogSampleDbContext_test.mdf" providerName="System.Data.SqlClient" />
  </connectionStrings>
 
  以下省略.....

アプリケーション構成ファイルの切り替えは、ビルド時に行われるのではなくDeploy時に行われるので、Web.configに指定するDBはテスト側にしておいて Web.Release.configに本番側のDBの設定をします。

Web.Release.config
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings>
    <add name="MiniBlogSampleDbContext" 
      connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=MiniBlogSampleDb;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\MiniBlogSampleDbContext.mdf" 
      xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
  </connectionStrings>
  <system.web>
  </system.web>
</configuration>

この記述によりWeb.configに指定されている接続文字列をReleaseのデプロイ時に置き換えることができます。

3. Seedで初期データの投入

1.のモデル変更時のデータベースとの同期で指定した Database.SetInitializerをカスタマイズすることで初期必須データの投入*1をさせることができます。

Initializerに指定したDropCreateDatabaseIfModelChangesを継承したクラスを作成してSeedメソッドをオーバライドします。 これで、モデルが変更になった際にはテーブルが再作成されて、合わせてSeedの中でセットしたデータが投入されます。*2

Models/MiniBlogSampleDbInitializer.cs
using System;
using System.Data.Entity;

namespace MiniBlogSample.Models
{
    public class MiniBlogSampleDbInitializer : DropCreateDatabaseIfModelChanges<MiniBlogSampleDbContext>
    {
        protected override void Seed(MiniBlogSampleDbContext context)
        {
            base.Seed(context);

            var message = new Message
            {
                Text = "Dummy_Text",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            };
            context.Messages.Add(message);
        }
    }
}

global.asax.csの中の

 Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MiniBlogSampleDbContext>());

 Database.SetInitializer(new MiniBlogSampleDbInitializer ());

に変更します。

次回はSetInitializerに追加になったMigration機能とテスト時のDB作成に便利なFabricatorについて記事にします。

*1:マスターデータなど

*2:モデルが再作成されないとセットされません