【C#】VSCodeでWebアプリを作成してみよう ASP.NET Core【Blazor編】

C#ライン

Blazorを使ってWebアプリを作成

Blazorは初心者でも学びやすいC#を使って、SPA(シングルページアプリケーション)を簡単に作ることができるフレームワークです。Blazorを使うとC#だけでフロントエンド(画面表示部分)とバックエンド(データ処理部分)を開発できます。

ASP.NET Core Razor編はこちら

Blazorアプリの作成

今回はTodoリストを一覧表示するWebアプリを作成します。アプリに使用するデータベースは軽量・簡易版のSQLite3を使用します。
それでは早速作成していきましょう。まずはコマンドプロンプトを起動し、作業用のフォルダに移動するため次のコマンドを実行します。「cd 移動先のフォルダ名」(移動フォルダは自身のPCフォルダに置き換えてください)

C:\Users\sumio>cd C:\C#_Apps\VSCode

フォルダ移動したら次のコマンドを実行します。

dotnet new blazor -n MyTodoApp -o MyTodoApp -f net8.0 --interactivity Server -au Individual --use-local-db false

新規に作成されたフォルダに移動しますので次のコマンドを実行します。

cd MyTodoApp

blazor

VSCodeで開く

フォルダに移動したら、コマンド「code .」を実行するとVSCodeが起動し、今作成したフォルダ「MyTodoApp」が開きます。

VSCodeの左側のペインから「ソリューションエクスプローラ」→「MyTodoApp」を開くと作成されたフォルダが確認できます。

データモデルの定義

「ソリューションエクスプローラ」→「MyTodoApp」→「MyTodoApp」プロジェクトを右クリックし「新しいフォルダ」を選択します。

新しいフォルダ名の入力ボックスに「Models」を入力しエンターキーをクリックします。

MyTodoAppプロジェクト直下に「Models」フォルダが作成されました。

今作成した「Models」フォルダを右クリックし「新しいファイル」を選択します。

テンプレートの検索ボックスが表示されるので「クラス」を選択します。

ファイル名「TodoItem」を入力しエンターキーをクリックします。

Modelsフォルダ配下に「TodoItem.cs」ファイルが作成されました。

作成した「TodoItem.cs」ファイルを開きデータモデルのTodoItemクラスを定義します。下記のように{と}の中の4行追記します。[Key]という属性のついているフィールド(Id)が主キーとなります。

using System;
using System.ComponentModel.DataAnnotations;

namespace MyTodoApp.Models;

public class TodoItem
{
    [Key]
    public int Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public DateTime? FixedDate { get; set; }
}

次にソリューションエクスプローラのDate→ApplicationDbContext.csファイルを開きます。

3行目に「using MyTodoApp.Models;」を追記し、
{と}の中に「public DbSet TodoItems { get; set; }」の1行を追記します。

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using MyTodoApp.Models;

namespace MyTodoApp.Data;

public class ApplicationDbContext(DbContextOptions options) : IdentityDbContext(options)
{
    public DbSet TodoItems { get; set; }
}

データベースの作成

EFCoreコードファーストにより、データモデルクラスが先にあり、そこからデータベーススキーマを生成しデータベースへテーブル定義を反映します。

データモデルを作成するためのEFCoreツールをインストールします。コマンドプロンプトから「dotnet tool install –global dotnet-ef」コマンドを実行します。(実行するフォルダは.slnと同じ場所です)下記のように表示されたら完了です。

次にマイグレーションを実行するため「dotnet ef migrations add AddTodoItem -o Data\Migrations」コマンドを実行します。(実行するフォルダは.slnと同じ場所です)

上記の様に表示されるとData→Migrationsフォルダ内にマイグレーションファイルが作成されます。

マイグレーションをデータベースへ反映させるため「dotnet ef database update」コマンドを実行します。

上記の様に表示されたらデータベース作成は成功です。エクスプローラでDataフォルダ内に「app.db」のSqlite3データベースファイルが作成されたことが確認できます。

コマンドプロンプトでSqliteデータベースのテーブルを見てみましょう。「cd data」コマンドを実行しDataフォルダに移動します。

「sqlite3 app.db」コマンドを実行しsqliteデータベースに接続します。(※Sqlite3のパスが通っていないとコマンドエラーとなりますのでテーブル確認手順はスキップしても大丈夫です)

「.table」コマンドを実行すると「TodoItems」テーブルが確認できます。

「.q」コマンドでSqliteを終了します。

忘れないようにプロジェクトフォルダに戻りましょう。「cd ..」コマンドを実行しフォルダを移動します。

データベース参照のContentクラスの作成

Todoリスト一覧を表示するためTodoItemsテーブルからTodoのデータを取得するクラスをこれから作成します。その前にプロジェクトフォルダに「Contents」フォルダを作成します。VSCodeの左側ペインの「ソリューションエクスプローラ」→「MyTodoApp」→「MyTodoApp」のプロジェクトフォルダを右クリックしメニューから「新しいフォルダ」を選択します。

