具有MySql和迁移的实体框架失败,因为“最大密钥长度为767字节”

时间:2013-12-30 02:37:00

标签: c# mysql entity-framework

[编辑] 此问题已解决!请参阅帖子末尾的说明。

[编辑2] 好的,这个线程已经过时了,MySQL Connector的新版本已经使用MySQL EF解析器处理了这个问题。在这个帖子上寻找@KingPong的答案。不过,我还没有测试过它。

我正在尝试使用MySql和EntityFramework进行迁移,但似乎有些错误。

当我在软件包管理器控制台中输入Update-Database -Verbose时,EF会执行一些“镜像”我的模型类的查询,一切都很完美,但是EF尝试执行此查询:

create table `__MigrationHistory` 
(
  `MigrationId` varchar(150)  not null 
  ,`ContextKey` varchar(300)  not null 
  ,`Model` longblob not null 
  ,`ProductVersion` varchar(32)  not null
  ,primary key ( `MigrationId`,`ContextKey`) 
 ) engine=InnoDb auto_increment=0

结果是:Specified key was too long; max key length is 767 bytes

我尝试将数据库排序规则更改为utf-8,但仍然相同。也许关键长度是450个字符,做UTF-8数学(​​我可能错了),我认为它试图创建一个大约1800字节长度的密钥。

由于我是EF新手,我接受了一些教程,他们告诉我这样做:

    public Configuration()
    {
        AutomaticMigrationsEnabled = false;

        SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator());
    }

也许这个SQL生成器做错了,或者EF本身要求生成器生成一个高达767字节的密钥。

我该如何解决这个问题,避免这个问题并让它与MySql一起使用?

[编辑] 好的,这个问题解决了。你必须告诉EF它必须改变它生成__MigrationHistory表的方式。

我做了什么: 首先,使用以下内容创建名为MySqlHistoryContext.cs(或任何您想要的)的文件:

...
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Migrations.History;


namespace [YOUR NAMESPACE].Migrations //you can put any namespace here, but be sure you will put the corret using statement in the next file. Just create a new class :D
{
    public class MySqlHistoryContext : HistoryContext
    {

        public MySqlHistoryContext(DbConnection connection, string defaultSchema):base(connection,defaultSchema)
        {

        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<HistoryRow>().Property(h => h.MigrationId).HasMaxLength(100).IsRequired();
            modelBuilder.Entity<HistoryRow>().Property(h => h.ContextKey).HasMaxLength(200).IsRequired(); 
        }
    }
}

您的Configuration.cs文件夹中可能有一个名为Migrations的文件。如果是,请进行必要的调整,否则创建新文件。实际上如果你没有这个文件,你将无法得到这个错误,因为EF会在你Add-Migration [name]时自动创建它。

namespace [YOUR NAMESPACE].Migrations
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Migrations;
    using System.Linq;

    internal sealed class Configuration : DbMigrationsConfiguration<CodeFirstMySql.Models.Context>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;

            SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); //it will generate MySql commands instead of SqlServer commands.

            SetHistoryContextFactory("MySql.Data.MySqlClient", (conn, schema) => new MySqlHistoryContext(conn, schema)); //here s the thing.



        }

        protected override void Seed(CodeFirstMySql.Models.Context context){}//ommited
    }
}

然后Update-Database -Verbose玩得开心!

7 个答案:

答案 0 :(得分:34)

回答Adding custom MigrationHistory context ...

的解释

EF6使用 MigrationHistory 表来跟踪模型更改并确保数据库架构和概念架构之间的一致性。默认情况下,此表不适用于MySQL,因为主键太大。要解决此问题,您需要缩小该表的密钥大小。

基本上,EF6允许您使用Fluent API修改MigrationId / ContextKey索引列的密钥大小,如下所示:

modelBuilder.Entity<HistoryRow>().Property(h => h.MigrationId).HasMaxLength(100).IsRequired();
modelBuilder.Entity<HistoryRow>().Property(h => h.ContextKey).HasMaxLength(200).IsRequired();

Complete Instructions Here...

答案 1 :(得分:33)

