1
1

More than 3 years have passed since last update.

ASP.net coreの探索 ~2 CRUDページを編集してRazorを見てみる~

Posted at

前回のおさらい

https://qiita.com/CheerfulGeneral/items/be34fcccfe397c27d064
1行掲示板を
モダンになったASP.net coreで作ってみようという試みです。
前回は
・Model(掲示板データ)を定義
・データベースのマイグレーション(初期化)
・スキャフォールディング(骨組みページ)の作成
を行いました。
今回はスキャフォールディングを編集して、掲示板を完成させたいと思います。
軽くRazorも見てみましょう。

1.CRUDページ

https://wa3.i-3-i.info/word123.html
CRUD(クラッドと読むらしい)とは
Create, Read, Update, Delete
の頭文字であり、要するにデータを作ったり処理することです。
スキャフォールディングを作成すると、このCRUDページが自動作成されます。
以下の名前でページが作成されています。
・Index (データを表示させるページ CRUDのR)
・Create (データを作成するページ CRUDのC)
・Edit (データを編集するページ CRUDのU)
・Detail (データを詳細にみるページ CRUDのR 2つめ)
・Delete (データを削除するページ CRUDのD)

前回はIndexとCreateをそのまま表示させて
1行掲示板みたいに使いました。
今回はこの2つをベースに、もっと掲示板ぽくしてみようと思いいます。

2.Indexページから見るRazorコード

index.PNG

Index.html
@page
@model RasorSample.Pages.BBS.IndexModel

@{
    ViewData["Title"] = "1行掲示板";
}

<h1>1行掲示板</h1>

<p>
    <a asp-page="Create">投稿する</a>
</p>
<table class="table">
    <tbody>
@foreach (var item in Model.Writing) {
        <tr>
            <td>
                <h2>@Html.DisplayFor(modelItem => item.Name)</h2>
            </td>
            <td>
                <h2>@Html.DisplayFor(modelItem => item.Comment)</h2>
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">編集</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">削除</a>
            </td>
        </tr>
}
    </tbody>
</table>

Indexページを上記のように改修しました。
ページは.cshtmlという拡張子ファイルで出来います。

改修箇所とRazor

Indexで改修したのは
1.Tableのtheadを消去して、掲示板ぽくした。
2.Titleおよび他用語の日本語化
です。
Razorの形式を見ながら、改修箇所を見ていきましょう。

@page

@page

Razorはアットマーク(@)の後に
コードを書く形式となっております。
@pageは、このファイルがRazorですよーという合図のようです。

@model

@model RasorSample.Pages.BBS.IndexModel

モデルを参照するコードです。
WPFの開発をしたことがる人はわかるかもしれませんが、
Razorページは.cshtmlの裏にC#ファイル(.cshtml.cs)があります。
このC#ファイルのクラスが「IndexModel」という名前です。
モデルをC#で扱う形式に変換するというイメージかもしれません。

@ViewData

@{
    ViewData["Title"] = "1行掲示板";
}

@の中の「ViewData["Title"]でページタイトルを編集します。
ViewDataというのはページ表示項目を集めた辞書となっており
その"Title"キーの値を編集します。

asp属性

htmlのaタグに「asp-○○」という属性があります。

<a asp-page="Create">投稿する</a>
<a asp-page="./Edit" asp-route-id="@item.ID">編集</a> |

asp-pageは指定したアドレスに移行します。
(FormのGetメソッドみたい)
asp-route-idはルートidをつける属性みたいです。
ルートidを調べてみましたが、よくわかりませんでした...
(ページ移行アクションで間違いがないように、IDを送るみたいですが...)

この辺りを改修すれば、掲示板みたいな表示になりました。

3.Createページ

Create.PNG

Create.cshtml
@page
@model RasorSample.Pages.BBS.CreateModel

@{
    ViewData["Title"] = "投稿ページ";
}

<h1>投稿する</h1>

<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="Writing.Name" class="control-label"></label>
                <input asp-for="Writing.Name" class="form-control" />
                <span asp-validation-for="Writing.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label class="control-label">書き込み内容</label>
                <input asp-for="Writing.Comment" class="form-control" />
                <span asp-validation-for="Writing.Comment" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Writing.ReleaseDate" class="control-label"></label>
                <input asp-for="Writing.ReleaseDate" class="form-control" />
                <span asp-validation-for="Writing.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="投稿" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">掲示板に戻る</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

記入したデータをCreate(掲示板に投稿)する画面です。
基本的に改修はIndexと同様に
必要な表示箇所を直すくらいです。

ここでは、Indexでは見なかったC#ファイルを見てみましょう。

create.cshtml.cs
using RasorSample.Model;

namespace RasorSample.Pages.BBS
{
    public class CreateModel : PageModel
    {
        private readonly RasorSample.Data.RasorSampleContext _context;

        public CreateModel(RasorSample.Data.RasorSampleContext context)
        {
            _context = context;
        }

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

        [BindProperty]
        public Writing Writing { get; set; }

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

