背景
Blazorアプリを使ってPostgreSQLを操作するアプリケーションを開発中です。
開発中に躓いたところを列記していきます。
前提条件
PostgreSQLデータベースは既存のものが既にあるものとします。
なので、既存のデータベースにアクセスして、エンティティを生成する方針で開発します。
.NetFrameworkは6.0, Nugetのバージョンは6.***を利用しています。
初期設定
プロジェクトの作成等は下記リンク参照。
既存のデータベースからマイグレーションする
プロジェクトを作成したらフレームワークのバージョンに合わせてNuGetパッケージをインストールします。
NuGetパッケージマネージャーコンソールを起動して下記コマンドを実行してください。
ビルドエラーが出ているとコンソールでエラーになるので、ビルドエラーは解消しておいてください。
Install-Package Npgsql.EntityFrameworkCore.PostgreSQL
Install-Package Microsoft.EntityFrameworkCore.Tools
続けて下記コマンドをパッケージマネージャーコンソールで実行します。
PostgreSQLの情報に合わせて適宜変更してください。
※-OutputDirで指定したディレクトリにエンティティが生成されます。
Scaffold-DbContext "Host=192.168.***.***;Database=YourDataBase;Username=postgres;Password=YourPass" Npgsql.EntityFrameworkCore.PostgreSQL -OutputDir Models
データベースの接続設定
Program.csにコネクションを追加します。
using BlazorApp1.Models;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// サービスの追加
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
// PostgreSQLのDbContextをサービスに追加
builder.Services.AddDbContext<YourContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
var app = builder.Build();
// ミドルウェアの設定
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
// PostgreSQLのDbContextをサービスに追加
builder.Services.AddDbContext<YourContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
appsettings.jsonファイルにデータベースサーバーへの接続情報を追記します。
PostgreSQLのデフォルトのポート指定であればポートの入力は不要です。
"ConnectionStrings": {
"DefaultConnection": "Host=192.168.1.141;Database=yours;Username=postgres;Password=***"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
"ConnectionStrings": {
"DefaultConnection": "Host=192.168.1.141;Database=yours;Username=postgres;Password=***"
},
***Context.csファイルを確認してみる
Modelsディレクトリに****Context.csファイルが生成されているので、ページ毎にこれを参照すると名前空間がインポートできます。
一旦****Context.csを確認してみます。
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
namespace BlazorApp1.Models
{
public partial class kdx_metalContext : DbContext
{
public kdx_metalContext()
{
}
public kdx_metalContext(DbContextOptions<kdx_metalContext> options)
: base(options)
{
}
public virtual DbSet<Core> Cores { get; set; } = null!;
public virtual DbSet<CysRef> CysRefs { get; set; } = null!;
public virtual DbSet<DefectivesRef> DefectivesRefs { get; set; } = null!;
public virtual DbSet<Employee> Employees { get; set; } = null!;
public virtual DbSet<EmployeeType> EmployeeTypes { get; set; } = null!;
public virtual DbSet<FrameStatesRef> FrameStatesRefs { get; set; } = null!;
public virtual DbSet<FramesRef> FramesRefs { get; set; } = null!;
public virtual DbSet<Material> Materials { get; set; } = null!;
public virtual DbSet<Message> Messages { get; set; } = null!;
public virtual DbSet<Mold> Molds { get; set; } = null!;
public virtual DbSet<Pattern> Patterns { get; set; } = null!;
public virtual DbSet<PatternMaterial> PatternMaterials { get; set; } = null!;
public virtual DbSet<PatternType> PatternTypes { get; set; } = null!;
public virtual DbSet<PlacesRef> PlacesRefs { get; set; } = null!;
public virtual DbSet<Pouring> Pourings { get; set; } = null!;
public virtual DbSet<PouringGate> PouringGates { get; set; } = null!;
public virtual DbSet<ProsRef> ProsRefs { get; set; } = null!;
public virtual DbSet<ProsTime> ProsTimes { get; set; } = null!;
public virtual DbSet<Shift> Shifts { get; set; } = null!;
public virtual DbSet<SpotsRef> SpotsRefs { get; set; } = null!;
マイグレーションの時点で、実際のデータベースにおけるテーブル名が、Blazorアプリの命名規則に準じて変更されてしまっていることが分かります。
その為、****Context.csの命名に従ってデータベース操作をする必要があります。
ページ側の設定
@inject ***Context DbContextで名前空間をインポートします。
@page "/"
@using BlazorApp1.Models
@inject ***Context DbContext
@using Microsoft.EntityFrameworkCore
<h3>Places Reference Data</h3>
@if (placesRefs == null)
{
<p>Loading...</p>
}
else if (placesRefs.Count == 0)
{
<p>No data available.</p>
}
else
{
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
@foreach (var place in placesRefs)
{
<tr>
<td>@place.Id</td>
<td>@place.Name</td>
</tr>
}
</tbody>
</table>
}
@code {
private List<PlacesRef> placesRefs;
protected override async Task OnInitializedAsync()
{
// データベースからplaces_refテーブルのデータを取得
placesRefs = await DbContext.PlacesRefs.ToListAsync();
}
}
DbContext.PlacesRefsでファイルで定義された命名に従ってテーブル操作が可能です。
ここでは、private List<PlacesRef> placesRefsという変数を宣言することで、構造体配列的にテーブル内のデータを扱うことができるようになっています。
.ToListAsync()で、DbContext.PlacesRefsのテーブルから変数に代入しています。
ロードが終わるまで(placesRefsがnullの間)はLoadingの文字が表示され、ロード後にテーブルリストのhtmlが表示される仕組みです。
テーブルのデータを書き換えてみる
Pagesディレクトリに新たにPatterns.razorファイルを作って、別のテーブルを参照し、更にデータの変更ができるようにしてみましょう。
@page "/patterns"
@using BlazorApp1.Models
@inject ***Context DbContext
@using Microsoft.EntityFrameworkCore
<h3>Patterns Data</h3>
@if (pattern == null)
{
<p>Loading...</p>
}
else if (pattern.Count == 0)
{
<p>No data available.</p>
}
else
{
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Nickname</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach (var item in pattern)
{
<tr>
<td>@item.Id</td>
<td>
<input @bind="@item.Nickname" class="form-control" />
</td>
<td>
<button class="btn btn-primary" @onclick="() => UpdateNickname(item)">Save</button>
</td>
</tr>
}
</tbody>
</table>
}
@code {
private List<Pattern> pattern;
protected override async Task OnInitializedAsync()
{
// 初期データを取得
pattern = await DbContext.Patterns
.OrderBy(p => p.Id) // ID順にソート
.ToListAsync();
}
// ニックネームの更新処理
private async Task UpdateNickname(Pattern updatedPattern)
{
// 変更されたエンティティをデータベースで更新
var patternToUpdate = await DbContext.Patterns.FindAsync(updatedPattern.Id);
if (patternToUpdate != null)
{
patternToUpdate.Nickname = updatedPattern.Nickname;
await DbContext.SaveChangesAsync(); // データベースを更新
}
}
}
Saveボタンが押されたときにデータベースに入力内容を反映させるようになっています。
NicknameをTEST FULL→TEST HENKOUに変更してみます。
データベース側でもデータの変更が確認できました。
ここでは端折っていますが、orderby句やwhere句を使ったソートやフィルタリングも可能でした。
// ニックネームの更新処理
private async Task UpdateNickname(Pattern updatedPattern)
{
// 変更されたエンティティをデータベースで更新
var patternToUpdate = await DbContext.Patterns.FindAsync(updatedPattern.Id);
if (patternToUpdate != null)
{
patternToUpdate.Nickname = updatedPattern.Nickname;
await DbContext.SaveChangesAsync(); // データベースを更新
}
}
まとめ
BlazorはNode.js環境と違ってIDEでの開発になるので、デバッグ含めてかなり楽でした。(Node.jsもIDEで開発できますが。。。)
初期設定のプロジェクトサンプルを使って、躓きながらも2時間程度でここまで開発できるのは凄い。
ボタンを使ったフィルタリング等を今後やっていきたいと思います。