使用MySQL Connector / Net 6.8和Entity Framework第6版,您可以使用MySQL内置的EF支持解决此问题。诀窍是告诉实体框架使用MySQL解析器。来自Connector/Net Developer Guide

  

这可以通过三种方式完成:

     
      
  • 在上下文类中添加DbConfigurationTypeAttribute:

    [DbConfigurationType(typeof(MySqlEFConfiguration))]
  •   
  • 在应用程序启动时调用DbConfiguration.SetConfiguration(new MySqlEFConfiguration())

  •   
  • 在配置文件中设置DbConfiguration类型:

    <entityFramework codeConfigurationType="MySql.Data.Entity.MySqlEFConfiguration, MySql.Data.Entity.EF6">
  •   
     

也可以创建自定义DbConfiguration类并添加所需的依赖项解析器。

当我按照这些说明(我使用配置文件方法)时,表已成功创建。它使用以下DDL:

create table `__MigrationHistory` (
  `MigrationId` nvarchar(150) not null,
  `ContextKey` nvarchar(300)  not null,
  `Model` longblob not null,
  `ProductVersion` nvarchar(32) not null,
  primary key ( `MigrationId`)
) engine=InnoDb auto_increment=0

答案 2 :(得分:9)

虽然ChAmp33n接受的答案已经解决了提问者提出的问题,但我认为在答案之后会有一些“突破”的变化。我已经应用了接受的答案,但例外仍然存在。

通过检查EF生成的SQL查询,我发现问题与表AspNetRoles中的表AspNetUsers和Name(varchar utf8 256)中的UserName(varchar utf8 256)有关,而HistoryRow的表很好。 / p>

所以以下代码解决了这个问题。

    public class WebPortalDbContext : IdentityDbContext<ApplicationUser>
{
    public WebPortalDbContext()
        : base("IdentityConnection")
    {

    }

    public static WebPortalDbContext Create()
    {
        return new WebPortalDbContext();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Microsoft.AspNet.Identity.EntityFramework.IdentityRole>()
            .Property(c => c.Name).HasMaxLength(128).IsRequired();

        modelBuilder.Entity<Microsoft.AspNet.Identity.EntityFramework.IdentityUser>().ToTable("AspNetUsers")//I have to declare the table name, otherwise IdentityUser will be created
            .Property(c => c.UserName).HasMaxLength(128).IsRequired();
    }


}

为了澄清解决方案,这里是我正在使用的库:

  1. EF 6.1.0
  2. Microsoft ASP.NET Identity EntityFramework 2.0.0
  3. ASP.NET Identity Owin 2.0.0
  4. MySql .NET库6.8.3
  5. 此解决方案也适用于

    1. EF 6.1.1
    2. Microsoft ASP.NET Identity EntityFramework 2.0.1
    3. ASP.NET Identity Owin 2.0.1

答案 3 :(得分:2)

查看EF CodePlex网站上的this workitem。如果您使用的是EF6,则可以配置迁移历史记录表并缩短列数 - here是一篇展示如何执行此操作的文章。

答案 4 :(得分:2)

对于那些像我一样头疼的人,我自己找到了解决方案。 转到迁移文件(在我的例子中是201603160820243_Initial.cs),编辑所有maxLength:256到128.再次运行Update-database,它适用于我。

仍然不知道为什么nvarchar(256)(512字节)不满足767字节,并且更新不适用于键列。

答案 5 :(得分:0)

您指定的主键长度为450个unicode字符。在检查列大小限制时,MySQL假定字符大小最坏的情况 - 可能是2或4个字节,具体取决于您的字符集和排序规则 - 所以这些都是900或1800字节,都太长了。

主键实际上不应该是聚合键。主键控制表格在磁盘上的布局方式;这是表现自杀。使主键成为整数,并附加具有所需结构的辅助键。

答案 6 :(得分:0)

这是因为实体框架计算的外键名称可能太长。我们可以在migartion.cs文件中指定外键名。 ForeignKey()方法的第四个参数是ForeignKeyName

默认情况下,它看起来像这样:

.ForeignKey("xxx", t => t.yyy) <br/>
Modify it as: <br/>
.ForeignKey("xxx", t => t.yyy,false, "YourForeignKeyName")