(16/30)みんなでBlazorを学ぼう:データベースを作成する

(16/30)みんなでBlazorを学ぼう:データベースを作成する

これで基本的なログはできましたが、入力するたびにページを再読み込みするとデータがリセットされてしまいます。これらのデータはブラウザにしか存在せず、実際にデータベースに保存されていないからです。保存するために、データベースに接続します。

最終更新 2021/12/18 23:34
StrayaWorker
読了目安 3 分
カテゴリ
Blazor
テーマ
みんなでBlazorを学ぼうシリーズ
タグ
.NET C# ASP.NET Core Blazor

基本的なログは得られるようになりましたが、毎回入力後にページをリロードするとデータがリセットされてしまいます。これはデータがブラウザ上にしか存在せず、実際にデータベースに保存されていないためです。データを永続化するために、データベースに接続する必要があります。

(注:Blazor WebAssembly はデータベースと直接やりとりする機能を持っていませんが、Microsoft は ASP.NET Core hosted オプションを提供しており、Blazor WebAssembly のプロジェクト作成時に同時に ASP.NET Core Web API プロジェクトをセットアップできます)

Entity Framework Core

まず appsettings.json に接続文字列を追加します。テスト用なので、SqlServer はローカルデータベースを使用します。

{
  "ConnectionStrings": {
    "DBConnection": "Server=(localdb)\\MSSQLLocalDB;database=Blog;integrated security=true;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

次に NuGet から2つのパッケージ Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.Tools をダウンロードします。これらのパッケージは SqlServer とやりとりするための ORM コンポーネントです。ORM とは何でしょうか?

プロジェクト構成ファイル:BlazorServer.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">

	<PropertyGroup>
		<TargetFramework>net6.0</TargetFramework>
		<Nullable>enable</Nullable>
		<ImplicitUsings>enable</ImplicitUsings>
	</PropertyGroup>

	<ItemGroup>
	  <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.1" />
	  <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.1">
	    <PrivateAssets>all</PrivateAssets>
	    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	  </PackageReference>
	</ItemGroup>

</Project>

昔のプログラム開発者は SqlServer からデータを取得するために SQL 文 を学ぶ必要がありました。その後、.NET プラットフォーム(VBC# を含む)は ADO.NET というデータ取得の優れたツールを開発し、データを取得した後、プログラムと一対一で マッピング していました。しかし ADO.NET のデータマッピングは期待通りではなく、Microsoft は Entity Framework をリリースしました。これが初期の ORM(Object-Relational Mapper) であり、開発者はデータを エンティティ として扱い、実際に SQL 文 に触れる必要がなくなり、デザインツール(Designer)も提供されてプログラム開発者がより容易に操作できるようになりました。

しかし、Entity Framework は重すぎると感じる人もおり、より軽量なツールである Dapper に移行しました。筆者も Dapper を使ったことがありますが、自分で SQL 文 を組み立てるのが好きな人には確かに便利だと感じます。

そして .NET Core というクロスプラットフォーム版の ORM では、Entity Framework Core を使用してデータマッピングの処理を行います。

パッケージのインストール後、AppDbContext というクラスを追加し、DbContext を継承します。コンストラクタ内で DbContextOptions<AppDbContext> options を基底クラス(つまり DbContext)に渡します。

AppDbContext.cs

using Microsoft.EntityFrameworkCore;

namespace BlazorServer.Models;

public class AppDbContext : DbContext
{
	public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
	{
	}

	public DbSet<BlogModel>? Blogs { get; set; }

	public DbSet<PostModel>? Posts { get; set; }
}

次に Program.cs を開き、データベースを使用するための登録を行います。ここでは UseSqlServer() を使用していますが、もちろん UseMysql()UseOracle() も利用可能で、対応するパッケージをインストールすれば使えます。

パッケージマネージャーコンソール を開き、コマンド Add-Migration Init を入力して Entity Framework Core にデータベースマイグレーションを作成するよう指示します。すると、マイグレーションディレクトリ Migration が生成されます。これは Entity Framework Core の特徴であり、プログラムとデータベースの間に中間的な Migration を作成します。また、Migration の大きな利点は、確定する前であれば何度でも修正でき、確定後にデータベースを更新できることです。

Blog と Post の関係

次に PostBlogId を追加します。BlogPost に対して 主テーブル であり 子テーブル です。マイグレーションを追加する際に主テーブルとの関連(つまり主テーブルの外部キーが子テーブルを参照する)を追加していなくても、EF Core は賢く自動的に追加しますが、その場合のフィールド名は Model+Key の形式(BlogModelBlogId)になります。そこで名前を簡略化しましょう。

まず PostModel に2つのプロパティ BlogIdBlog を追加します。必ずこのように追加してください。BlogId はデータベースに保存するためのもので、Blog はデータを関連付ける際にエンティティとして使用でき、自分でテーブルを JOIN する必要がなくなります。詳細は後述します。

次に Remove-Migration を使用して元の Migration を削除し、再度 Migration を追加します。コマンド Add-Migration Init を実行すると、新しい Migration では簡略化された名前 BlogId になっていることがわかります。この時点でデータベースを更新します。

コマンド Update-Database を実行してデータベースを更新し、SQL Server オブジェクトエクスプローラー を開くと、先ほど作成したデータベース Blog が見つかります。データベース名は接続文字列の database の値に基づいています。データベースが作成されていることが確認でき、この過程で SQL 文や SSMS のインターフェースは使用していません。

ただし、一点注意が必要です。途中でデータベースを変更すると、元の Migration が問題を起こす可能性が非常に高くなります。各データベースのデータ型には違いがあるため、最初にどのデータベースを使用するかを計画しておくのが最善です。

AddDbContext<T> という最も一般的な方法の他に、AddDbContextFactory<T> という個別のコンポーネントで新しい DbContext を生成する方法もあります。AddDbContext<T> のライフサイクルは scoped であり、Blazor Server の場合、システムを閉じない限り DbContext は破棄されません。ライフサイクルをコンポーネントのみに限定したい場合には、AddDbContextFactory<T> を使用できます。

参照:

  1. Entity Framework
  2. Dapper
  3. Database Providers
  4. New DbContext instances

注:この記事のコードは .NET 6 + Visual Studio 2022 でリファクタリングされています。原文リンクとリファクタリング後のコードを比較して学習できます。ご愛読ありがとうございます。原作者をサポートしてください。

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2021/12/25

(29/30)みんなで学ぶBlazor:Blazor単体テスト

システム開発において最も退屈なプロセスは、おそらくバグ修正です。特に、null オブジェクトにアクセスしようとするエラー(`Object reference not set to an instance of an object.`)は、多くの初心者が最初に直面する問題です。退屈なバグ修正から解放されるために、この記事では「単体テスト」を紹介します。

続きを読む
同じカテゴリ / 同じタグ 2021/12/25

(28/30)みんなで学ぶBlazor:ポリシーベースの認可

以前に「ASP.NET Core Identity」は「Claim」ベースの検証を使用すると述べましたが、実は「ASP.NET Core Identity」には異なる種類の認可方法があります。最も簡単な「ログイン認可」「ロール認可」「Claim認可」ですが、これらはすべて同じ方法で実現されています:原則認可(ポリシーベースの認可)です。

続きを読む