web-dev-qa-db-fra.com

Entity Framework avec MySql et Migrations échouant car "la longueur de clé maximale est de 767 octets"

[Modifier] Ce problème a été résolu! Voir les instructions à la fin de l'article.

[Edit 2] Ok, ce fil est ancien, et les nouvelles versions de MySQL Connector le gèrent déjà avec les résolveurs MySQL EF. Recherchez la réponse @KingPong sur ce fil. Je ne l'ai cependant pas testé.

J'essaie d'utiliser MySql et EntityFramework avec les migrations, mais quelque chose semble ne pas fonctionner.

Lorsque j'entre Update-Database -Verbose dans la console du gestionnaire de packages, EF exécute certaines requêtes qui "refléteront" mes classes de modèle, et tout se passe parfaitement, MAIS EF essaie ensuite d'exécuter cette requête:

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

Et le résultat est: Specified key was too long; max key length is 767 bytes

J'ai essayé de changer mon classement de base de données en utf-8, mais toujours le même. Peut-être que la longueur de la clé est de 450 caractères, en faisant le calcul UTF-8 (ce qui peut être faux), je pense qu'il essaie de créer une clé d'environ 1800 octets de longueur.

Depuis que je suis nouveau sur EF, j'ai suivi quelques tutoriels et ils m'ont dit de faire ceci:

    public Configuration()
    {
        AutomaticMigrationsEnabled = false;

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

Peut-être que ce générateur SQL fait la mauvaise chose, ou EF lui-même demande au générateur de créer une clé jusqu'à 767 octets.

Comment puis-je résoudre ce problème, éviter ce problème et le faire fonctionner avec MySql?

[Modifier] Ok, ce problème a été résolu. Vous devez dire à EF qu'il doit changer la façon dont il génère la table __MigrationHistory.

Ce que j'ai fait: créez d'abord un fichier appelé MySqlHistoryContext.cs (ou ce que vous voulez) avec ce contenu:

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

Vous pourriez avoir un fichier appelé Configuration.cs dans votre dossier Migrations. Si oui, faites les ajustements nécessaires, sinon créez un nouveau fichier. En fait, vous ne pourrez pas obtenir cette erreur si vous n'avez pas ce fichier, car EF le crée automatiquement lorsque vous 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
    }
}

Alors Update-Database -Verbose et amusez-vous!

32
Ricardo Pieper

Réponse paraphrasée de Ajout d'un contexte MigrationHistory personnalisé ...

EF6 utilise une table MigrationHistory pour suivre les modifications du modèle et assurer la cohérence entre le schéma de la base de données et le schéma conceptuel. Cette table ne fonctionne pas pour MySQL par défaut car la clé primaire est trop grande. Pour remédier à cette situation, vous devrez réduire la taille de clé de cette table.

Essentiellement, EF6 vous permet de modifier la taille de clé pour les colonnes d'index MigrationId/ContextKey à l'aide de l'API Fluent comme ceci:

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

Compléter les instructions ici ...

34
ChAmp33n

Avec MySQL Connector/Net 6.8 et Entity Framework version 6, vous pouvez résoudre ce problème en utilisant la prise en charge intégrée de MySQL pour EF. L'astuce consiste à dire à Entity Framework d'utiliser les résolveurs MySQL. Dans le Connector/Net Developer Guide :

Cela peut se faire de trois manières:

  • Ajout de DbConfigurationTypeAttribute sur la classe de contexte:

    [DbConfigurationType (typeof (MySqlEFConfiguration))]
  • Appel de DbConfiguration.SetConfiguration (nouveau MySqlEFConfiguration ()) au démarrage de l'application

  • Définissez le type DbConfiguration dans le fichier de configuration:

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

Il est également possible de créer une classe DbConfiguration personnalisée et d'ajouter les résolveurs de dépendances nécessaires.

Lorsque j'ai suivi ces instructions (j'ai utilisé l'approche du fichier de configuration), la table a été créée avec succès. Il a utilisé le DDL suivant:

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
33
KingPong

Bien que la réponse acceptée par ChAmp33n ait résolu le problème soulevé par la personne qui a posé la question, je pense cependant qu'il y a eu des changements "de rupture" après cette réponse. J'avais appliqué la réponse acceptée mais les exceptions demeuraient.

En vérifiant les requêtes SQL générées par EF, j'avais trouvé que le problème était lié à UserName (varchar utf8 256) dans la table AspNetUsers et Name (varchar utf8 256) dans la table AspNetRoles, tandis que la table pour HistoryRow était correcte.

Les codes suivants ont donc résolu le problème.

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


}

Pour clarifier la solution, voici les bibliothèques que j'utilise:

  1. EF 6.1.0
  2. Microsoft ASP.NET Identity EntityFramework 2.0.0
  3. ASP.NET Identity Owin 2.0.0
  4. Bibliothèques MySql .NET 6.8.3

Et cette solution fonctionne également pour

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

Pour ceux qui ont encore ce mal de tête comme moi, j'ai moi-même trouvé une solution. Accédez au fichier de migration (201603160820243_Initial.cs dans mon cas), modifiez toute la longueur maximale: 256 à 128. Exécutez à nouveau Update-database et cela fonctionne pour moi.

Je ne sais toujours pas pourquoi nvarchar (256) (512 octets) n'est pas satisfait 767 octets et les mises à jour ne concernent pas les colonnes de clés.

2
Nam Bui

Jetez un oeil à ce workitem sur le site EF CodePlex. Si vous utilisez EF6, vous pouvez configurer la table d'historique des migrations et raccourcir les colonnes - ici est un article qui montre comment procéder.

2
Pawel

La clé primaire que vous avez spécifiée comporte 450 caractères unicode. MySQL suppose le pire des cas pour la taille des caractères lors de la vérification des limites de taille de colonne - 2 ou 4 octets probablement, selon votre jeu de caractères et votre classement - c'est donc 900 ou 1800 octets, les deux trop longs.

Les clés primaires ne devraient vraiment pas être des clés agrégées. Les clés primaires régissent la disposition de la table sur le disque; c'est un suicide de performance. Faites de la clé primaire un entier et attachez une clé secondaire avec la structure souhaitée.

0
John Haugeland

En effet, le nom de la clé étrangère calculé par le framework d'entité peut être trop long. Nous pouvons spécifier le nom de la clé étrangère dans le fichier migartion.cs. Le quatrième paramètre de la méthode ForeignKey() est ForeignKeyName.

Par défaut, cela ressemble à ceci:

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