.NET Framework 4.5 と Entity Framework コードファーストを利用するアプリでの DB 構築と項目の追加(2)

(その1)でコードファーストによる DB 作成まで終わったので、その続きを書きます。

残っている手順は、次のものです。

  1. 稼働環境の DB の更新確認用に実行ファイルなどを適当なフォルダへコピーしておく
  2. Code First Migrations の設定
  3. モデルの変更とそれに伴う利用側の変更
  4. 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 でも同様です。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です