ページ作成ウィザードを利用して、Entity Framework Core を利用したRazor Pageを作成する - C#

ページ作成ウィザードを利用して、Entity Framework Core を利用したRazor Pageを作成する手順を紹介します。

手順

プロジェクトの作成

今回は空のRazorPagesプロジェクトを作成します。

モデルとDbContextの作成

モデルと、DbContextのコードを作成します。
コードは直接記述しても良いですし、既存のデータベースがある場合は、Scaffold-DbContext コマンドを実行して作成しても良いです。
MyDbContext.cs
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace EntityFrameworkCoreRazorPages
{
  public class MyDbContext : DbContext
  {
    public DbSet<MyTable1Rec> MyTable1 { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
      options.UseSqlServer("(データベース接続文字列)");
    }
  }
}
MyTable1Rec.cs
namespace EntityFrameworkCoreRazorPages
{
    public class MyTable1Rec
    {
      public int id { get; set; }
      public string Name { get; set; }
      public string Category { get; set; }
      public string Code { get; set; }
      public int Price { get; set; }
    }
}

RazorPageの作成

ソリューションエクスプローラーでプロジェクトに Pages フォルダを作成します。フォルダを選択して、右クリックし、[追加]メニューの[Razor ページ...]の項目をクリックします。


下図の[新規スキャフォールディング アイテムの追加]ダイアログが表示されます。[Entity Framework を使用する Razor ページ]の項目を選択し、 ウィンドウ右下の[追加]ボタンをクリックします。


[Entity Framework を使用する Razor ページの追加]ダイアログが表示されます。[テンプレート]のドロップダウンリストボックスをクリックして、テンプレートを選択します。


[Razor ページ名]、[テンプレート]、[モデル クラス]、[データ コンテキスト クラス]を設定します。設定ができたら、[追加]ボタンをクリックします。


スキャフォールディングが実行され、ページが追加されます。
テンプレートを変更して同じ手順を繰り返し、Createテンプレートで"PageCreate"、Deleteテンプレートで"PageDelete"、Detailsテンプレートで"PageDetails"、 Editテンプレートで"PageEdit"、Listテンプレートで"PageList" ページを作成します。

コードの修正

Program.csを変更します。コード内にAddDbContext() メソッドを追加します。
Program.cs
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<EntityFrameworkCoreRazorPages.MyDbContext>();
var app = builder.Build();

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

app.Run();

タグヘルパーが利用できるようにするため、各cshtmlファイルの先頭に次のコードを追記します。
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

または、Pages/Shared フォルダ内に _ViewImports.cshtml ファイルを作成し、以下のコードを記述する方法もあります。
@namespace EntityFrameworkCoreRazorPages.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

PageCreate.cshtmlのasp-page部分を今回作成したページ名に合わせて修正します。
  <a asp-page="./PageList">Back to List</a>

PageCreate.cshtml.cs の RedirectToPage メソッドのリダイレクト先パラメーターを今回作成したページ名に合わせて修正します。
  return RedirectToPage("./PageList");

PageDelete.cshtmlのasp-page部分を今回作成したページ名に合わせて修正します。
  <a asp-page="./PageList">Back to List</a>

PageDelete.cshtml.cs の RedirectToPage メソッドのリダイレクト先パラメーターを今回作成したページ名に合わせて修正します。
  return RedirectToPage("./PageList");

PageDetails.cshtmlのasp-page部分を今回作成したページ名に合わせて修正します。
  <a asp-page="./PageEdit" asp-route-id="@Model.MyTable1Rec?.id">Edit</a> |
  <a asp-page="./PageList">Back to List</a>

PageEdit.cshtmlのasp-page部分を今回作成したページ名に合わせて修正します。
  <a asp-page="./PageList">Back to List</a>

PageEdit.cshtml.cs の RedirectToPage メソッドのリダイレクト先パラメーターを今回作成したページ名に合わせて修正します。
  return RedirectToPage("./PageList");

PageList.cshtmlのasp-page部分を今回作成したページ名に合わせて修正します。
  <a asp-page="PageCreate">Create New</a>
  <a asp-page="./PageEdit" asp-route-id="@item.id">Edit</a> |
  <a asp-page="./PageDetails" asp-route-id="@item.id">Details</a> |
  <a asp-page="./PageDelete" asp-route-id="@item.id">Delete</a>

実行結果

プロジェクトを実行し、(アプリケーションルートURL)/PageList のURLにアクセスします。
[Details]のリンクをクリックします。


Details画面が表示されます。


Details画面で[Edit]リンクをクリックするか、List画面で[Edit]リンクをクリックします。下図の編集画面が表示されます。


値を編集し[Save]ボタンをクリックします。


リスト画面に切り替わり、変更内容が反映されていることが確認できます。

コード

