programing

"최대 키 길이가 767바이트"로 인해 MySql 및 마이그레이션이 포함된 엔티티 프레임워크가 실패했습니다.

oldcodes 2023. 10. 16. 22:04
반응형

"최대 키 길이가 767바이트"로 인해 MySql 및 마이그레이션이 포함된 엔티티 프레임워크가 실패했습니다.

[편집] 이 문제는 해결되었습니다!게시물의 끝에 있는 설명을 참조합니다.

[편집 2] 네, 이 스레드는 오래되었고, MySQL Connector의 새로운 버전은 이미 MySQL EF 레졸버로 이 문제를 처리합니다.이 스레드에서 @KingPong 답변을 찾습니다.테스트는 안 해봤는데요.

MySql과 EntityFramework를 Migration과 함께 사용하려고 하는데 뭔가 잘못된 것 같습니다.

할 때.Update-Database -VerbosePackage Manager Console에서 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가 __Migration을 생성하는 방식을 변경해야 한다고 말해야 합니다.이력표.

한 일:: 라는 .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.cse안에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재미있게 놀아요!

사용자 지정 마이그레이션 추가에서 의역한 답변기록 컨텍스트...

EF6에서 마이그레이션 사용모델 변경을 추적하고 데이터베이스 스키마와 개념 스키마 간의 일관성을 보장하기 위한 히스토리 테이블.기본 키가 너무 크기 때문에 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();

여기에 지시사항 완료...

MySQL Connector/Net 6.8 및 Entity Framework 버전 6을 사용하면 이 문제를 MySQL의 EF에 대한 내장 지원을 사용하여 해결할 수 있습니다.방법은 엔티티 프레임워크에 MySQL 레졸버를 사용하도록 지시하는 것입니다.Connector/Net Developer Guide에서:

이 작업은 세 가지 방법으로 수행할 수 있습니다.

  • DbConfiguration 추가컨텍스트 클래스에 Attribute를 입력합니다.

    [DbConfigurationType((MySqlEFConfiguration)의 유형)]
  • DbConfiguration을 호출합니다.응용 프로그램 시작 시 SetConfiguration(새 MySqlEFconfiguration())

  • 구성 파일에서 DbConfiguration 유형을 설정합니다.

    <entity Framework 코드 구성="MySql"을 입력합니다.데이터. 엔티티.MySqlEF구성, 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

ChAmp33n의 수용된 답변은 질문자에 의해 제기된 문제를 해결했지만, 그 답변 이후에 약간의 "파격적인" 변화가 있었다고 생각합니다.저는 수락된 답변을 신청했지만 예외는 남아있었습니다.

EF에서 생성된 SQL 쿼리를 확인한 결과 테이블 AspNetUsers의 UserName(varcharutf8 256)과 테이블 AspNetRols의 Name(varcharutf8 256)과 관련된 문제인 반면 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();
    }


}

솔루션을 명확히 하기 위해 제가 사용하고 있는 라이브러리는 다음과 같습니다.

  1. EF 6.1.0
  2. 마이크로소프트 ASP.NET 아이덴티티 프레임워크 2.0.0
  3. ASP.NET ID Owin 2.0.0
  4. MySql .NET 라이브러리 6.8.3

그리고 이 해결책은 또한

  1. EF 6.1.1
  2. 마이크로소프트 ASP.NET 아이덴티티 프레임워크 2.0.1
  3. ASP.NET ID Owin 2.0.1

저처럼 아직도 머리가 아픈 분들을 위해 제가 직접 해결책을 찾아봤습니다.마이그레이션 파일로 이동(201603160820243)_Initial.cs ), 모든 maxLength 편집: 256~128.Update-Database를 다시 실행하면 효과가 있습니다.

nvarchar(256)(512바이트)가 767바이트를 충족하지 못하고 키 열에 대한 업데이트가 아닌 이유를 여전히 알 수 없습니다.

EF CodePlex 사이트에서 이 작업 항목을 살펴보십시오.EF6를 사용하는 경우 마이그레이션 기록 테이블을 구성하고 열을 짧게 만들 수 있습니다. 이 작업을 수행하는 방법을 보여주는 문서가 여기에 있습니다.

