Entity Framework Core を利用してシンプルなカード型データベース編集アプリケーションを作成する - ASP.NET Core

Entity Framework Core を利用したシンプルなカード型データベース編集アプリケーションのコードを紹介します。

概要

こちらの記事では、ASP.NET Coreでカード型のデータベースアプリケーションを作成する手順を紹介しました。 この記事では、Entity Framework Core を利用して、カード型のデータベースアプリケーションを作成する手順とコードを紹介します。

カード型のデータベース変更アプリケーションは利用する機会も多いため、レコードの表示、編集、追加、削除を合わせたプログラム全体を紹介します。

データベース

以下のテーブルを作成してデータを作成します。
ProductsB テーブル
idmodelnameclasscategoryprice
1C-XM01モーダンチェアホームチェア56000
2X-XD05ラージデスクオフィステーブル87000
3A-DA40ラウンドダイニングチェアホームチェア28000
4O-XX100ナチュラルオフィスオフィスチェア13800
5R-D400ラウンドダイニングテーブルホームテーブル128000
6R7000ウッドキャビネットオフィスその他32000
7B-200リネンベッドホームベッド184500
8B-250ホワイトダブルベッドホームベッド324850
9W-80ワーキングチェアオフィスチェア45000
10EG-10Xエルゴノミクスデスクオフィステーブル88500
11OC-908オーガニックチェアホームチェア56000

プログラム

ASP.NET Core アプリケーションのプロジェクトを作成します。今回は空のプロジェクトから作成しています。

事前準備

NuGetを利用して、Entity Framework Core のパッケージをインストールします。

Entity Framework Coreのインストール

Entity Framework CoreとEntity Framework Core SQL Server をインストールします。 詳細な手順はこちらの記事を参照してください。

Entity Framework Core Design のインストール

Microsoft.EntityFrameworkCore.Design パッケージをインストールします。

NuGetパッケージマネージャーコンソールで次のコマンドを実行してインストールすることもできます。
Install-Package Microsoft.EntityFrameworkCore.Design 

Entity Framework Core Tools のインストール

Microsoft.EntityFrameworkCore.Toolsパッケージをインストールします。

NuGetパッケージマネージャーコンソールで次のコマンドを実行してインストールすることもできます。
Install-Package Microsoft.EntityFrameworkCore.Tools

データベースの接続文字列の取得

データベースの接続文字列を取得します。
既にデータベースの接続文字列がわかっている場合は、この手順はスキップできます。

サーバーエクスプローラーに新しいデータ接続を追加し、データベースの接続を作成します。 詳細な手順はこちらの記事を参照してください。

接続作成後、サーバーエクスプローラーでデータ接続のノードをクリックして選択し、プロパティウィンドウの[接続文字列]の値をコピーすることで、 接続文字列を取得できます。

データベースのスキャフォールディング : DbContext のコード生成

NuGetパッケージマネージャーのコンソールで、次のコマンドを実行し、DbContextのコードを生成します。
Scaffold-DbContext 'Data Source=(サーバーのホスト名または、IPアドレス); Initial Catalog=(データベース名)x;Connect Timeout=600;Persist Security Info=True; User ID=(ユーザーID);Password=(パスワード);Max Pool Size=250;Encrypt=False' Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
今回のデモでは、以下の接続文字列を利用しています。
Data Source=192.168.0.16; Initial Catalog=iPentecSandBox;Connect Timeout=600;Persist Security Info=True; User ID=******;Password=********
コマンドを実行すると、Modelsディレクトリ内に以下のファイルが作成されます。 (指定ディレクトリへのDbContextクラスのコード生成の詳細はこちらの記事を参照してください。)
  • "(データベース名).cs" のファイル名のDbContextクラスのファイル
  • "(テーブル名).cs" のファイル名のテーブルのデータ構造のクラスのファイル


コード

以下のコードを用意します。

Razor Pages

Pages/Index.cshtml
@page
@using SimpleDBCard.Models
@model SimpleDBCard.Pages.IndexModel
@{
}
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
}
<html>
  <head>
    <title>レコード一覧</title>
    <link rel="stylesheet" href="~/Style.css"/>
  </head>
  <body>
    <div>
      <h1>レコード一覧</h1>
      <a href="~/Insert">レコードの追加</a>
        <table>
          <thead>
            <tr>
              <th>id</th>
              <th>model</th>
              <th>name</th>
              <th>class</th>
              <th>category</th>
              <th>price</th>
              <th></th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            @foreach(ProductsB rec in Model.items){
              <tr>
                <td>@rec.Id</td>
                <td>@rec.Model</td>
                <td>@rec.Name</td>
                <td>@rec.Class</td>
                <td>@rec.Category</td>
                <td>@string.Format("{0:#,0}", rec.Price)</td>
                <td><a href="~/Update/@rec.Id">編集</a></td>
                <td><a href="~/Delete/@rec.Id">削除</a></td>
              </tr>
            }
          </tbody>
        </table>
    </div>
  </body>
</html>
Pages/Index.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using SimpleDBCard.Models;

