問題多々...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の作法のようです

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型で検証してくれるメソッドです。

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 を返してくれるという点を利用して実装しました。

// 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()メソッド使うよりは早いかと。たぶん...

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


ロギングしとく

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

追加するライブラリ


  • Microsoft.Extensions.Logging.Console

生成プロジェクトによるけど、これが入ってないと思いますので。

DbContext の継承クラスにて初期化

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);

... 以下略
}

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