Windows 8 アプリとデータベース/サービスとの連携 – ① データモデル作成とデータベース生成
皆様、こんにちは!今期(7月以降)初めての投稿となります。今年もよろしくお願いします!
9/2(火) タッチ/ペン対応アプリの開発とクラウドを活用した既存サービスとの接続方法のセミナーにご参加いただきました皆様、お忙しいところありがとうございました。今後も、皆様の関心のあるテーマを中心にセミナーの企画とセッションのご提供をして行きたいと考えていますので、今後ともよろしくお願いします。
さて、早速、お約束した通り、私が担当しましたこのセッションの詳細情報とコードにつき、ご紹介していきたいと思います。
概要
Windows ストアアプリからデータベースへの連携を行う場合には、直接のデータ連携はできず、Web サービス経由で行う必要があります。ここでは、SQL Server Express Edition 上に生成されたデータベースを、ASP.NET Web APIから、REST インターフェースとJSON形式のペイロードを使用して公開し、それをWindows ストアアプリから利用するシナリオをご紹介します。
ADO.NET Entity Framework 5.0以降で実装された、Code First の機能を使い、Visual Studio 上のソースコードをビルドすることにより、SQL Server Express Edition 上に、各カラムの属性やリレーションシップ等のメタ情報を含めつつデータベースを生成することが可能です。また、Migration 機能により、カラムの追加等のデータベースへの変更を、データに影響を与えることなく行うことが可能です。ここで出来上がったデータベースを、ASP.NET Web API を使い、REST のインターフェースで公開します。
ここでは、商品管理用のWindows ストアアプリを実装します。これらの機能を実現するために、SQL Server Express Edition 、ADO.NET Entity Framework 6.0、Code First、ASP.NET Web API、そして Windows ストアアプリのテンプレートの各技術の基本的な使い方をご紹介します。
このエントリは、① バックエンドサービス作成 ① -1 データモデル作成とデータベース生成について、ご紹介します。
必要なソフトウェア他
· Microsoft Windows 8.1
· Microsoft Visual Studio 2013
· SQL Server 2012 Express Edition
サンプル:タイムセール商品管理の構成
業務用のWindows ストアアプリ(以下、ビジネスストアアプリ)では、企業内外のデータソースへのアクセスが重要となります。ただ、Windows ストアアプリの場合、データベースに直接接続はできず、Web サービスを経由して接続することになります。
今回作成するビジネス Windows ストアアプリ
スーパーマーケットのタイムセールを管理するストアアプリとなります。アーキテクチャー的には、このバックエンドサービスは、Windows サーバーでも、Windows Azure でもほぼ同じコードで配置可能となります。TimeSaleStoreAppと呼ばれる、商品情報と商品売価を管理するアプリケーションに、全ての機能を統合します。Windows ストアアプリとして、デザイン要件を満たす必要があるほか、データとデータ ソースへの接続が必要です。マイクロソフトの最新の O/RM フレームワークである、Entity Framework 6.0 を使用してデータモデルを実装します。データモデルが作成できたら、ASP.NET Web API を介し、REST サービスを通じて利用できるリモートデータを使用して、クライアント側(Windows ビジネスストアアプリ)の開発が可能になります。最後にWindows ビジネスストアアプリにデータを提示するための UI を開発します。Visual Studio 2013 に準備されているWindows ストアアプリ用のテンプレートを使い、C#/XAML で開発します。HttpClient を使います。また、共有コントラクトを実装し、このアプリに Windows 8.1 の共有機能を統合します。
データベースのモデル
今回のアプリのデータベースのモデルは、スーパーマーケットの食料品の商品の売価管理のサンプルで、非常に簡単です。1つのテーブルの中に、Product(商品)とProductPrice(商品の売価)を持っています。商品の売価は、価格設定日が一番最近のものが反映される、という仕組みで、過去の売価設定の履歴を全部持っているという仕様です。
店舗 |
売価変更時刻 |
商品 |
売価 |
変更の理由 |
決定した日付時刻 |
担当者 |
Code Firstアプローチ
まずはデータベースモデルを実装するために、Entity Framework 6.0 および Code First の技術を使用します。Visual Studio 2013 を起動し、新しいプロジェクト-> Windows -> クラスライブラリを選択します。
Code First とは、既存の Database First パターンや Model First パターンの代わりとなる、Entity Framework の新しい開発パターンです。Code First により、.NET の CLR クラスを使用してモデルを定義できるようになります。
その後、これらの CLR クラスを既存のデータベースにマップしたり、CLR クラスを使用してデータベース スキーマを生成したりすることができます。したがって、SQL データベースは、このセクションの最後に作成されます。
適当な名前(ここではTimeSale)で空のクラスライブラリプロジェクトを作成したら、エンティティの実装に入ります。先ほどのER 図に示した通り、1つの .NET クラスを作ります。ProductwithPriceです。そのために必要な手順は下記の通りです。
クラスライブラリプロジェクトの作成
最初に、Entity Framework 6.0 を追加します。クラスを実装する前に、Entity Framework への参照を追加する必要があります。現在、Entity Framework は .NET から分離されていますので、NuGet を使ってこれを行います。参照設定を右クリックして、NuGet パッケージの管理をクリックします。
検索ボックスで、Entity Framework とタイプして、6.0が見つかったらエンターキーを押します。当該パッケージがリストに追加されます。インストールボタンをクリックしてインストールしてください。下の図は、NuGet パッケージの管理ダイアログボックスです。License Agreement に同意して、ダイアログボックスを閉じます。
次に、クラスを追加します。プロジェクトを右クリック→新しいクラスを追加、で行います。
ProductwithPrice: 店舗ID ( Key )、店舗、売価変更時刻、商品、売価、変更の理由、決定した日付時刻、担当
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Threading.Tasks;
6: using System.ComponentModel.DataAnnotations;
7:
8: namespace TimeSale
9: {
10: public partial class ProductwithPrice
11: {
12: [Key]
13: public int StoreId { get; set; }
14: public string StoreName { get; set; }
15: public string PriceChangedDate { get; set; }
16: public string ProductName { get; set; }
17: public string RetailPrice { get; set; }
18: public string ReasonforChangePrice { get; set; }
19: public string PriceFixedDate { get; set; }
20: public string Contact { get; set; }
21: }
22: }
Primary Key を設定するには、System.ComponentModel.DataAnnotationsをusing 句で追加し、 [Key] のメタタグを付けます。
DB コンテキストの作成
これでエンティティができましたので、DBContext を作成します。これにより、データベースに対する全てのCRUD(読み取り・更新・追加・削除)の処理が管理できます。
TimeSaleApp.cs ファイル追加と DataContext クラスの実装
DBContextクラスを下記の通り実装します。最初に、System.Data.Entityをusing 句に追加しておきます。
1: using System;
2: using System.Collections.Generic;
3: using System.Data.Entity;
4: using System.Linq;
5: using System.Text;
6: using System.Threading.Tasks;
7:
8: namespace TimeSale
9: {
10: public class TimeSaleAppContext:DbContext
11: {
12: public TimeSaleAppContext() :
13: base("productwithprice_db")
14: {
15: Configuration.LazyLoadingEnabled = false;
16: }
17:
18: //Entity セット
19: public DbSet<ProductwithPrice> ProductwithPrices
20: { get; set; }
21: }
22: }
新しいアイテムの追加 : SQL Server への接続文字列を追加
実装の最後に、SQL Server への接続文字列を追加する必要があります。接続文字列は、DBContext の実装で決定したものと、完全に同じ名前である必要があります。そうでない場合には、データベースは生成されませんので、ご注意ください。
App.config ファイルを開き、内容をすべて選択して(Ctrl + A)削除し、下記内容を追加します(ConnectionString)。
1: <?xml version="1.0" encoding="utf-8"?>
2: <configuration>
3: <connectionStrings>
4: <add name="productwithprice_db"
5: providerName="System.Data.SqlClient"
6: connectionString="Data Source=.\sqlexpress;Initial
7: Catalog=productdb;Integrated Security=True"/>
8: </connectionStrings>
9: </configuration>
SQL インスタンス名に基づいて、ConnectionString は適宜変更してください。この時点ではデータベースは SQL Server 上には存在していません。もし当該 SQL Server 上に同じ名前のデータベースがある場合、Entity Framework はその既存のデータベースに、テーブルを追加します。
テストプロジェクトの作成とデータの作成
次に、テストをして確認と、データの追加をしておきましょう。コンソールアプリケーションを作成し、正しいモデルが実装されたかを確認します。ファイル->追加->新しいプロジェクトを、順にクリックします。コンソールアプリケーションを選択し、名前を適当に(例:TimeSaleTest)つけてOKをクリックします。
先述のTimeSaleプロジェクト同様の手順で、まずは、Entity Framework を NuGet から取得して追加します。次いで、参照設定を右クリックし、TimeSaleプロジェクトを参照します。 Program.cs ファイルを開いて、下記の通り実装します:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Threading.Tasks;
6: using TimeSale;
7:
8: namespace TimeSaleTest
9: {
10: class Program
11: {
12: static void Main(string[] args)
13: {
14: TimeSaleAppContext tsc = new TimeSaleAppContext();
15:
16: var productwithprice = new ProductwithPrice()
17: {
18: StoreId = 1,
19: StoreName = "品川店",
20: PriceChangedDate = "2013/1/16 9:00",
21: ProductName = "白菜1個",
22: RetailPrice = "300",
23: ReasonforChangePrice = "通常価格",
24: PriceFixedDate = "2013/1/15 18:00",
25: Contact = "野菜担当 山田"
26: };
27:
28: var productwithprice2 = new ProductwithPrice()
29: {
30: StoreId = 2,
31: StoreName = "白金高輪店",
32: PriceChangedDate = "2013/1/16 16:00",
33: ProductName = "ブドウ1房",
34: RetailPrice = "650",
35: ReasonforChangePrice = "夕方のタイムセール",
36: PriceFixedDate = "2013/1/16 12:00",
37: Contact = "店長 鈴木"
38: };
39:
40: var productwithprice3 = new ProductwithPrice()
41: {
42: StoreId = 3,
43: StoreName = "五反田店",
44: PriceChangedDate = "2013/1/16 18:00",
45: ProductName = "トマト3個",
46: RetailPrice = "150",
47: ReasonforChangePrice = "タイムセール終了",
48: PriceFixedDate = "2013/1/16 12:00",
49: Contact = "店長 鈴木"
50: };
51:
52: var productwithprice4 = new ProductwithPrice()
53: {
54: StoreId = 4,
55: StoreName = "目黒店",
56: PriceChangedDate = "2013/1/17 9:00",
57: ProductName = "玉ねぎ3つ",
58: RetailPrice = "260",
59: ReasonforChangePrice = "木曜セール",
60: PriceFixedDate = "2013/1/16 18:00",
61: Contact = "野菜担当 山田"
62: };
63:
64: var productwithprice5 = new ProductwithPrice()
65: {
66: StoreId = 5,
67: StoreName = "渋谷店",
68: PriceChangedDate = "2013/1/17 9:00",
69: ProductName = "人参3本",
70: RetailPrice = "260",
71: ReasonforChangePrice = "木曜セール",
72: PriceFixedDate = "2013/1/16 18:00",
73: Contact = "野菜担当 青木"
74: };
75:
76: var productwithprice6 = new ProductwithPrice()
77: {
78: StoreId = 6,
79: StoreName = "川崎店",
80: PriceChangedDate = "2013/1/17 9:00",
81: ProductName = "玉ねぎ3本",
82: RetailPrice = "240",
83: ReasonforChangePrice = "タイムセール終了",
84: PriceFixedDate = "2013/1/16 18:00",
85: Contact = "野菜担当 金子"
86: };
87:
88: var productwithprice7 = new ProductwithPrice()
89: {
90: StoreId = 1,
91: StoreName = "品川店",
92: PriceChangedDate = "2013/1/17 9:00",
93: ProductName = "ながのパープル1房",
94: RetailPrice = "700",
95: ReasonforChangePrice = "タイムセール終了",
96: PriceFixedDate = "2013/1/16 18:00",
97: Contact = "野菜担当 藤田"
98: };
99:
100: var productwithprice8 = new ProductwithPrice()
101: {
102: StoreId = 5,
103: StoreName = "目黒店",
104: PriceChangedDate = "2013/1/17 9:00",
105: ProductName = "玉ねぎ3本",
106: RetailPrice = "240",
107: ReasonforChangePrice = "木曜タイムセール",
108: PriceFixedDate = "2013/1/16 18:00",
109: Contact = "野菜担当 山田"
110: };
111:
112: var productwithprice9 = new ProductwithPrice()
113: {
114: StoreId = 6,
115: StoreName = "川崎店",
116: PriceChangedDate = "2013/1/17 9:00",
117: ProductName = "玉ねぎ3本",
118: RetailPrice = "240",
119: ReasonforChangePrice = "木曜タイムセール",
120: PriceFixedDate = "2013/1/16 18:00",
121: Contact = "野菜担当 金子"
122: };
123:
124: tsc.ProductwithPrices.Add(productwithprice);
125: tsc.ProductwithPrices.Add(productwithprice2);
126: tsc.ProductwithPrices.Add(productwithprice3);
127: tsc.ProductwithPrices.Add(productwithprice4);
128: tsc.ProductwithPrices.Add(productwithprice5);
129: tsc.ProductwithPrices.Add(productwithprice6);
130: tsc.ProductwithPrices.Add(productwithprice7);
131: tsc.ProductwithPrices.Add(productwithprice8);
132: tsc.ProductwithPrices.Add(productwithprice9);
133:
134: tsc.SaveChanges();
135:
136: var pdct = tsc.ProductwithPrices.FirstOrDefault();
137: Console.WriteLine("Products from Database");
138: Console.WriteLine("9レコード追加しました。最初の商品名は:");
139: Console.WriteLine("Products from database Name:{0}",pdct.ProductName);
140: Console.Read();
141:
142: }
143: }
TimeSaleTestをスタートアッププロジェクトに設定し、開始ボタンを押して実行します。コンソールアプリケーションが立ち上がり、エンティティが無事にデータベースに作成されたことがわかります。
すなわち、productwithprice_db が SQL Server 上に作られ、エンティティおよびデータは、この TimeSaleTest プログラムから作成されているということになります。
SQL Server Management Studio を立ち上げ、ローカルのExpress Edition にログインします。
新しいクエリ、をクリックし、下記の通り入力して実行します。
1: select * from [ProductswithPrice].[dbo].[ProductwithPrices]
すると、下記のような結果が返されます。9件のレコードが確認できます。
Code First アプローチは、このようにきわめて強力です。ConnectionString は修正する必要がありますが※、それ以外は Model First アプローチによりコードで書いた通りに稼働します。
※ 接続先を最初から Microsoft Azure SQL Database にしておけば、その上にデータベースが作成されます。
以上です。次は、Web サービス作成とデータ公開、のところを解説します。
鈴木 章太郎