All Articles

Resolvendo o erro “Specified key was too long” no Identity Core com MySQL

Já havia me deparado com esse erro anteriormente em um projeto Laravel. Nesse cenário a solução foi bem simples bastando editar o AppServiceProvider e definir o tamanho máximo para strings no método boot:

use Illuminate\Support\Facades\Schema;

public function boot()  
{  
    Schema::defaultStringLength(191);  
}

Hoje tive o mesmo problema em um projeto .Net Core, o que me fez procurar por uma solução direta no MySQL sem precisar alterar o tamanho da string pelo código.

Descobri que não existe uma solução definitiva no banco de dados já que o MySQL tem um limite de 767 bytes usando a engine InnoDB e 1000 bytes para a engine MYISAM, nesse caso o jeito é resolver o problema no código.

No projeto estou usando o Identity para autenticação dos usuários e ao gerar as tabelas o limite de bytes é estourado:

fail: Microsoft.EntityFrameworkCore.Database.Command\[20102\]  
      Failed executing DbCommand (1ms) \[Parameters=\[\], CommandType='Text', Comma  
ndTimeout='30'\]  
      CREATE TABLE \`AspNetUserLogins\` (  
          \`LoginProvider\` varchar(767) NOT NULL,  
          \`ProviderKey\` varchar(767) NOT NULL,  
          \`ProviderDisplayName\` text NULL,  
          \`UserId\` varchar(767) NOT NULL,  
          PRIMARY KEY (\`LoginProvider\`, \`ProviderKey\`),  
          CONSTRAINT \`FK\_AspNetUserLogins\_AspNetUsers\_UserId\` FOREIGN KEY (\`User  
Id\`) REFERENCES \`AspNetUsers\` (\`Id\`) ON DELETE CASCADE  
      );  
MySql.Data.MySqlClient.MySqlException (0x80004005): Specified key was too long;  
max key length is 1000 bytes

Podemos ver que ao criar a tabela AspNetUserLogins o Identity define um tamanho que ultrapassa o máximo permitido pela engine do MySQL.

Para resolver o problema vamos alterar a forma que o Identity define o tamanho dos campos para criação das tabelas. Para isso iremos sobrescrever o método OnModelCreating no DbContext da aplicação:

public class ApplicationDbContext: IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
           : base(options)
        {
        }


        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
              modelBuilder.Entity<ApplicationUser>(entity => {
                entity.Property(m => m.Id).HasMaxLength(110);
                entity.Property(m => m.Email).HasMaxLength(127);
                entity.Property(m => m.NormalizedEmail).HasMaxLength(127);
                entity.Property(m => m.NormalizedUserName).HasMaxLength(127);
                entity.Property(m => m.UserName).HasMaxLength(127);
            });
            modelBuilder.Entity<IdentityRole>(entity => {
                entity.Property(m => m.Id).HasMaxLength(200);
                entity.Property(m => m.Name).HasMaxLength(127);
                entity.Property(m => m.NormalizedName).HasMaxLength(127);
            });
            modelBuilder.Entity<IdentityUserLogin<string>>(entity =>
            {
                entity.Property(m => m.LoginProvider).HasMaxLength(127);
                entity.Property(m => m.ProviderKey).HasMaxLength(127);
            });
            modelBuilder.Entity<IdentityUserRole<string>>(entity =>
            {
                entity.Property(m => m.UserId).HasMaxLength(127);
                entity.Property(m => m.RoleId).HasMaxLength(127);
            });
            modelBuilder.Entity<IdentityUserToken<string>>(entity =>
            {
                entity.Property(m => m.UserId).HasMaxLength(110);
                entity.Property(m => m.LoginProvider).HasMaxLength(110);
                entity.Property(m => m.Name).HasMaxLength(110);               
                
            });



        }

    }

Com isso reduzimos o tamanho dos campos criados possibilitando a criação das tabelas no MySQL.