Xamarin.Forms + Entity Framework Core SQLiteで組みたい場合に最初にやること

# 環境
- Windows10 Pro (20H2) 64bit
- Visual Studio 2019 16.8.4
- Xamarin 16.8.000.261

# 要約
たまーにXamarin触るんですけど、大体EFCoreとSQLite周りでハマります。
主にMigration出来るようになるまでの手順が分からなくなります。
そろそろプロジェクト作る都度ネットで色々調べて初期設定する状態から卒業したいので、
自分用メモ兼ねて最初の手順をまとめておきます。

# 手順
## Xamarin.Formsのプロジェクトを立てる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470282/507ea691-19c6-79c7-3167-d5824fc1228f.png)
ここは詰まるポイント無し。Visual Studioさんに全投げ。

## ModelとDbContext用のプロジェクトを立てる
データベースに保存するテーブル用クラスと、アクセス用DbContextをまとめるDLL用のプロジェクトを作ります。
.NET Standardのクラスライブラリで良いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470282/b6f36434-1c63-cc2b-5329-de15f12b400c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470282/c8269ea5-2b91-0042-e81e-4522f60bc29d.png)

## Migration用にコンソールアプリのプロジェクトを追加する
Migration実行するときには.NET Coreで動くプロジェクトが必要です。
Xamarinは動かせないので、Migrationをするためだけのダミープロジェクトを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470282/cbc8cd70-514b-35e6-d51a-06387a00cc4f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470282/1d725704-8c6e-d2fe-914c-3f4de7112016.png)

## Nugetから必要なパッケージをインストール
Mictosoft.EntityFramework.Core.Sqliteと、Mictosoft.EntityFramework.Core.ToolsをNugetからインストールします。

- Mictosoft.EntityFramework.Core.Sqlite
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470282/72126968-430e-5ff9-84da-6e33918fae58.png)

- Mictosoft.EntityFramework.Core.Tools
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470282/329fa560-ee3c-7aa1-4c85-d5cfb40cdbd3.png)

## Migration用プロジェクトからDLL用プロジェクトを参照設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470282/860530b9-b376-52f0-f6bb-798f342ad591.png)
こうやって

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470282/3b878b6f-715e-da58-ba15-ccab87e1f23d.png)
こう。ここも詰まるポイント無しです。

## ModelとDbContextを作成する
先に作ったDLL用プロジェクトにModelとDbContextを追加します。
ざっくり書いていきます。

```c#:Model\Bookshelf.cs
using System;
using System.Collections.Generic;
using System.Text;

namespace XamaEFCSqlite.Common.Model
{
    /// <summary>
    /// 本棚
    /// </summary>
    public class Bookshelf
    {
        /// <summary>
        /// ID
        /// </summary>
        public Guid BookshelfID { get; set; }

        /// <summary>
        /// 場所
        /// </summary>
        public string Location { get; set; }

        /// <summary>
        /// 容量
        /// </summary>
        public int Capacity { get; set; }

        public IList<Book> Books { get; set; }

        public Bookshelf()
        {
            BookshelfID = Guid.NewGuid();
            Location = string.Empty;
            Capacity = 0;
            Books = new List<Book>();
        }
    }
}
```

```c#:Model\Book.cs
using System;
using System.Collections.Generic;
using System.Text;

namespace XamaEFCSqlite.Common.Model
{
    /// <summary>
    /// 本
    /// </summary>
    public class Book
    {
        /// <summary>
        /// 本ID
        /// </summary>
        public Guid BookID { get; set; }

        /// <summary>
        /// タイトル
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        /// ページ数
        /// </summary>
        public int PageCount { get; set; }

        public Book()
        {
            BookID = Guid.NewGuid();
            Title = string.Empty;
            PageCount = 0;
        }
    }
}
```

```c#:StoreDbContext.cs
using Microsoft.EntityFrameworkCore;
using System;
using XamaEFCSqlite.Common.Model;

namespace XamaEFCSqlite.Common
{
    public class StoreDbContext : DbContext
    {
        public DbSet<Bookshelf> Bookshelves { get; set; }
        public DbSet<Book> Books { get; set; }
    }
}
```