画面上部にテキスト入力ボックスが表示されますので「Contents」と入力しエンターキーをクリックします。

「Contents」フォルダが作成されました。「Contents」フォルダを右クリックし「新しいファイル」を選択します。

ファイル選択のリストが表示されますので「クラス」を選択します。

ファイル名入力テキストボックスに「TodoListContent」と入力しエンターキーをクリックします。(モデルのファイル名は単数形のためsはなしです)

「TodoListContent.cs」クラスファイルが作成されました。

「TodoListContent.cs」ファイルを開き下記のように修正します。

using System;
using Microsoft.EntityFrameworkCore;
using MyTodoApp.Data;
using MyTodoApp.Models;

namespace MyTodoApp.Contents;

public class TodoListContent
{
    private readonly ApplicationDbContext _context;

    public TodoListContent(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<List> GetTodosAsync()
    {
        var items = await _context.TodoItems.ToListAsync();
        return items;
    }
}

次に「Program.cs」ファイルを編集します。
先頭に「using MyTodoApp.Contents;」を追記しContents\TodoListContent.csファイルのTodoListContentクラスを参照できるようにします。
40行目あたりの「var app = builder.Build();」の上に「builder.Services.AddScoped(); 」の1行を追記します。

TodoListContentクラスはApplicationDbContexクラスにを継承しており、「builder.Services.AddScoped()」と追記することでProgram.csでDIコンテナへ事前登録しています

一覧表示用のRazorコンポーネントを追加

「MyTodoApp」→「Conponents」→「Pages」フォルダを右クリックし「新しいファイル」を選択します。

選択リストから「Razorコンポーネント」を選択します。

ファイル名入力テキストボックスに「TodoList」と入力しエンターキーをクリックします。

「TodoList.razor」Razorコンポーネントファイルが作衛されました。ファイルを開き下記のように編集します。

@page "/todolist"
@rendermode InteractiveServer
@using MyTodoApp.Models
@using MyTodoApp.Contents
@inject TodoListContent _todoService

<PageTitle>TodoList</PageTitle>

<h1>Todo List</h1>

<ul>
    @foreach (var item in _todoItems)
    {
        <li>
            <span>Id:@item.Id, @item.Title</span>
        </li>
    }
</ul>

@code {
    private List<TodoItem> _todoItems = new();

    protected override async Task OnInitializedAsync()
    {
        _todoItems = await _todoService.GetTodosAsync();
    }
}

トップページにTodoListへのリンクを追加

「MyTodoApp\Components\Layout\NavMenu.razor」と展開し「NavMenu.razor」ファイルを開きます。

21行目あたりのHomeリンクタグの下に下記を追記します。

<div class="nav-item px-3">
    <NavLink class="nav-link" href="todolist">
        <span class="bi bi-list-nested-nav-menu" 
            aria-hidden="true"></span> TodoList
    </NavLink>
</div>

それではWebサーバーを起動し画面に反映されたか確認してみましょう。プロジェクトフォルダ(\MyTodoApp)で「dotonet watch」コマンドを実行します。下記のように表示されWebサーバーが起動します。(少々時間がかかる場合があります)

自動でWebブラウザーが立ち上がります。

画面左側もメニューのhomeの下に「TodoList」リンクが追加されました。そのリンクをクリックしTodoList画面を表示します。

TodoListが表示されましたが今のところデータがありませんのでまだ1件も表示されていません。次からTodoListを登録する画面を作っていきます。

データベースへの登録

次にTodoListデータベースにデータを追加する機能を実装します。MyTodoApp\Contents\TodoListContent.cs ファイルを開きます。

GetTodosAsync()メソッドの下に次のコードを追記します。

    public async Task AddTodoAsync(TodoItem newTodoItem)
    {
        _context.TodoItems.Add(newTodoItem);
        await _context.SaveChangesAsync();
    }

次に画面表示するファイル MyTodoApp\Components\Pages\TodoList.razorを開きます。

ファイルのコードを下記のように編集します。

@page "/todolist"
@rendermode InteractiveServer
@using MyTodoApp.Models
@using MyTodoApp.Contents
@inject TodoListContent _todoService

<PageTitle>TodoList</PageTitle>

<h1>Todo List</h1>

  <table class="table">
        <thead>
            <tr>
                <th>Id</th>
                <th>Title</th>
                <th>FixedDate</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in _todoItems)
            {
                <tr>
                    <td>@item.Id</td>
                    <td>@item.Title</td>
                    <td>@item.FixedDate</td>
                </tr>
            }
        </tbody>
    </table>

<input @bind="_newTodoItem.Title" placeholder="New todo title" />
<button @onclick="AddTodoAsync">新規</button>

