(その1)でコードファーストによる DB 作成まで終わったので、その続きを書きます。
残っている手順は、次のものです。
- 稼働環境の DB の更新確認用に実行ファイルなどを適当なフォルダへコピーしておく
- Code First Migrations の設定
- モデルの変更とそれに伴う利用側の変更
- DB への反映
それでは、「稼働環境の DB の更新確認用に実行ファイルなどを適当なフォルダにコピーしておく」ですが、コピー対象のファイルは「EntityFramework.dll」「ProductVupWithSdfTest004.exe」「ProductVupWithSdfTest004.exe.config」です。ViewModelBase を DLL 化した場合には「ViewModelBase.dll」もコピーしてください(「ProductVupWithSdfTest004.exe.config」のコピーを忘れると、App.config で行った接続文字列の設定が参照されず、SQL Server Compact への接続が行われないので注意 😉 )。コピーした「ProductVupWithSdfTest004.exe」を動かすと DB が作成されます。
次に「Code First Migrations の設定」を行なっていきます。
まずはマイグレーションを有効化させます。
Visual Studio のメニューから「ツール」・「ライブラリ パッケージ マネージャー」・「パッケージ マネージャー コンソール」と選択していきます。表示されるパッケージマネージャー コンソールで、「Enable-Migrations」コマンドを入力します。
マイグレーションが有効化されると、プロジェクトに Migrations フォルダが作成され、Configuration.cs と xxxx_InitialCreate.cs (xxxx は日付などの数字) が格納されます。
次に、「モデルの変更とそれに伴う利用側の変更」ですが、方針として「オートマチック・マイグレーションは行わない」こととします。モデルの変更は、以前書いたものに合わせて、Customers テーブルに GroupId フィールドを、Products テーブルに Price フィールドをそれぞれ追加することにします。
Customer クラス
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ProductVupWithSdfTest004.Models
{
public class Customer
{
public int CustomerId { get; set; }
[MaxLength(50)]
public string CustomerName { get; set; }
public int GroupId { get; set; }
public virtual List<Sale> Sales { get; set; }
}
}
Product クラス
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ProductVupWithSdfTest004.Models
{
public class Product
{
public int ProductId { get; set; }
[MaxLength(50)]
public string ProductName { get; set; }
public decimal Price { get; set; }
public virtual List<Sale> Sales { get; set; }
}
}
次に利用側の変更ということで、MainWindowViewModel クラスです。
using System.Text;
using System.Windows.Input;
using MakCraft.ViewModels;
using ProductVupWithSdfTest004.DAL;
using ProductVupWithSdfTest004.Models;
namespace ProductVupWithSdfTest004.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
private string _result;
public string Result {
get { return _result; }
set
{
_result = value;
base.RaisePropertyChanged(() => Result);
}
}
private void addCommandExecute()
{
using (var repository = new SalesRepository())
{
addCommandExecute(repository);
}
}
private void addCommandExecute(ISalesRepository repository)
{
var customer = repository.AddCustomer(new Customer { CustomerName = "カスタマー2", GroupId = 2 });
var product = repository.AddProduct(new Product { ProductName = "商品2", Price = 500 });
repository.AddSale(new Sale { Customer = customer, Product = product, Quantity = 20 });
repository.Save();
}
private ICommand addCommand;
public ICommand AddCommand
{
get
{
if (addCommand == null)
addCommand = new RelayCommand(
param => addCommandExecute()
);
return addCommand;
}
}
private void findCustomersCommandExecute()
{
using (var repository = new SalesRepository())
{
findCustomersCommandExecute(repository);
}
}
private void findCustomersCommandExecute(ISalesRepository reposirory)
{
var customers = reposirory.FindCustomers();
var builder = new StringBuilder();
customers.ForEach(w => builder.Append(
string.Format("ID: {0}, Customer: {1}, Group ID: {2}\r\n",
w.CustomerId, w.CustomerName, w.GroupId)));
Result = builder.ToString();
}
private ICommand findCustomersCommand;
public ICommand FindCustomersCommand
{
get
{
if (findCustomersCommand == null)
findCustomersCommand = new RelayCommand(
param => findCustomersCommandExecute()
);
return findCustomersCommand;
}
}
private void findSalesCommandExecute()
{
using (var repository = new SalesRepository())
{
findSalesCommandExecute(repository);
}
}
private void findSalesCommandExecute(ISalesRepository repository)
{
var sales = repository.FindSales();
var builder = new StringBuilder();
sales.ForEach(sale => builder.Append(
string.Format("ID: {0}, Customer: {1}, Product: {2}, Price: {3}, Quantity: {4}\r\n",
sale.SaleId, sale.Customer.CustomerName, sale.Product.ProductName,
sale.Product.Price, sale.Quantity)));
Result = builder.ToString();
}
private ICommand findSalesCommand;
public ICommand FindSalesCommand
{
get
{
if (findSalesCommand == null)
findSalesCommand = new RelayCommand(
param => findSalesCommandExecute()
);
return findSalesCommand;
}
}
}
}
変更を行ったのは、「addCommandExecute(ISalesRepository repository) メソッドの各テーブルの登録データ」「findCustomersCommandExecute(ISalesRepository reposirory) メソッドの表示用データの作成部分」「findSalesCommandExecute(ISalesRepository repository) メソッドの表示用データの作成部分」です。
次に DB への反映ということで、まずは開発環境の DB がコマンドで変更されることを確認し、続いて初期状態に戻してからプログラムを動かし DB が動的に変更されることを確認します。次に、実行ファイルをコピーして実環境での DB 変更も確認してみます。
開発環境の DB の変更です。
手順としては Add-Migration コマンドでスクリプトを生成し、Update-Database コマンドで DB を更新するという順番になります。生成するスクリプトの名前は「AddTest」とします。
パッケージマネージャー コンソールで、「Add-Migration AddTest」コマンドを入力します。
コマンドが終了すると Migrations フォルダに「xxxx_AddTest.cs」というファイル名でスクリプトファイルが作成されます。
ここで、既存レコードの GroupId には 3 をセットするようにカスタマイズします。
生成された「xxxx_AddTest.cs」ファイルを修正します。
namespace ProductVupWithSdfTest004.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddTest : DbMigration
{
public override void Up()
{
AddColumn("dbo.Customers", "GroupId", c => c.Int(nullable: false, defaultValue: 3));
AddColumn("dbo.Products", "Price", c => c.Decimal(nullable: false, precision: 18, scale: 2));
}
public override void Down()
{
DropColumn("dbo.Products", "Price");
DropColumn("dbo.Customers", "GroupId");
}
}
}
GroupId フィールドの追加の行に「defaultValue: 3」の指定を追加しました。
次に DB をコマンドで変更します。
パッケージマネージャー コンソールで、「Update-Database」コマンドを入力します。
データベースエクスプローラーで SampleModifyDatabase.sdf の Customers テーブルのデータを表示すると、GroupId フィールドが追加され、値に 3 がセットされているのがわかります。
開発環境の DB がコマンドで変更できることが確認できたので、次に初期状態に戻してからプログラムを動かし DB が動的に変更されることを確認します。
パッケージマネージャー コンソールで、「Update-Database -TargetMigration: InitialCreate」コマンドを入力します。
データベースエクスプローラーで SampleModifyDatabase.sdf の Customers テーブルのデータを表示すると、GroupId フィールドが無くなっているのが確認できます(「最新の状態に更新」を行うのを忘れずに 🙂 )。データベースエクスプローラーを開いたついでに、先ほどのデバッグ実行時に追加したデータを削除しておくと、次の動作テスト時に同じデータが追加される見た目の悪さを防げます 😆 (削除する時には Sales テーブルから行いましょう 😛 )
次に、自動アップデートの設定を行います。
App.xaml.cs に設定し、アプリケーションの開始時に自動アップデートが行われるようにします。
using System.Data.Entity;
using System.Windows;
using ProductVupWithSdfTest004.DAL;
using ProductVupWithSdfTest004.Migrations;
namespace ProductVupWithSdfTest004
{
/// <summary>
/// App.xaml の相互作用ロジック
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Database.SetInitializer(new MigrateDatabaseToLatestVersion<CreateModifyContext, Configuration>());
}
}
}
これでデバッグ実行すると DB が自動アップデートされていることが確認できます。
開発環境の自動アップデートまで確認できたので、次は、実環境での DB 変更の確認です。
先ほどファイルをコピーしておいたフォルダへ「ProductVupWithSdfTest004.exe」をコピーします。コピーしたファイルを実行すると、問題なく追加したフィールドを扱えて、データの追加・表示が行えることが確認できます。Visual Studio のデータベースエクスプローラーでテーブルを見ると、GroupId フィールと Price フィールドがそれぞれ追加されていることが確認できます。
以上、WPF 以前からの流れで WPF を使用して Entity Framework Code First Migrations の動作確認を行いましたが、基本的にはコンソール・アプリケーションでも ASP.NET MVC でも同様です。