namespace SimpleDBCard.Pages
{
  public class IndexModel : PageModel
  {
    public IEnumerable<ProductsB>? items;

    public void OnGet()
    {
      IPentecSandBoxContext cx = new IPentecSandBoxContext();
      items = cx.ProductsBs;
    }
  }
}
Pages/Update.cshtml
@page "/Update/{id:int}"
@model SimpleDBCard.Pages.UpdateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
}
<html>
  <head>
    <title>レコードの編集</title>
    <link rel="stylesheet" href="~/Style.css"/>
  </head>
  <body>
  <div>
    <h1>レコードの編集</h1>
    <form method="post">
      <input type="hidden" asp-for="@Model.item.Id"/>

      <div class="EditColumnFrame">
        <div class="EditColumn">id</div>
        <div class="EditColumn">@Model.item.Id</div>
      </div>

      <div class="EditColumnFrame">
        <div class="EditColumn">model</div>
        <div class="EditColumn"><input type="text" asp-for="@Model.item.Model"></div>
      </div>

      <div class="EditColumnFrame">
        <div class="EditColumn">name</div>
        <div class="EditColumn"><input type="text" asp-for="@Model.item.Name"></div>
      </div>

      <div class="EditColumnFrame">
        <div class="EditColumn">class</div>
        <div class="EditColumn"><input type="text" asp-for="@Model.item.Class"></div>
      </div>

      <div class="EditColumnFrame">
        <div class="EditColumn">category</div>
        <div class="EditColumn"><input type="text" asp-for="@Model.item.Category"></div>
      </div>

      <div class="EditColumnFrame">
        <div class="EditColumn">price</div>
        <div class="EditColumn"><input type="text" asp-for="@Model.item.Price"></div>
      </div>

      <div><input type="submit" value="変更"></div>
    </form>
  </body>
</html>
Pages/Update.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using SimpleDBCard.Models;

namespace SimpleDBCard.Pages
{
  public class UpdateModel : PageModel
  {
    [BindProperty]
    public ProductsB item { get; set; }

    public void OnGet(int id)
    {
      IPentecSandBoxContext cx = new IPentecSandBoxContext();
      item = cx.ProductsBs.Single(i => i.Id == id);
      item.Model = item.Model?.Trim();
      item.Name= item.Name?.Trim();
      item.Class = item.Class?.Trim();
      item.Category = item.Category?.Trim();
    }

    public IActionResult OnPost()
    {
      if (item != null) {
        IPentecSandBoxContext cx = new IPentecSandBoxContext();
        cx.ProductsBs.Update(item);
        cx.SaveChanges();
      }
      return RedirectToPage("/Index");
    }
  }
}
Pages/Insert.cshtml
@page
@model SimpleDBCard.Pages.InsertModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
}
<html>
  <head>
    <title>レコードの追加</title>
    <link rel="stylesheet" href="~/Style.css"/>

  </head>
  <body>
  <div>
    <h1>レコードの追加</h1>
    <div>レコードを追加します。</div>
    <form method="post">
      <div><input type="submit" value="追加"></div>
    </form>
  </body>
</html>
Pages/Insert.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using SimpleDBCard.Models;

namespace SimpleDBCard.Pages
{
  public class InsertModel : PageModel
  {
    public void OnGet()
    {
    }

    public IActionResult OnPost()
    {
      ProductsB rec = new ProductsB();
      rec.Name = "新規アイテム";
      rec.Model = "NEW-MODEL";

      IPentecSandBoxContext cx = new IPentecSandBoxContext();
      EntityEntry<ProductsB> ret = cx.ProductsBs.Add(rec);
      cx.SaveChanges();

      return RedirectToPage("/Update", new { id = rec.Id });
    }
  }
}
Pages/Delete.cshtml
@page "/Delete/{id:int}"
@model SimpleDBCard.Pages.DeleteModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@{
}
<html>
  <head>
    <title>レコードの削除</title>
    <link rel="stylesheet" href="~/Style.css"/>

  </head>
  <body>
  <div>
    <h1>レコードの削除</h1>
    <div>id = @Model.deleteid のレコードを削除します。</div>
    <form method="post">
      <input type="hidden" asp-for="@Model.deleteid"/>
      <div><input type="submit" value="削除"></div>
    </form>
  </body>
</html>
Pages/Delete.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using SimpleDBCard.Models;

namespace SimpleDBCard.Pages
{
  public class DeleteModel : PageModel
  {
    [BindProperty]
    public int deleteid { get; set; }

    public void OnGet(int id)
    {
      deleteid = id;
    }

    public IActionResult OnPost()
    {
      IPentecSandBoxContext cx = new IPentecSandBoxContext();
      ProductsB rec = cx.ProductsBs.Single(i => i.Id == deleteid);
      cx.ProductsBs.Remove(rec);
      cx.SaveChanges();

      return RedirectToPage("/Index");
    }

  }
}

CSS Program.cs

