MacでBlazorServer使ってみる

# はじめに

会社でBlazor開発をすることになったが家にMacBookしかなかったので、
作業概要を備忘録として残します。

# 環境

* macOS Big Sur	11.5.2
* dotnet 5.0
* Docker for desktop
* SQLServer
* Homebrew
* VsCode

※ VisualStudio for Macは有料だったので使わない。
※ Homebrew,VsCode,Monoの環境構築はすでに終わっていたので省く

# 環境構築

## Docker for desktop

* Homebrewからインストール
* インストールしたら、Lanchpadからアイコン探して実行  
※ docker-composeもセットで入る

```zsh
$ brew install docker
$ brew cask install docker
```

## SQLServer(Dockerコンテナ)

* 下記を参考に必要最低限でファイルを作成  
参考　https://qiita.com/urushibata/items/2d499e057fe9ed0cde64

```docker-compose.yml
version: '3'

services:
    mssql:
      image: mcr.microsoft.com/mssql/server:2019-latest
      container_name: mssql2019
      environment:
        - MSSQL_SA_PASSWORD=[password]
        - ACCEPT_EULA=Y
        - MSSQL_PID=Express
        - MSSQL_COLLATION=Japanese_CI_AS
      hostname: db-server
      restart: always
      ports:
        - 1433:1433
      volumes:
        - ./log:/var/opt/mssql/log
        - ./data:/var/opt/mssql/data
```
* 下記のコマンドでコンテナ起動

```zsh
$ cd [docker-compose.ymlのあるディレクトリ]
$ docker-compose up -d
```

## BlazorServerアプリの作成

* 毎度証明書の警告が面倒なのでhttpで開発する
* 公式のチュートリアル
* https://dotnet.microsoft.com/learn/aspnet/blazor-tutorial/create

```zsh
$ dotnet new blazorserver -o TestBlazor --no-https
# projectフォルダへ移動
$ cd TestBlazor
```

# Blazorアプリのカスタマイズ(tips集)

## DBContextについて
* 公式のチュートリアルどおりやるとDBContextを直接DIさせる内容だが、随所でNewしたい
* EFcore関連から調べるとDBFactoryをServiceに追加できるみたいなのでそっちを使う
* 公式サイト
* https://docs.microsoft.com/ja-jp/aspnet/core/blazor/blazor-server-ef-core?view=aspnetcore-5.0

```appsettings.json
"ConnectionStrings": {
    "MyDbContext": "Persist Security Info=False;User ID=sa;Password=[自分で決めたpass];Initial Catalog=[自分で決めたDB名];Server=localhost"
  }
```

```MyDbContext.cs
public class MyDbContext : DbContext
{
    public MyDbContext (DbContextOptions<MyDbContext> options) : base (options) { }
    public DbSet<Book> Books { get; set; }
}
```


```Startup.cs
// 下記をサービスのところに追加
services.AddDbContextFactory<MyDbContext> (options =>
  options.UseSqlServer (Configuration.GetConnectionString ("MyDbContext"))
);
```

* 各所でのContextの使い方は下記

```Index.razor
@page "/"
@inject IDbContextFactory<MyDbContext> DbFactory

@teststr

<p>@_surnameMessage</p>

@code {
    private string teststr;

    protected async override Task OnInitializedAsync()
    {
        using (var context = DbFactory.CreateDbContext())
        {
            // 処理
            teststr = context.Books.FirstOrDefault(b => b.ID == 1);
        }
    }
}
```

## sessionについて

* 公式サイト　
* https://docs.microsoft.com/ja-jp/aspnet/core/blazor/state-management?view=aspnetcore-5.0&pivots=server
* `ProtectedSessionStorage`を使えと書かれている
    * mvcのときみたいにserviceにaddsessionを書かなくてもセッションは使える模様
    * （ServerPrerenderedの場合）OnInitializedAsyncでは呼び出せないので注意
    * Loading処理かいてOnAfterRenderAsyncで呼び出すかプリレンダリングやめるかで回避できる
* 下記が例、Counterのページで再読み込みしてもクリックの回数がリセットされない

```Counter.razor
@page "/counter"
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedLocalStorage ProtectedLocalStore

@if (isConnected)
{
    <p>Current count: <strong>@currentCount</strong></p>
    <button @onclick="IncrementCount">Increment</button>
}
else
{
    <p>Loading...</p>
}

@code {
    private int currentCount;
    private bool isConnected;// Sessionからデータ取得できたかどうか

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {// 初回読み込みのみ実行
            isConnected = true;
            // OnInitializedAsyncでは呼び出せない
            var result = await ProtectedLocalStore.GetAsync<int>("count");
            currentCount = result.Success ? result.Value : 0;
            StateHasChanged();
        }
    }

    private async Task IncrementCount()
    {
        currentCount++;
        await ProtectedLocalStore.SetAsync("count", currentCount);
    }
}
```