Подтвердить что ты не робот

Entity Framework с MySql и Migrations не работает, потому что "максимальная длина ключа составляет 767 байт"

[Изменить] Эта проблема решена! См. Инструкции в конце сообщения.

[Edit 2] Хорошо, этот поток устарел, а более новые версии MySQL Connector уже обрабатывают это с помощью MySQL EF-резольверов. Найдите ответ @KingPong на эту тему. Однако я его не тестировал.

Я пытаюсь использовать MySql и EntityFramework с Migrations, но что-то кажется неправильным.

Когда я вхожу 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 и получайте удовольствие!

4b9b3361

Ответ 1

Ответ перефразирован из Добавление пользовательского контекста MigrationHistory...

EF6 использует таблицу MigrationHistory для отслеживания изменений модели и обеспечения согласованности между схемой базы данных и концептуальной схемой. Эта таблица не работает для MySQL по умолчанию, потому что первичный ключ слишком велик. Чтобы исправить эту ситуацию, вам необходимо уменьшить размер ключа для этой таблицы.

По существу, EF6 позволяет вам изменять размер ключа для столбцов индекса MigrationId/ContextKey, используя Fluent API, например:

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

Полные инструкции здесь...

Ответ 2

С MySQL Connector/Net 6.8 и Entity Framework версии 6 вы можете решить эту проблему, используя встроенную поддержку MySQL для EF. Хитрость заключается в том, чтобы сообщить Entity Framework о том, как использовать решатели MySQL. В Руководство для разработчиков /Net Developer:

Это можно сделать тремя способами:

  • Добавление атрибута DbConfigurationTypeAttribute в класс контекста:

    [DbConfigurationType(typeof(MySqlEFConfiguration))]
  • Вызов DbConfiguration.SetConfiguration(новый 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

Ответ 3

Хотя принятый ответ ChAmp33n разрешил проблему, поднятую вопрошающим, однако я думаю, что после этого ответа произошли некоторые "взломанные" изменения. Я применил принятый ответ, но исключения остались.

Проверяя SQL-запросы, сгенерированные EF, я обнаружил, что проблема связана с UserName (varchar utf8 256) в таблице AspNetUsers и Name (varchar utf8 256) в таблице AspNetRoles, тогда как таблица для HistoryRow была в порядке.

Итак, следующие коды разрешили проблему.

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


}

Чтобы прояснить решение, вот библиотеки, которые я использую:

  • EF 6.1.0
  • Microsoft ASP.NET Identity EntityFramework 2.0.0
  • ASP.NET Identity Owin 2.0.0
  • Библиотеки MySql.NET 6.8.3

И это решение также работает для

  • EF 6.1.1
  • Microsoft ASP.NET Identity EntityFramework 2.0.1
  • Идентификатор ASP.NET Owin 2.0.1

Ответ 4

Посмотрите этот workitem на сайте EF CodePlex. Если вы используете EF6, вы можете настроить таблицу истории миграции и сделать столбцы короче - здесь - статья, которая показывает, как это сделать.

Ответ 5

Для тех, кто до сих пор получает эту головную боль, как я, я нашел решение самостоятельно. Перейдите в файл миграции (201603160820243_Initial.cs в моем случае), отредактируйте все maxLength: от 256 до 128. Запустите Update-database снова, и он работает для меня.

По-прежнему не знаю, почему nvarchar (256) (512 байт) не удовлетворяет 767 байтам, а обновления не относятся к столбцам ключей.

Ответ 6

Первичным ключом, который вы указали, является длинное число длиной в 1024 символа. MySQL принимает наихудший случай для размера символа при проверке ограничений размера столбца - возможно, 2 или 4 байта в зависимости от вашего набора символов и сортировки - так, чтобы либо 900, либо 1800 байт, слишком длинные.

Первичные ключи действительно не должны быть агрегированными ключами. Первичные ключи определяют порядок размещения таблицы на диске; это самоубийство. Сделайте первичный ключ целым числом и добавьте дополнительный ключ с требуемой структурой.

Ответ 7

Это связано с тем, что имя внешнего ключа, вычисленное по структуре сущностей, может быть слишком длинным. Мы можем указать имя внешнего ключа в файле migartion.cs. Четвертым параметром метода ForeignKey() является ForeignKeyName.

По умолчанию он выглядит примерно так:

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