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

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

<h1>1.CRUDページ</h1>
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つをベースに、もっと掲示板ぽくしてみようと思いいます。

<h1>2.Indexページから見るRazorコード</h1>
![index.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/31728acb-4426-5ef7-1f37-01c623ae38e0.png)


```html: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という拡張子ファイルで出来います。

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

<h3>@page</h3>

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

<h3>@model</h3>

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

<h3>@ViewData</h3>
```
@{
    ViewData["Title"] = "1行掲示板";
}
```
@の中の「ViewData["Title"]でページタイトルを編集します。
ViewDataというのはページ表示項目を集めた辞書となっており
その"Title"キーの値を編集します。

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

```html
<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を送るみたいですが...)

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

<h1>3.Createページ</h1>
![Create.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/560d0078-53c0-9748-b13f-4e8547a89f00.png)

```html: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#ファイルを見てみましょう。

```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()メソッドです。

```C#
// 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に戻ります。

<h1>4.その他ページ・通常MVCとの違い</h1>
![Edit.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/8f4c1aa9-dfb6-933a-bb2d-b2975e99ec29.png)
![Delete.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/6023b9ef-44ba-2cca-87b8-d77d98806d5d.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の経験があるのも関係あるかもしれません。)

<h1>5.IndexとCreateをくっつけてみた</h1>
これで1行掲示板になった！と安心していましたが
第1回で載せた掲示板イメージはこんなのでした。
![1行掲示板イメージズ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/eeaebe5b-cde6-5aa4-8993-2585b83935ba.png)

てなわけで、イメージ通りになるように
IndexとCreateをくっつけてみました。
![New index.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/3a207fbf-2656-fc00-fbcf-5eff76e9c237.png)

```html: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>

```

```csharp: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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/c93a3f27-e9a1-9c7b-e89c-46512ddcfa0c.png)
ちゃんと書き込みできました。

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

<h1>まとめと今後の展望</h1>
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カ月しか経ってないため
情報もそんなないと思いますが...）

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

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