将 Entity Framework Code First 与 mysql 一起使用时强制 engine=innodb

1 mysql asp.net-mvc entity-framework entity-framework-6 entity-framework-migrations

我使用实体框架 6 和 msyql 数据库创建了一个新的 .NET MVC 5 Web 应用程序。我首先使用代码/模型。数据库服务器有一个默认的存储引擎 MyISAM,但我希望 EF 创建的表是 InnoDb。有谁知道是否有办法指定 EF 将在CREATE TABLE语句中使用的存储引擎?

bub*_*ubi 5

实际上,MySQL EF 提供程序使用的引擎始终是 InnoDB,如果不重写 DDL 生成器,就无法更改它。

要尝试,您可以创建一个简单的项目并启用 MySQL 登录。你会注意到每个 create 语句都会以engine=InnoDb auto_increment=0

例如这个类

public class Blog
{
    public int BlogId { get; set; }
    [MaxLength(200)]
    public string Name { get; set; }
    [MaxLength(200)]
    public string Topic { get; set; }
    public DateTime LastUpdated { get; set; }

    [DefaultValue(0)]
    public int Order { get; set; }
    public virtual List<Post> Posts { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

使用标准 MySQL EF 提供程序迁移,生成此 MySQL DDL 语句

CREATE TABLE `Blogs` (
    `BlogId` INT NOT NULL auto_increment,
    `Name` NVARCHAR(200),
    `Topic` NVARCHAR(200),
    `LastUpdated` DATETIME NOT NULL,
    `Order` INT NOT NULL,
    PRIMARY KEY (`BlogId`)
    ) engine = InnoDb auto_increment = 0
Run Code Online (Sandbox Code Playgroud)

engine = InnoDb来自哪里?它在迁移源代码中进行了硬编码。
您可以查看迁移源代码 https://github.com/mysql/mysql-connector-net/blob/6.9/Source/MySql.Data.EntityFramework5/MySqlMigrationSqlGenerator.cs 方法MySqlMigrationSqlGenerator.Generate(CreateTableOperation op)。最后一句话是sb.Append(") engine=InnoDb auto_increment=0");

因此,正确的问题应该是如何将引擎从 InnoDB 更改为另一个引擎。您可以继承 MySqlMigrationSqlGenerator 类并覆盖该方法,即:

internal class MyOwnMigrationSqlGenerator : MySqlMigrationSqlGenerator
{

    public MyOwnMigrationSqlGenerator()
    {
        Engine = "InnoDB";
    }

    public MyOwnMigrationSqlGenerator(string engine)
    {
        Engine = engine;
    }




    private readonly List<MigrationStatement> _specialStatements = new List<MigrationStatement>();

    public string Engine { get; set; }

    public override IEnumerable<MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken)
    {
        List<MigrationStatement> migrationStatements = base.Generate(migrationOperations, providerManifestToken).ToList();
        migrationStatements.AddRange(_specialStatements);
        return migrationStatements;
    }

    protected override MigrationStatement Generate(CreateTableOperation op)
    {
        StringBuilder sb = new StringBuilder();
        string tableName = TrimSchemaPrefix(op.Name);
        var autoIncrementCols = (List<string>)(typeof(MySqlMigrationSqlGenerator).GetProperty("autoIncrementCols", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this));
        var primaryKeyCols = (List<string>)(typeof(MySqlMigrationSqlGenerator).GetProperty("primaryKeyCols", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this));


        sb.Append("create table " + "`" + tableName + "`" + " (");


        if (op.PrimaryKey != null)
        {
            op.PrimaryKey.Columns.ToList().ForEach(col => primaryKeyCols.Add(col));
        }


        //columns 
        sb.Append(string.Join(",", op.Columns.Select(c => "`" + c.Name + "` " + Generate(c))));


        // Determine columns that are GUID & identity 
        List<ColumnModel> guidCols = new List<ColumnModel>();
        ColumnModel guidPk = null;
        foreach (ColumnModel columnModel in op.Columns)
        {
            if (columnModel.Type == PrimitiveTypeKind.Guid && columnModel.IsIdentity && String.Compare(columnModel.StoreType, "CHAR(36) BINARY", true) == 0)
            {
                if (primaryKeyCols.Contains(columnModel.Name))
                    guidPk = columnModel;
                guidCols.Add(columnModel);
            }
        }


        if (guidCols.Count != 0)
        {
            var createTrigger = new StringBuilder();
            createTrigger.AppendLine(string.Format("DROP TRIGGER IF EXISTS `{0}_IdentityTgr`;", TrimSchemaPrefix(tableName)));
            createTrigger.AppendLine(string.Format("CREATE TRIGGER `{0}_IdentityTgr` BEFORE INSERT ON `{0}`", TrimSchemaPrefix(tableName)));
            createTrigger.AppendLine("FOR EACH ROW BEGIN");
            foreach (ColumnModel opCol in guidCols)
                createTrigger.AppendLine(string.Format("SET NEW.{0} = UUID();", opCol.Name));
            createTrigger.AppendLine(string.Format("DROP TEMPORARY TABLE IF EXISTS tmpIdentity_{0};", TrimSchemaPrefix(tableName)));
            createTrigger.AppendLine(string.Format("CREATE TEMPORARY TABLE tmpIdentity_{0} (guid CHAR(36))ENGINE=MEMORY;", TrimSchemaPrefix(tableName)));
            createTrigger.AppendLine(string.Format("INSERT INTO tmpIdentity_{0} VALUES(New.{1});", TrimSchemaPrefix(tableName), guidPk.Name));
            createTrigger.AppendLine("END");
            var sqlOp = new SqlOperation(createTrigger.ToString());
            _specialStatements.Add(Generate(sqlOp));
        }


        if (op.PrimaryKey != null) // && !sb.ToString().Contains("primary key")) 
        {
            sb.Append(",");
            sb.Append("primary key ( " + string.Join(",", op.PrimaryKey.Columns.Select(c => "`" + c + "`")) + ") ");
        }


        string keyFields = ",";
        autoIncrementCols.ForEach(col => keyFields += (!primaryKeyCols.Contains(col) ? string.Format(" KEY (`{0}`),", col) : ""));
        sb.Append(keyFields.Substring(0, keyFields.LastIndexOf(",")));
        sb.Append(string.Format(") engine={0} auto_increment=0", Engine));

        return new MigrationStatement() { Sql = sb.ToString() };
    }

    private string TrimSchemaPrefix(string table)
    {
        if (table.StartsWith("dbo.") || table.Contains("dbo."))
            return table.Replace("dbo.", "");


        return table;
    }

}
Run Code Online (Sandbox Code Playgroud)

然后,在您的迁移配置中,您可以指定自己的 sql 生成器。

internal sealed class MyContextMigrationConfiguration : DbMigrationsConfiguration<MyContext>
{
    public MyContextMigrationConfiguration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = true;
        SetSqlGenerator("MySql.Data.MySqlClient", new MyOwnMigrationSqlGenerator("MyPreferredEngine"));
    }

}
Run Code Online (Sandbox Code Playgroud)

编辑
MyOwnMigrationSqlGenerator 类有一个错误。可能最好的办法是重写所有 MySqlMigrationSqlGenerator。在这种情况下,我只是修复了访问 MySqlMigrationSqlGenerator 私有字段的类(这很糟糕)。