LoginSignup
3
4

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-03-19

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

    ... 以下略
} 

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

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部分だけファイルで書き込みたいのですが、出来ませんかね...

3
4
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4