問題多々...MySQL/MariaDBをMySQL.EntityFrameworkCoreで使いこなす

# ASP.NET Core/.NET Core でMySQL/MariaDBにあるデータを操作しようとすると問題が多々発生

Oracleの純正MySQL EntityFramework Coreのライブラリは、いくつか足りないメソッドがあったり、結果が違ったりする。
また、いくつかのメソッドがないので、代用方法で駆使する必要があり、覚えるのが大変なので、ちょっとその当たりを記録しておこうと思う。

## 環境

MySQL.Data.EntityFrameworkCoreを利用すると起こる現象なので、データベースはMySql/MariaDBどちらでも問題ないと思われる。

* MySql.Data (8.0.15)
* MySql.Data.EntityFrameworkCore (8.0.15)
* .NET Core 2.2
* Windows/Mac/Linuxどれでも問題なく発生してくれる

## Index/Uniqueの設定は...

UniqueやIndexのあるテーブルがあり、スキャフォさせるために設定する Index というのが EntityFrameworkにあるということが書かれていたのですが、こちらMySqlEntityFrameworkには見当たらないのです。
正解は、Fluent APIを利用するとのこと。
なお、プライマリキーも1つのときは、[Key]で指定できますが、複数の場合はFluent APIを利用して定義するのが、EntityFrameworkの作法のようです

```c#
using Microsoft.EntityFrameworkCore;
using MySql.Data.MySqlClient;

[Table("test")]
public class TestInfo {
    [Key]
    [Column("id", TypeName = "int(11)")]
    public int ID { set; get }

    // id, name での複合プライマリキー
    [Column("name", TypeName = "varchar(100)")]
    public string Name { set; get; }

    // email は unique
    [Column("email", TypeName = "varchar(255)")]
    public string Email { set;get; }
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // Index/unique設定
    modelBuilder.Entity<TestInfo>()
        .HasIndex( t => t.Email )
        .IsUnique();
    // 複合プライマリキー
    modelBuilder.Entity<TestInfo>()
        .HasKey( g => new { g.ID, g.Name } );
}
```

## 動作しない Any()

Any() メソッドは、存在する場合 true 存在しない場合 false と、bool型で検証してくれるメソッドです。

```C#
var ret = MasterContext.Where( g => g.email == "xxxxx@xxxxxxxx.xxx" ).Any(); // No coercion operator is defined between types 'System.Int16' and 'System.Boolean'.
if( ret ) [true] else [false];
```

でも、例外が返ります。どうやら、MySQLのEntityFrameworkCoreにはちゃんと実装されてくれてないみたいです。とはいえ、.Count() メソッドを使うと COUNT(*) で数を数えてくれるのでめちゃくちゃ遅いです。そこで、FirstOrDefault() メソッドを利用することで、データが無ければ null を返してくれるという点を利用して実装しました。

```C#
// Any()メソッドはbool以外が返ってきて例外がでるので、別回避する必要あり
var ret = ( MasterContext.Where( g => g.Email == "xxxxx@xxxxxxxx.xxx" ).Select( g => new { g.Email } ).FirstOrDefault()) != null;
if( ret ) [true] else [false];
```

この方法だと、以下のSQLが吐き出されるので多分、Count()メソッド使うよりは早いかと。たぶん...

```SQL
SELECT g.email WHERE g.email = 'xxxxx@xxxxxxxx.xxx' LIMIT 1;
```

## ロギングしとく

まだ、チューニング方法や猫自身がEntityFrameworkを使うのがお初なので、コーディング方法を確立出来てないので、もしかするとまだまだ問題が出るかも。なので、そのために、そのSQL大丈夫？問いのを確認するために、ロギングする方法。

追加するライブラリ

* Microsoft.Extensions.Logging.Console

生成プロジェクトによるけど、これが入ってないと思いますので。
DbContext の継承クラスにて初期化

```c#
public class HogeHogeContext : DbContext 
{
    // 接続情報を保存するための入れ物
    protected MySqlConnectionStringBuilder Config { private set; get; } = new MySqlConnectionStringBuilder();

    // MySQLの接続情報を生成するためのおまじない
    public HogeHogeContext(AppSettings settings) {
        Config.Server = settings.Server;
        Config.Port = settings.Port;
        Config.UserID = settings.User;
        Config.Password = settings.Pass;
        Config.Database = settings.Schema;
        Config.ConnectionTimeout = settings.ConnectionTimeout;
        Config.DefaultCommandTimeout = settings.CommandTimeout;
    }

    // ログを出力させるためのおまじない
    public static readonly LoggerFactory LoggerFactory = new LoggerFactory(
        new[] {
            new ConsoleLoggerProvider( (category, level ) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information, true );
        }
    });

    // ロギング設定とデータベースの接続設定
    protected override void OnConfiguring( DbContextOptionsBuilder optionsBuilder ) =>
        optionsBuilder
            .UseMySQL(Config.ConnectionsString)
            .EnableSensitiveDataLogging()
            .UseLoggerFactory(LoggerFactory);

    ... 以下略
} 

```

上記、dotnet 2.x までらしいので、 dotnet 3.x からは、以下のように書くといいらしい。

```c#
using Microsoft.Extensions.Logging;

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            var logger = LoggerFactory.Create(builder =>
            {
                builder.AddDebug()
                .AddFilter(category: DbLoggerCategory.Database.Command.Name, level: LogLevel.Information);
            });
            optionsBuilder.UseMySql(Config.ConnectionString).UseLoggerFactory(logger);

    ... 以下略
```

あとは、コンソール画面に勝手に出力してくれます。ただ、いまのこのConsoleLoggerProvider() クラスの書き方、古いらしく、新しく書き換えローってIDEに言われます。新しい書き方はまだ調査中。あと、コンソール画面に多数ログが出るので、SQL部分だけファイルで書き込みたいのですが、出来ませんかね...