@code {
    private List<TodoItem> _todoItems = new();
    private TodoItem _newTodoItem = new TodoItem();

    protected override async Task OnInitializedAsync()
    {
        _todoItems = await _todoService.GetTodosAsync();
    }

    private async Task ReloadTodosAsync()
    {
        _todoItems = await _todoService.GetTodosAsync();
    }

    private async Task AddTodoAsync()
    {
        await _todoService.AddTodoAsync(_newTodoItem);
        _newTodoItem = new TodoItem();
        await ReloadTodosAsync();
    }
}

編集が終わったらアプリを起動し画面を確認してみましょう。コマンドで「dotnet watch」を実行します。アプリ起動したら左側メニュー「TodoList」リンクをクリックしTodoListアプリからテキストボックスにタイトルを入力し、登録ボタンをクリックします。

これでアプリからデータベースにTodoを登録することができました。次は編集、削除を実装します。

データベースの更新、削除

TodoListデータベースにデータを編集、削除する機能を実装します。MyTodoApp\Contents\TodoListContent.cs ファイルを開きます。先ほど追記したAddTodoAsyncメソッドの下に下記コードを追記します。

    public async Task UpdateTodoAsync(TodoItem ItemInput)
    {
        var existingItem = await _context.TodoItems.FindAsync(ItemInput.Id);
        if (existingItem != null)
        {
            existingItem.Title = ItemInput.Title;
            await _context.SaveChangesAsync();
        }
    }

    public async Task DeleteTodoAsync(int id)
    {
        var existingItem = await _context.TodoItems.FindAsync(id);
        if (existingItem != null)
        {
            _context.TodoItems.Remove(existingItem);
            await _context.SaveChangesAsync();
        }
    }

次に画面表示するファイル MyTodoApp\Components\Pages\TodoList.razorを開きます。
コードを下記のように編集します。

@page "/todolist"
@rendermode InteractiveServer
@using MyTodoApp.Models
@using MyTodoApp.Contents
@inject TodoListContent _todoService

<PageTitle>TodoList</PageTitle>

<h1>Todo List</h1>

  <table class="table">
        <thead>
            <tr>
                <th>Id</th>
                <th>Title</th>
                <th>データ修正</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in _todoItems)
            {
                <tr>
                    <td>@item.Id</td>
                    <td>@item.Title</td>
                    <td>                
                        @if (_editingTodoItem != null && _editingTodoItem.Id == item.Id)
                        {
                            <input @bind="_editingTodoItem.Title" placeholder="titleを編集" />
                            <button @onclick="UpdateTodoAsync">保存</button>
                        }
                        else
                        {
                            <button @onclick="() => EnterEditMode(item)">編集</button>
                        }
                        <button @onclick="() => DeleteTodoAsync(item.Id)">削除</button>
                    </td>
                </tr>
            }
        </tbody>
    </table>

<input @bind="_newTodoItem.Title" placeholder="New todo title" />
<button @onclick="AddTodoAsync">新規</button>

@code {
    private List<TodoItem> _todoItems = new();
    private TodoItem _newTodoItem = new TodoItem();
    private TodoItem? _editingTodoItem;

    protected override async Task OnInitializedAsync()
    {
        _todoItems = await _todoService.GetTodosAsync();
    }

    private async Task ReloadTodosAsync()
    {
        _todoItems = await _todoService.GetTodosAsync();
    }

    private async Task AddTodoAsync()
    {
        await _todoService.AddTodoAsync(_newTodoItem);
        _newTodoItem = new TodoItem();
        await ReloadTodosAsync();
    }

    private void EnterEditMode(TodoItem todoItem)
    {
        _editingTodoItem = new TodoItem
            {
                Id = todoItem.Id,
                Title = todoItem.Title
            };
    }

    private async Task UpdateTodoAsync()
    {
        if (_editingTodoItem != null)
        {
            await _todoService.UpdateTodoAsync(_editingTodoItem);
            _editingTodoItem = null;
            await ReloadTodosAsync();
        }
    }

    private async Task DeleteTodoAsync(int id)
    {
        await _todoService.DeleteTodoAsync(id);
        await ReloadTodosAsync();
    }
}

以上でデータベースのCRUD(登録、更新、削除)機能の完成です!

アプリの実行

それではアプリを起動して動作確認をしてみましょう。dotnet watchコマンドを実行してアプリを起動します。

C:\C#_Apps\VSCode\MyTodoApp>dotnet watch

アプリが起動したら左側メニューのTodoListリンクをクリックし、先ほど登録したTitleの編集ボタンをクリックします。

データ修正のテキストボックスが表示されましたのでTitleを更新して保存ボタンをクリックします。

Titleが更新されました。それでは削除ボタンでデータを削除してみます。

以上でアプリは完成です。

まとめ

いかがだったでしょうか。Blazorを利用することでシングルページでCRUD機能のアプリを作成することができます。また、今回作ったアプリは左側にログイン認証画面へのリンクが付いていますが、コードを実装していませんので登録はできません。画面のみ最初から用意されています。認証機能についてはまた別の記事で掲載予定です!

ASP.NET Core Razor編はこちら

コメント