とりあえずモデル２つとコンテキストを作りました。

## DbContextに細工
MigrationをするためにはDbContextにパラメータなしのコンストラクタと、データベースプロバイダーを構成する手段が必要です。
「データベースプロバイダーを構成する手段」で手っ取り早いのは、OnConfigureをoverrideすることっぽいので、実装していきます。

```c#:StoreDbContext.cs
using Microsoft.Data.Sqlite;            //ここを追加
using Microsoft.EntityFrameworkCore;
using System;
using XamaEFCSqlite.Common.Model;

namespace XamaEFCSqlite.Common
{
    public class StoreDbContext : DbContext
    {
        public DbSet<Bookshelf> Bookshelves { get; set; }
        public DbSet<Book> Books { get; set; }

        //--- ここから追加 ---
        const string dbName = "sample.db";
        string dbPath;

        public StoreDbContext() : this(string.Empty) { }

        public StoreDbContext(string DatabasePath)
        {
            dbPath = DatabasePath;
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);

            string path = System.IO.Path.Combine(dbPath, dbName);
            SqliteConnectionStringBuilder builder = new SqliteConnectionStringBuilder()
            {
                DataSource = path,
                ForeignKeys = true
            };
            optionsBuilder.UseSqlite(builder.ConnectionString);
        }
        //--- ここまで追加 ---
    }
}

```
いきなりごっそり増えましたね。
ここは正直**有識者がいらっしゃったら是非相談したいポイント**です。

SQLiteのプロバイダーを指定するため、OnConfiguring内でUseSqliteしています。
この時、UseSqliteの引数として接続文字列を渡す必要があります。

ファイル名は定数[dbName]に指定してありますが、ファイルを保存しておく場所は**実行するOS(AndroidとかiOSとか)によって変わります**。
その為、コンストラクタでファイルの場所だけ受け取れるようにしておいて、接続文字列を組み立てる際に完全パスに変えています。

そしてMigrationが実行できるように、パラメーターを受け取らないコンストラクタも定義します。
実際のコードの中でパラメーター無しコンストラクタを呼び出すと、ファイルの場所指定が空白のままOnConfiguring→UseSqliteされるので**結構ヤバいことになります**。変な所にSqliteDB作られるとか、アクセス不可でエラーになるとか……

なので、実際にコード書くときは[Obsolate]付けるとかで対処した方が安全かもしれませんね。
ちなみにMigration実行時はパスとか関係ありません。

ともあれ、これで準備が整いました。

## はじめてのMigration
「最初にやること」としては前セクションまでで終わってたりしますが、せっかくなのでMigrationのコマンドもおさらいしておきます。
Migrationクラスを作成するコマンドは

```shell
dotnet ef migrations add <Migration名> -s <Migration用プロジェクト名> -p <DLLプロジェクト名>
```
こんな感じです。今回の構成の場合は

```shell
dotnet ef migrations add InitialCreate -s XamaEFCSqlite.MigrationTool -p XamaEFCSqlite.Common
```
こうですね。
[dotnet ef]コマンドが見つからない的なエラーが出た場合は[この辺](https://docs.microsoft.com/ja-jp/ef/core/cli/dotnet)とか見てインストールしましょう。
参照設定ちゃんとしてるのに[Could not load assembly 'なんとか'. Ensure it is referenced by the startup project 'かんとか'.]見たいなエラーが出る場合は、一度リビルドすると良いかもしれません。

## 結果確認！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470282/d3195d04-3891-50f1-ae78-86cc954dd326.png)
出来ました。以降変更がある場合はさっきのコマンドを使ってMigrationクラスを作っていく形になります。

# まとめ
いつも困るところをまとめてみました。
一番最初にやる処理ってプロジェクトごとに1回しかやらないので結構忘れがちです。
自分用メモとしてもQiitaは有効だと思うので今後も書いていきます。


//後は文章ちゃんとまとめる能力が欲しい。
//記事作ると大体クソ長になるのやめたい。