            _context.Writing.Add(Writing);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

上の方ではcontext(ページ内容を含むクラス?)を作成しています。
OnGetメソッドでページを初期化します。

真ん中の[BindProperty]では、作ったModelクラスのプロパティを作成しております。

Createで重要なのは OnPostAsync()メソッドです。

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

            _context.Writing.Add(Writing);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }

これはページでデータが送信された際に処理されます。
データフォームが正常であれば、Post送信されてIndexに戻ります。

4.その他ページ・通常MVCとの違い

Edit.PNG
Delete.PNG

CRUDには他にもページがありますが、
Indexなどの要領で同様に改修しました。

ASP.net coreではスキャフォールディングを作成すれば
Webアプリの基本動作は自動で実装してくれます。
他Webフレームワークのように、最初からViewやControllerを作らなくても
簡単にWebアプリケーションを作れてしまいます。

Createの箇所を見て気づいた方がいらっしゃると思いますが
RazorでWebアプリを作成する場合
1ページ(1 View)に1つのC#ファイル(1 Controller)が付きます。
そのため、ページごとに動作・Controllerを書きます。

通常のMVCはControllerを一括で書いて、
ルーティングごとにViewの切り替えをしていたと思います。
ここがRazorと通常MVCフレームワークの違いです。

個人的にはRazorの方が作りやすいなーと感じました。
(WPFの経験があるのも関係あるかもしれません。)

5.IndexとCreateをくっつけてみた

これで1行掲示板になった!と安心していましたが
第1回で載せた掲示板イメージはこんなのでした。
1行掲示板イメージズ.png

てなわけで、イメージ通りになるように
IndexとCreateをくっつけてみました。
New index.PNG

index.cshtml
@page
@model RasorSample.Pages.BBS.IndexModel

@{
    ViewData["Title"] = "1行掲示板";
}

<h1>1行掲示板</h1>

<div class="row">
    <div class="col-3"></div>
    <div class="col-6">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="WritingCreator.Name" class="control-label"></label>
                <input asp-for="WritingCreator.Name" class="form-control" />
                <span asp-validation-for="WritingCreator.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label class="control-label">書き込み内容</label>
                <input asp-for="WritingCreator.Comment" class="form-control" />
                <span asp-validation-for="WritingCreator.Comment" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="投稿" class="btn btn-primary" />
            </div>
        </form>
    </div>
    <div class="col-3"></div>
</div>

<table class="table">
    <tbody>
@foreach (var item in Model.Writing) {
        <tr>
            <td>
                <h2>@Html.DisplayFor(modelItem => item.Name)</h2>
            </td>
            <td>
                <h2>@Html.DisplayFor(modelItem => item.Comment)</h2>
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">編集</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">削除</a>
            </td>
        </tr>
}
    </tbody>
</table>

index.cshtml.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RasorSample.Model;

namespace RasorSample.Pages.BBS
{
    public class IndexModel : PageModel
    {
        private readonly RasorSample.Data.RasorSampleContext _context;

        public IndexModel(RasorSample.Data.RasorSampleContext context)
        {
            _context = context;
        }

        public IList<Writing> Writing { get;set; }  //For Index


        [BindProperty]
        public Writing WritingCreator { get; set; } //For Create

        public async Task OnGetAsync()  //For Index
        {
            Writing = await _context.Writing.ToListAsync();
        }

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

            WritingCreator.ReleaseDate = DateTime.Now;  //投稿時の日付を自動記載
            _context.Writing.Add(WritingCreator);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

IndexにCreateを貼り付けただけですね(笑
日付の選択が面倒だと思ったので、書き込み時の日付を
自動的に記入するようにしました。
スキャフォールディングで素材が全てそろっているので
コピペだけでもここまで出来てしまいます。

New index 書き込み後.PNG
ちゃんと書き込みできました。

(余談)cshtmlを編集していた際に、col-○○を使用して思いましたが
このRazorページってBootstrapを標準装備しているんですね。
(ASP.netの標準装備に同様なcolクラスがあるのかもしれないが)

まとめと今後の展望

Razorでスキャフォールディングを使えば
とても楽にMVCのWebアプリが作れました。
今度はSPA(シングルページアプリケーション)に挑戦してみたいと思います。

私も勉強しながら投稿しているため、
随分知識不足で正しくなさそうな書き込みとなってしまいました...oTL
もし間違いや指摘がありましたら、遠慮なくコメントお願いします。

また、ご存じの方がいらしましたら下記の情報提供をお願いします。
・ASP.net core 5.0のアプリを公開できるシステムorレンタルサーバー
 本来はVPSやクラウドに実行環境載せるのだと思いますが、このあたりの知識がまだ乏しくて...
・ASP.net core 5.0でMySQLを使う
 調査してみましたが、Web勉強中素人の私ではよく扱えませんでした...

(この辺りはASP.net core 5.0がリリースしてまだ3カ月しか経ってないため
情報もそんなないと思いますが...)

参考文献

https://demi-urge.com/razor-mvc-difference/
MVCとRazorの違いがわかりやすく図示されております。

https://github.com/Sho-Zhao/RasorSample
GitHubにRazorで作った1行掲示板をアップロードしました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1