コード全体です。
MyDbContext.cs
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace EntityFrameworkCoreRazorPages
{
  public class MyDbContext : DbContext
  {
    public DbSet<MyTable1Rec> MyTable1 { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
      options.UseSqlServer("(データベース接続文字列)");
    }

  }
}
MyTable1Rec.cs
namespace EntityFrameworkCoreRazorPages
{
    public class MyTable1Rec
    {
      public int id { get; set; }
      public string Name { get; set; }
      public string Category { get; set; }
      public string Code { get; set; }
      public int Price { get; set; }
    }
}
Program.cs
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<EntityFrameworkCoreRazorPages.MyDbContext>();
var app = builder.Build();

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

app.Run();
_ValidationScriptsPartial.cshtml
<environment names="Development">
    <script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</environment>
<environment names="Staging,Production">
    <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.17.0/jquery.validate.min.js"
            asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js"
            asp-fallback-test="window.jQuery && window.jQuery.validator"
            crossorigin="anonymous"
            integrity="sha384-rZfj/ogBloos6wzLGpPkkOr/gpkBNLZ6b6yLy4o+ok+t/SAKlL5mvXLr0OXNi1Hp">
    </script>
    <script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.9/jquery.validate.unobtrusive.min.js"
            asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
            asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
            crossorigin="anonymous"
            integrity="sha384-ifv0TYDWxBHzvAk2Z0n8R434FL1Rlv/Av18DXE43N/1rvHyOG4izKst0f2iSLdds">
    </script>
</environment>
PageCreate.cshtml
@page
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model EntityFrameworkCoreRazorPages.Pages.PageCreateModel

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>PageCreate</title>
</head>
<body>

<h4>MyTable1Rec</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="MyTable1Rec.Name" class="control-label"></label>
                <input asp-for="MyTable1Rec.Name" class="form-control" />
                <span asp-validation-for="MyTable1Rec.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="MyTable1Rec.Category" class="control-label"></label>
                <input asp-for="MyTable1Rec.Category" class="form-control" />
                <span asp-validation-for="MyTable1Rec.Category" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="MyTable1Rec.Code" class="control-label"></label>
                <input asp-for="MyTable1Rec.Code" class="form-control" />
                <span asp-validation-for="MyTable1Rec.Code" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="MyTable1Rec.Price" class="control-label"></label>
                <input asp-for="MyTable1Rec.Price" class="form-control" />
                <span asp-validation-for="MyTable1Rec.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
</body>
</html>
PageCreate.cshtm.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using EntityFrameworkCoreRazorPages;

namespace EntityFrameworkCoreRazorPages.Pages
{
    public class PageCreateModel : PageModel
    {
        private readonly EntityFrameworkCoreRazorPages.MyDbContext _context;

        public PageCreateModel(EntityFrameworkCoreRazorPages.MyDbContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public MyTable1Rec MyTable1Rec { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.MyTable1 == null || MyTable1Rec == null)
            {
                return Page();
            }

            _context.MyTable1.Add(MyTable1Rec);
            await _context.SaveChangesAsync();

            return RedirectToPage("./PageList");
        }
    }
}
PageDelete.cshtm
@page
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model EntityFrameworkCoreRazorPages.Pages.PageDeleteModel

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>PageDelete</title>
</head>
<body>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>MyTable1Rec</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.MyTable1Rec.Name)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.MyTable1Rec.Name)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.MyTable1Rec.Category)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.MyTable1Rec.Category)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.MyTable1Rec.Code)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.MyTable1Rec.Code)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.MyTable1Rec.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.MyTable1Rec.Price)
        </dd>
    </dl>
    
    <form method="post">
        <input type="hidden" asp-for="MyTable1Rec.id" />
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-page="./PageList">Back to List</a>
    </form>
</div>
</body>
</html>
PageDelete.cshtm.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using EntityFrameworkCoreRazorPages;

namespace EntityFrameworkCoreRazorPages.Pages
{
    public class PageDeleteModel : PageModel
    {
        private readonly EntityFrameworkCoreRazorPages.MyDbContext _context;

        public PageDeleteModel(EntityFrameworkCoreRazorPages.MyDbContext context)
        {
            _context = context;
        }

        [BindProperty]
      public MyTable1Rec MyTable1Rec { get; set; } = default!;

        public async Task<IActionResult> OnGetAsync(int? id)
        {
            if (id == null || _context.MyTable1 == null)
            {
                return NotFound();
            }

            var mytable1rec = await _context.MyTable1.FirstOrDefaultAsync(m => m.id == id);

            if (mytable1rec == null)
            {
                return NotFound();
            }
            else 
            {
                MyTable1Rec = mytable1rec;
            }
            return Page();
        }

        public async Task<IActionResult> OnPostAsync(int? id)
        {
            if (id == null || _context.MyTable1 == null)
            {
                return NotFound();
            }
            var mytable1rec = await _context.MyTable1.FindAsync(id);

            if (mytable1rec != null)
            {
                MyTable1Rec = mytable1rec;
                _context.MyTable1.Remove(MyTable1Rec);
                await _context.SaveChangesAsync();
            }

            return RedirectToPage("./PageList");
        }
    }
}
PageDetails.cshtm
@page
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model EntityFrameworkCoreRazorPages.Pages.PageDetailsModel

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>PageDetails</title>
</head>
<body>