wwwroot/Style.css
Table {
  margin-left: 1rem;
  margin-top: 2rem;
  border-collapse: collapse;
}

  Table tr th {
    border: solid 1px #ff78ae;
    padding: 0.1rem 0.25rem 0.1rem 0.25rem;
  }

  Table tr td {
    border: solid 1px #ff78ae;
    padding: 0.1rem 0.25rem 0.1rem 0.25rem;
  }

.EditColumnFrame {
  display:flex;
}

.EditColumn {
  min-width:6rem;
}
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();

app.UseStaticFiles();
app.UseRouting();
app.MapRazorPages();
app.Run();

ファイル構成

ファイル構成は下図の状態です。Modelsディレクトリ内に、Scaffold-DbContextコマンドで生成されたコードファイルが配置されています。

解説

Index.cshtml

データベースからレコードを読み出し、ページにレコードの情報を表示します。
取得したレコードの情報はIEnumerable<ProductsB>型のitems変数に格納します。
ページへの表示は、RazorPage内でforeachループでページモデルクラスのitems変数にアクセスして要素をページに表示します。

Update.cshtml

pageディレクティブを次の記述に変更し、編集するIDのパラメーターを受け取れる状態にします。
@page "/Update/{id:int}"
変更するレコードの情報はOnGetメソッドで読み込み、ProductsBクラスのitem変数に設定します。
Idから1レコードを取得するため、DbSetオブジェクトのSingleメソッドを呼び出して取得します。

取得した値のnchar型のフィールドには空白が含まれるため、Trimメソッドを実行して空白を除去します。
空白の除去処理はDbContextクラスのコードに実装する方法もありますが、 今回は、Scaffold-DbContextで生成したコードを編集せずに利用したいため、OnGetメソッド内にTrimの処理を実装しています。
  IPentecSandBoxContext cx = new IPentecSandBoxContext();
  item = cx.ProductsBs.Single(i => i.Id == id);
  item.Model = item.Model?.Trim();
  item.Name= item.Name?.Trim();
  item.Class = item.Class?.Trim();
  item.Category = item.Category?.Trim();

idはhiddenフィールドに設定します。
<input type="hidden" asp-for="@Model.item.Id"/>

変更可能なフィールドはinputタグのasp-for属性にメンバ変数を設定します。
<input type="text" asp-for="@Model.item.Name">

変更内容のデータベースの反映は OnPaseメソッドで実行します。
DbSetオブジェクトの Update() メソッドを呼び出し、その後DbContextオブジェクトの SaveChanges() メソッドを呼び出します。
  IPentecSandBoxContext cx = new IPentecSandBoxContext();
  cx.ProductsBs.Update(item);
  cx.SaveChanges();

変更後は /Index ページにリダイレクトして戻ります。

Insert.cshtml

レコードの挿入は、確認後にテーブルのDbSetオブジェクトのAddメソッドを呼び出し、レコードを挿入します。 挿入したレコードのIDはレコードのオブジェクトのIdプロパティに設定されます。
RedirectToPageメソッドを呼び出し、挿入したレコードの編集画面に遷移します。

Delete.cshtml

pageディレクティブを次の記述に変更し、編集するIDのパラメーターを受け取れる状態にします。
@page "/Delete/{id:int}"

受け取ったIDはページモデルクラスのdeleteidプロパティに挿入します。
    [BindProperty]
    public int deleteid { get; set; }

    public void OnGet(int id)
    {
      deleteid = id;
    }

サブミットボタンがクリックされると、OnPostメソッドが呼び出され、削除するIDのレコードオブジェクトを取得し、 DbSetオブジェクトのRemove()メソッドを呼び出してレコードを削除します。
  IPentecSandBoxContext cx = new IPentecSandBoxContext();
  ProductsB rec = cx.ProductsBs.Single(i => i.Id == deleteid);
  cx.ProductsBs.Remove(rec);
  cx.SaveChanges();

実行結果

プロジェクトを実行します。Webブラウザが起動し下図のページが表示されます。
レコードの一覧がページに表示されます。編集したいレコードの[編集]リンクをクリックします。


リンクをクリックすると編集ページに遷移します。レコードの内容がテキストボックスに入力された状態になっています。


テキストボックスを編集し、レコードの内容を変更します。変更後[変更]ボタンをクリックします。


/Index ページに戻ります。レコードの内容が編集できている状態が確認できます。


ページ上部の[レコードの追加]リンクをクリックします。下図のレコードの追加画面が表示されます。[追加]ボタンをクリックします。


レコードが作成され、追加したレコードの編集画面に遷移します。


レコードの内容を編集し[変更]ボタンをクリックします。


レコードが追加できています。


削除したいレコードの[削除]リンクをクリックします。


レコードの削除画面が表示されます。削除するレコードのidの値が表示されています。[削除]ボタンをクリックします。


レコードが削除され、/Indexページに戻ります。レコードが削除できていることが確認できます。


Entity Framework Core を利用してシンプルなカード型のデータベース編集アプリケーションを作成できました。
Entity Framework Core を利用しない版と比較すると、記述するコード量が大幅に減ります。

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2023-11-25
作成日: 2023-05-07
iPentec all rights reserverd.