サンプルデータベースの作成
さて、この図3のプログラムをベースに、冒頭の図1に示した帳票を出力するサンプルを組み込んでいきます。簡単に済ませるとすれば、「何かボタンをクリックすると、いつも決まった帳票が出力される」というサンプルを作ることですが、帳票出力といえば、「データベースに格納されたデータを取り出して帳票を作りたい」ということが、ほとんどではないでしょうか。
ここでは実用に寄せて、簡単なデータベースを作り、そこに格納されているデータを帳票出力するというサンプルを作ります。とはいえきちんとしたデータベースやページを作るとたいへんですし、それが本連載の目的ではないので、できるだけ簡素化します。
ここでは、マイクロソフト社の下記のページにある手順に則り、「モデルのクラスを作り、そこからデータベース、テーブル、そして、初期ページの自動生成」までを実施します。下記の工程では、サンプルとして不必要なページが作られますが、そのあたりは目をつむります。話を簡単にするため、データベースには、事前にインストールや初期設定をしておく必要がないSQLiteを使います。
【手順】サンプルデータベースの作成
[1]EF Coreツールなどのインストール
今回は、データベース操作に「Entity Framework(EF) Core」を使っていきます。そのためのツールやライブラリをインストールするため、次のコマンドを入力します。
dotnet tool install -g dotnet-aspnet-codegenerator dotnet tool install --global dotnet-ef dotnet add package Microsoft.EntityFrameworkCore.Design dotnet add package Microsoft.EntityFrameworkCore.SQLite dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
ツールは、ホームディレクトリの/.net/toolsフォルダにインストールされます。次のように入力して、パスを通しておきます。
export PATH="$PATH:/$HOME/.dotnet/tools"
[2]モデルを作る
ツールの準備が整ったら、まずは、テーブルの基となる「モデル」を作ります。ここでは、「注文(Order)」と「明細(Detail)」の親子関係を持つテーブルとします。exampleappフォルダの下にModelsフォルダを作り、その下にリスト2の内容で、Models.csファイルを作ります。
using System; using System.ComponentModel.DataAnnotations; namespace exampleapp.Models { public class Order { public int OrderID { get; set; } public string Zip { get; set;} public string Address1 {get; set;} public string Address2 {get; set;} public string Company {get; set;} public string Person {get; set;} public string Email {get; set;} [DataType(DataType.Date)] public DateTime OrderDate { get; set; } public ICollection<Detail> Details {get; set;} } public class Detail { public int ID { get; set; } public int OrderID {get; set;} public string ProductName { get; set;} public decimal UnitPrice {get; set;} public int Quantity {get; set;} } }
EF Coreで扱う場合、プロパティに命名規則があります。ここでは、次の命名規則に従っています。
- 主キー:「ID」や「クラス名+ID」を付けると、それは主キーとして扱われます(「OrderID列」および「ID列」)。
- リレーション:親子関係は、「ICollection<クラス名>」で構成します(OrderとDetailの関係)。子が親を参照する外部キーは、「クラス名+ID」で命名します(DetailクラスのOrderIDプロパティ)。
[3]ページを自動生成する
このモデル定義から、データベースへの接続定義や一覧や編集、削除のページなど、必要なコード一式を自動で生成します。
まずは、Orderテーブルについて、次のようにして作成します(ここでは「Pages/Orders」のように、-outDirオプションを「Orders」としている点に注意してください。Ordersである必要はないのですが、「Order」のようにモデルのクラス名と同じにすると「ページ名」と「モデルのクラス名」が同名になり、ビルド時にエラーが発生します)。
dotnet-aspnet-codegenerator razorpage -m Order -dc MyAppContext -udl -outDir Pages/Orders --referenceScriptLibraries -sqlite
同様にして、Detailテーブルについて作成します。
$HOME/.dotnet/tools/dotnet-aspnet-codegenerator razorpage -m Detail -dc MyAppContext
[4]データベースやテーブルを作る
これでPagesフォルダ以下に、「Orders」と「Details」のフォルダができ「一覧(Index)」「新規作成(Create)」「編集(Edit)」「削除(Delete)」の4つのページが作成されます。しかしデータベースもテーブルもまだないので、実行して開いてもエラーが発生するだけです。そこでデータベースとテーブルを作成していきましょう。
EF Coreには、このモデルから、自動でデータベースやテーブル自体を作成するマイグレーション機能があります。まず、次のように入力します。
dotnet ef migrations add InitialCreate
するとMigrationディレクトリ以下に、データベースやテーブルを作成するためのプログラムが自動生成されます。これをデータベースに反映するため、下記のコマンドを入力します。このコマンド入力によって、実際にデータベースとテーブルが作成されます。
dotnet ef database update
[5]動作確認する
この段階で動作確認しておきます。ビルドして起動します。
dotnet publish -c Release -o published
dotnet published/exampleapp.dll
ブラウザで、「http://localhost:5000/Orders/」を開きます。すると、Orderテーブルの一覧が表示されます(図5)。ここで[Create New]のリンクをクリックすると、新しい注文を作成できますが、何もしないでおいてください(以下の手順では、「何もレコードが存在しないとき」に限って、初期データを作成するプログラムを作るため、何かデータを入れると次の手順で失敗します)。
初期データの投入
まだテーブルの中身が空なので、サンプルのデータを投入します。いくつかのやり方がありますが、ここでは「何もレコードが存在しないとき」に限って、初期データを作成するプログラムを作ります。
あまり手間をかけたくないので、Program.csをリスト3のように書き換えます。書き換えた箇所には「★印」を付けました。
using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; // ★追加★ using exampleapp.Models; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddDbContext<MyAppContext>(options => options.UseSqlite(builder.Configuration.GetConnectionString("MyAppContext"))); var app = builder.Build(); // ★初期化データ投入 ここから★ using (var scope = app.Services.CreateScope()){ var services = scope.ServiceProvider; using (var context = services.GetRequiredService<MyAppContext>()) { if (!await context.Order.AnyAsync()) { var order = new Order { Zip = "123-4567", Address1 = "東京都新宿区", Address2 = "1-2-3 ●●ビル", Company = "株式会社●●", Person = "山田一郎", Email = "yamada@example.co.jp", OrderDate = DateTime.Today, Details = new List<Detail> { new Detail{ ProductName = "製品A", UnitPrice = 100, Quantity = 5}, new Detail{ ProductName = "製品B", UnitPrice = 300, Quantity = 3}, new Detail{ ProductName = "製品C", UnitPrice = 400, Quantity = 1}, } }; context.Order.Add(order); await context.SaveChangesAsync(); } } } // ★追加ここまで★ // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapRazorPages(); app.Run();
このように修正してビルドして実行し、先ほどと同様に「http://localhost:5000/Orders/」を確認すると、初期データが入っていることがわかります(図6)。図6には表示されていませんが、実際には明細も入っています。