<div>
    <h4>MyTable1Rec</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.MyTable1Rec.Name)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.MyTable1Rec.Name)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.MyTable1Rec.Category)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.MyTable1Rec.Category)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.MyTable1Rec.Code)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.MyTable1Rec.Code)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.MyTable1Rec.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.MyTable1Rec.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-page="./Edit" asp-route-id="@Model.MyTable1Rec?.id">Edit</a> |
    <a asp-page="./Index">Back to List</a>
</div>
</body>
</html>
PageDetails.cshtm.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using EntityFrameworkCoreRazorPages;

namespace EntityFrameworkCoreRazorPages.Pages
{
    public class PageDetailsModel : PageModel
    {
        private readonly EntityFrameworkCoreRazorPages.MyDbContext _context;

        public PageDetailsModel(EntityFrameworkCoreRazorPages.MyDbContext context)
        {
            _context = context;
        }

      public MyTable1Rec MyTable1Rec { get; set; } = default!; 

        public async Task<IActionResult> OnGetAsync(int? id)
        {
            if (id == null || _context.MyTable1 == null)
            {
                return NotFound();
            }

            var mytable1rec = await _context.MyTable1.FirstOrDefaultAsync(m => m.id == id);
            if (mytable1rec == null)
            {
                return NotFound();
            }
            else 
            {
                MyTable1Rec = mytable1rec;
            }
            return Page();
        }
    }
}
PageEdit.cshtm
@page
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model EntityFrameworkCoreRazorPages.Pages.PageEditModel

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>PageEdit</title>
</head>
<body>

<h4>MyTable1Rec</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="MyTable1Rec.id" />
            <div class="form-group">
                <label asp-for="MyTable1Rec.Name" class="control-label"></label>
                <input asp-for="MyTable1Rec.Name" class="form-control" />
                <span asp-validation-for="MyTable1Rec.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="MyTable1Rec.Category" class="control-label"></label>
                <input asp-for="MyTable1Rec.Category" class="form-control" />
                <span asp-validation-for="MyTable1Rec.Category" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="MyTable1Rec.Code" class="control-label"></label>
                <input asp-for="MyTable1Rec.Code" class="form-control" />
                <span asp-validation-for="MyTable1Rec.Code" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="MyTable1Rec.Price" class="control-label"></label>
                <input asp-for="MyTable1Rec.Price" class="form-control" />
                <span asp-validation-for="MyTable1Rec.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./PageList">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
</body>
</html>
PageEdit.cshtm.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using EntityFrameworkCoreRazorPages;

namespace EntityFrameworkCoreRazorPages.Pages
{
    public class PageEditModel : PageModel
    {
        private readonly EntityFrameworkCoreRazorPages.MyDbContext _context;

        public PageEditModel(EntityFrameworkCoreRazorPages.MyDbContext context)
        {
            _context = context;
        }

        [BindProperty]
        public MyTable1Rec MyTable1Rec { get; set; } = default!;

        public async Task<IActionResult> OnGetAsync(int? id)
        {
            if (id == null || _context.MyTable1 == null)
            {
                return NotFound();
            }

            var mytable1rec =  await _context.MyTable1.FirstOrDefaultAsync(m => m.id == id);
            if (mytable1rec == null)
            {
                return NotFound();
            }
            MyTable1Rec = mytable1rec;
            return Page();
        }

        // To protect from overposting attacks, enable the specific properties you want to bind to.
        // For more details, see https://aka.ms/RazorPagesCRUD.
        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _context.Attach(MyTable1Rec).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!MyTable1RecExists(MyTable1Rec.id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return RedirectToPage("./PageList");
        }

        private bool MyTable1RecExists(int id)
        {
          return (_context.MyTable1?.Any(e => e.id == id)).GetValueOrDefault();
        }
    }
}
PageList.cshtm
@page
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model EntityFrameworkCoreRazorPages.Pages.PageListModel

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>PageList</title>
</head>
<body>
<p>
    <a asp-page="PageCreate">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.MyTable1Rec[0].Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.MyTable1Rec[0].Category)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.MyTable1Rec[0].Code)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.MyTable1Rec[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.MyTable1Rec) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Category)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Code)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
              <a asp-page="./PageEdit" asp-route-id="@item.id">Edit</a> |
              <a asp-page="./PageDetails" asp-route-id="@item.id">Details</a> |
              <a asp-page="./PageDelete" asp-route-id="@item.id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>
</body>
</html>
PageList.cshtm.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using EntityFrameworkCoreRazorPages;

namespace EntityFrameworkCoreRazorPages.Pages
{
    public class PageListModel : PageModel
    {
        private readonly EntityFrameworkCoreRazorPages.MyDbContext _context;

        public PageListModel(EntityFrameworkCoreRazorPages.MyDbContext context)
        {
            _context = context;
        }

        public IList<MyTable1Rec> MyTable1Rec { get;set; } = default!;

        public async Task OnGetAsync()
        {
            if (_context.MyTable1 != null)
            {
                MyTable1Rec = await _context.MyTable1.ToListAsync();
            }
        }
    }
}
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2023-03-22
作成日: 2022-12-18
iPentec all rights reserverd.