지정한 기본 키의 길이는 유니코드 450자입니다.MySQL은 열 크기 제한을 확인할 때 문자 크기에 대해 최악의 경우를 가정합니다(문자 집합 및 대조에 따라 2바이트 또는 4바이트). 따라서 900 또는 1800바이트가 너무 길기 때문입니다.

기본 키는 집계 키가 되어서는 안 됩니다.기본 키는 테이블이 디스크에 배치되는 방식을 결정합니다. 그것은 성능 자살입니다.기본 키를 정수로 하고, 원하는 구조의 보조 키를 붙입니다.

엔티티 프레임워크별로 계산된 외래 키명이 너무 길 수 있기 때문입니다.우리는 migartion.cs 파일에서 외국 키 이름을 지정할 수 있습니다.의 네 번째 ForeignKey()은.ForeignKeyName.

기본적으로 다음과 같습니다.

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

제가 기존 데이터베이스로 작업하려고 했기 때문에 이러한 답변 중 어느 것도 효과가 없었습니다. 하지만 다른 답변 중 일부에서 약간 벗어나서 제가 한 일은 이렇습니다.

MySQL Workbench 또는 phpMyAdmin과 같은 데이터베이스에 직접 액세스하여 MySQL 명령을 실행하면 더욱 쉬워집니다.SQL 명령 중 일부를 C#에 코드화할 수도 있지만, 제가 어떻게 한 것은 아닙니다.

  1. 이 첫 번째 단계가 모든 사용자에게 적합한지는 잘 모르겠지만, 몇 번 후에 추가-마이그레이션/업데이트-데이터베이스를 실행할 필요 없이 먼저 실행했으면 좋았을 것 같습니다.초기 관리자 계정을 데이터베이스에 시드하려면

    1. ApplicationDbContextIdentityModels.cs대상:

       public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
       {
           public static ApplicationDbInitializer adi = new ApplicationDbInitializer();
      
           public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false)
           {
               adi.InitializeDatabase(this); //Delete after first run:
           }
      
           static ApplicationDbContext()
           {
               Database.SetInitializer<ApplicationDbContext>(adi); //Delete after first run
           }
           public static ApplicationDbContext Create()
           {
               return new ApplicationDbContext();
           }
      
      
           /* Optional: If you'd like to rename your tables, 
           *  you can do it by overriding this "OnModelCreating()" method.
           *  Be sure to change the table names in your 
           *  "Migrations\#######EmptyMigrations.cs" file to match the new table names
           *  before running "Update-Database" in the PM Console. */
           protected override void OnModelCreating(DbModelBuilder modelBuilder)
           {
               base.OnModelCreating(modelBuilder);
      
               /* Note that for the AspNetUsers table
               *  —because the "IdentityUser" class was extended 
               *  into "ApplicationUser" earlier in IdentityModels.cs—
               *  You must refer to the new class rather than the base class */
      
               //modelBuilder.Entity<ApplicationUser>().ToTable("tblIdentityUsers");
               //modelBuilder.Entity<IdentityUserClaim>().ToTable("tblIdentityUserClaims");
               //modelBuilder.Entity<IdentityUserLogin>().ToTable("tblIdentityUserLogins");
               //modelBuilder.Entity<IdentityUserRole>().ToTable("tblIdentityUserRoles");
               //modelBuilder.Entity<IdentityRole>().ToTable("tblIdentityRoles");
           }
       }
      
    2. 는 OWIN 의, 게 null 에 .userManager그리고.roleManagerInitializeIdentityForEFIdentityConfig.cs그래서 제가 대신 이 컨스트럭터를 사용했습니다.당연히 관리자 계정을 원하는 대로 변경합니다.

       public static void InitializeIdentityForEF(ApplicationDbContext db) {
           //var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
           //var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
           var roleStore = new RoleStore<IdentityRole>(db);
           var roleManager = new RoleManager<IdentityRole>(roleStore);
           var userStore = new UserStore<ApplicationUser>(db);
           var userManager = new UserManager<ApplicationUser>(userStore);
           //In normal usage I would probably wrap these in a series of using() statements as well.
      
           const string name = "Administrator";
           const string email = "foo@bar.com";
           const string password = "Password123";
           const string roleName = "Admin";
      
           //Create Role Admin if it does not exist
           var role = roleManager.FindByName(roleName);
           if (role == null) {
               role = new IdentityRole(roleName);
               var roleresult = roleManager.Create(role);
           }
      
           var user = userManager.FindByName(name);
           if (user == null) {
               user = new ApplicationUser { UserName = name, Email = email };
               var result = userManager.Create(user, password);
               result = userManager.SetLockoutEnabled(user.Id, false);
           }
      
           // Add user admin to Role Admin if not already added
           var rolesForUser = userManager.GetRoles(user.Id);
           if (!rolesForUser.Contains(role.Name)) {
               var result = userManager.AddToRole(user.Id, role.Name);
           }
       }
      
  2. 패키지 관리자 콘솔 열기

    1. 실행.PM> Enable-Migrations
    2. 실행.PM> Add-Migration EmptyMigration(이것은 무엇이든 이름 지을 수 있습니다)
  3. Project\Migrations\###########EmptyMigration.cs

    1. Up() 방법을 찾고,CreateTable()"dbo"합니다를 합니다."AspNetRoles" 와 "dbo"입니다.AspNetUsers"(또는 이름을 변경했을 수도 있는 모든 사용자).

    2. 를 로 달아주세요..Index()과 세미콜론합니다 위에 메소드 두합니다..PrimaryKey()

      다음과 같이 보여야 합니다.

       CreateTable(
           "dbo.AspNetRoles",
           c => new
           {
               Id = c.String(nullable: false, maxLength: 128, storeType: "nvarchar"),
               Name = c.String(nullable: false, maxLength: 256, storeType: "nvarchar"),
           })
           .PrimaryKey(t => t.Id);
       //.Index(t => t.Name, unique: true, name: "RoleNameIndex");
      

      그리고.

      CreateTable(
           "dbo.AspNetUsers",
           c => new
               {
                   Id = c.String(nullable: false, maxLength: 128, storeType: "nvarchar"),
                   Email = c.String(maxLength: 256, storeType: "nvarchar"),
                   EmailConfirmed = c.Boolean(nullable: false),
                   PasswordHash = c.String(unicode: false),
                   SecurityStamp = c.String(unicode: false),
                   PhoneNumber = c.String(unicode: false),
                   PhoneNumberConfirmed = c.Boolean(nullable: false),
                   TwoFactorEnabled = c.Boolean(nullable: false),
                   LockoutEndDateUtc = c.DateTime(precision: 0),
                   LockoutEnabled = c.Boolean(nullable: false),
                   AccessFailedCount = c.Int(nullable: false),
                   UserName = c.String(nullable: false, maxLength: 256, storeType: "nvarchar"),
               })
           .PrimaryKey(t => t.Id);
       // .Index(t => t.UserName, unique: true, name: "UserNameIndex");
      
  4. Package Manager Console로 돌아가서 실행PM> Update-Database -Verbose

  5. MySQL Workbench 또는 phpMyAdmin 또는 SQL 명령을 직접 실행하기 위해 사용하는 모든 것으로 이동합니다.

    1. 다음 명령을 실행하여 키 열 문자 집합을 더 작은 것으로 변경합니다.

      ALTER TABLE `DatabaseName`.`AspNetUsers` 
          MODIFY `UserName` VARCHAR(256) 
          CHARACTER SET latin1;
      ALTER TABLE `DatabaseName`.`AspNetRoles` 
          MODIFY `Name` VARCHAR(256) 
          CHARACTER SET latin1;
      
    2. 처음에는 할 수 없었던 인덱스 만들기:

       CREATE UNIQUE INDEX `UserNameIndex` 
           ON `DatabaseName`.`AspNetUsers` (`UserName` DESC) USING HASH;
       CREATE UNIQUE INDEX `RoleNameIndex` 
           ON `DatabaseName`.`AspNetRoles` (`Name` DESC) USING HASH;
      

p.s. 나는 그것을 그 안에서 알아차렸습니다.Down()method는 마이그레이션을 되돌리려고 할 때 테이블 이름에서 "dbo"를 꺼낸 후에야 작동했습니다.마일리지는 변동될 수 있습니다.

언급URL : https://stackoverflow.com/questions/20832546/entity-framework-with-mysql-and-migrations-failing-because-max-key-length-is-76

반응형