.NETCore3.1＋MariaDB ＆ Identity導入

# はじめに
MariaDB(MySQL)を使った.NETCore3.1+IdentityのDocumentが少ないので作成。

ゼロから構築していくエントリーとしますので、最初から全部書いていきます。
KestrelからNginxに橋渡ししてますが、Webサーバは本稿のキモとは関係がないでしょう。

# 構成

| Architecture | Name |
| --- | --- |
| Machine | Azure |
| OS | CentOS 8 |
| Web Server | Nginx |
| Framework | .NETCore 3.1 |
| DB | Azure Database for MariaDB |

# 手順

## .NETCore 3.1 の導入

```
sudo su
﻿yum update -y
yum install -y libunwind libicu lsof
yum list | grep dotnet
yum install -y dotnet-sdk-3.1.x86_64
dotnet --version
```

## Projectの作成

```
mkdir /var/www/html -p
cd /var/www/html
dotnet new mvc --auth Individual -o TestProject
```

今回はMVCで立てます。


## MariaDB用のPackageを導入

```
cd TestProject
dotnet add package Pomelo.EntityFrameworkCore.MySql
```

## エディタでのSSH接続

以降に出てくるコマンド以外の編集作業はVisual Studio CodeあたりでSSH接続すれば非常に快適。
ググればエントリーは無限に出てきますので、ここでは割愛します。


## Kestrelの構築

`Program.cs`

    webBuilder.UseUrls("http://0.0.0.0:5000/"); //追記
    webBuilder.UseStartup<Startup>();

`/etc/systemd/system/kestrel-test.service`（新規作成）

    [Unit]
    Description=Example .NET Web API App running on CentOS

    [Service]
    WorkingDirectory=/var/www/html/TestProject
    ExecStart=/usr/bin/dotnet /var/www/html/TestProject/published/TestProject.dll
    Restart=always
    # Restart service after 10 seconds if the dotnet service crashes:
    RestartSec=10
    KillSignal=SIGINT
    SyslogIdentifier=dotnet-example
    User=root
    Environment=ASPNETCORE_ENVIRONMENT=Production
    #Environment=ConnectionStrings__DefaultConnection={Connection String}
    Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false

    [Install]
    WantedBy=multi-user.target

コマンド実行

```
systemctl daemon-reload
systemctl enable kestrel-test.service
systemctl restart kestrel-test.service
```

## nginxの構築

`/etc/nginx/nginx.conf`

    server_name  write.your.ip.address;

    location / {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

```
nginx -s reload
```

## SELinuxによるアクセス制御設定

```
setsebool -P httpd_can_network_connect 1
```


## HTTPS Redirectionの一時停止（省略可）
`Startup.cs`

    //app.UseHttpsRedirection();    //コメントアウト

## MariaDBの構築

```
yum install mariadb -y
mysql -h write.mariadb.ip.address -u username -p
create database dbname;
exit
```


## .NETCoreとMariaDBとの接続

`appsettings.json`

```
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=write.mariadb.ip.address;userid=username;password=yourpassword;database=dbname;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
``` 

`Startup.cs`

``` C#
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseMySql(
            Configuration.GetConnectionString("DefaultConnection")));
```

---

## .NETCore Identityによる認証の実装
### Identity関連TableのMigration File修正
MariaDB(MySql)の環境下においては、IdentityがDefaultで持っているMigation Fileを無修正で実行できない。
　⇒nvarchar(MAX)やDateTimeOffset型がないため。

`Data/Migrations/00000000000000_CreateIdentitySchema.cs`
・DateTimeOffsetをひとまず`<string>`に書き換え。

たとえばこんなかんじ

``` C#
// LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
LockoutEnd = table.Column<string>(nullable: true),
```


`Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs`
・`nvarchar(MAX)`をひとまず`nvarchar(1024)`で置換（そんなにいらないかもしれない）。
・`DateTimeOffset`をひとまず`<string>`の`nvarchar(64)`に書き換え。
※DateTimeOffsetカラムについては、Defaultの設定だと何のデータもInsertされてこないのでErrorにはならない。

たとえばこんなかんじ

``` C#
b.Property<string>("ConcurrencyStamp")
   .IsConcurrencyToken()
   .HasColumnType("nvarchar(1024)");
```

### Migrationの実行

準備ができたらMigrationを実行する。

```
dotnet ef database update
```

Migrationの実行でエラーが出てしまった場合は、DB側に出来てしまったTable群を削除し、再実行する。
　⇒Foreign制約がかかっているため、綺麗に同時消しができなかったりする。Tableを消すときの順番に注意。

なお、成功した場合は以下のようになる。

```
$ dotnet ef database update
Build started...
Build succeeded.
Done.
```

Migrationに成功した場合であっても、やり直したいと思うことがあるかもしれない。
その場合は、Migration Fileの編集＋Table群のDropで再度Migrationを実行することもできる。
ただし、Migrationが一度完了となった旨の情報が`__efmigrationshistory`Tableに格納されているので、
Migrationの再実行をする前に、DB側で以下コマンドの実行が必要となる。

```
truncate table __efmigrationshistory;
```

　⇒上記Truncateを実行しないと、いくら`dotnet ef database update`しようとも、何も実行しないMigrationがDoneになるだけとなる。

## Identity関連のスキャフォールディング

```
dotnet tool install -g dotnet-aspnet-codegenerator
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet aspnet-codegenerator identity -dc TestProject.Data.ApplicationDbContext --files "Account.Register;Account.Login;Account.Logout"
```
最後の行にはProject名が刺さっているので、適宜変更されたい。

---

# 実行

## 恒久的な実行

既にNginxとKestrelを常時立ち上げるように設定していますので、あとはコンパイルするだけ。

```
dotnet publish -o published -r centos.8-x64 && systemctl restart kestrel-test.service
```

コンパイル中にブラウザからアクセスしてしまう等の原因により、Processがバインドされ、上記コマンドがエラーとなることがまれに良くある。
この問題は`reboot`すれば解決するが、いちいち再起動は地獄。
そんな時は、先に`systemctl restart kestrel-test.service`を実行してやると回復する。
というか、なんなら最初から以下のコマンドをおすすめしておく。

```
systemctl restart kestrel-test.service && dotnet publish -o published -r centos.8-x64 && systemctl restart kestrel-test.service
```

ズボラ極まりないコマンドだが、こうかはばつぐんだ。

## 開発中の実行

開発中のデバッグ作業であれば、Visual Studio Codeのターミナルから`dotnet run`すれば非常に便利。
デバッグ用のConsole.WriteLineの出力も同じくターミナルのところに出力されるようになるので、開発ではどんどん使っていきましょう。
ただ、既に `kestrel-test.service`を走らせてしまっているので、これを一旦止める必要がある。

```
systemctl stop kestrel-test.service
dotnet run
```


# おわり
これでブラウザ上のRegisterからユーザ登録ができると思います。
とっつきにくさ天元突破のMicrosoft公式Documentに頼らざるを得ない部分も多いので、辛みが深いですね。