code

EntityType 'IdentityUserLogin'에 정의 된 키가 없습니다.

codestyles 2020. 8. 24. 08:23
반응형

EntityType 'IdentityUserLogin'에 정의 된 키가 없습니다. 이 EntityType에 대한 키 정의


Entity Framework Code First 및 MVC 5로 작업하고 있습니다. 개별 사용자 계정 인증을 사용 하여 응용 프로그램을 만들었을 때 계정 컨트롤러와 함께 Indiv 사용자 계정 인증이 작동하는 데 필요한 모든 필수 클래스 및 코드가 제공되었습니다. .

이미 적용된 코드는 다음과 같습니다.

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext() : base("DXContext", throwIfV1Schema: false)
    {

    }

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

하지만 그런 다음 먼저 코드를 사용하여 내 컨텍스트를 만들었으므로 이제 다음과 같이합니다.

public class DXContext : DbContext
{
    public DXContext() : base("DXContext")
    {

    }

    public DbSet<ApplicationUser> Users { get; set; }
    public DbSet<IdentityRole> Roles { get; set; }
    public DbSet<Artist> Artists { get; set; }
    public DbSet<Paintings> Paintings { get; set; }        
}

마지막으로 개발하는 동안 작업 할 데이터를 추가하는 다음과 같은 시드 방법이 있습니다.

protected override void Seed(DXContext context)
{
    try
    {

        if (!context.Roles.Any(r => r.Name == "Admin"))
        {
            var store = new RoleStore<IdentityRole>(context);
            var manager = new RoleManager<IdentityRole>(store);
            var role = new IdentityRole { Name = "Admin" };

            manager.Create(role);
        }

        context.SaveChanges();

        if (!context.Users.Any(u => u.UserName == "James"))
        {
            var store = new UserStore<ApplicationUser>(context);
            var manager = new UserManager<ApplicationUser>(store);
            var user = new ApplicationUser { UserName = "James" };

            manager.Create(user, "ChangeAsap1@");
            manager.AddToRole(user.Id, "Admin");
        }

        context.SaveChanges();

        string userId = "";

        userId = context.Users.FirstOrDefault().Id;

        var artists = new List<Artist>
        {
            new Artist { FName = "Salvador", LName = "Dali", ImgURL = "http://i62.tinypic.com/ss8txxn.jpg", UrlFriendly = "salvador-dali", Verified = true, ApplicationUserId = userId },
        };

        artists.ForEach(a => context.Artists.Add(a));
        context.SaveChanges();

        var paintings = new List<Painting>
        {
            new Painting { Title = "The Persistence of Memory", ImgUrl = "http://i62.tinypic.com/xx8tssn.jpg", ArtistId = 1, Verified = true, ApplicationUserId = userId }
        };

        paintings.ForEach(p => context.Paintings.Add(p));
        context.SaveChanges();
    }
    catch (DbEntityValidationException ex)
    {
        foreach (var validationErrors in ex.EntityValidationErrors)
        {
            foreach (var validationError in validationErrors.ValidationErrors)
            {
                Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
            }
        }
    }

}

내 솔루션은 잘 빌드되지만 데이터베이스에 액세스해야하는 컨트롤러에 액세스하려고하면 다음 오류가 발생합니다.

DX.DOMAIN.Context.IdentityUserLogin : : EntityType 'IdentityUserLogin'에 정의 된 키가 없습니다. 이 EntityType에 대한 키를 정의하십시오.

DX.DOMAIN.Context.IdentityUserRole : : EntityType 'IdentityUserRole'에 정의 된 키가 없습니다. 이 EntityType에 대한 키를 정의하십시오.

내가 뭘 잘못하고 있죠? 두 가지 컨텍스트가 있기 때문입니까?

최신 정보

Augusto의 답변을 읽은 후 옵션 3선택했습니다 . 내 DXContext 클래스는 다음과 같습니다.

public class DXContext : DbContext
{
    public DXContext() : base("DXContext")
    {
        // remove default initializer
        Database.SetInitializer<DXContext>(null);
        Configuration.LazyLoadingEnabled = false;
        Configuration.ProxyCreationEnabled = false;

    }

    public DbSet<User> Users { get; set; }
    public DbSet<Role> Roles { get; set; }
    public DbSet<Artist> Artists { get; set; }
    public DbSet<Painting> Paintings { get; set; }

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

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<User>().ToTable("Users");
        modelBuilder.Entity<Role>().ToTable("Roles");
    }

    public DbQuery<T> Query<T>() where T : class
    {
        return Set<T>().AsNoTracking();
    }
}

또한 a User.csRole.cs클래스를 추가했는데 다음과 같습니다.

public class User
{
    public int Id { get; set; }
    public string FName { get; set; }
    public string LName { get; set; }
}

public class Role
{
    public int Id { set; get; }
    public string Name { set; get; }
}

기본 ApplicationUser에는 해당 필드와 다른 필드가 있기 때문에 사용자에 대한 암호 속성이 필요한지 확실하지 않았습니다!

어쨌든 위의 변경 사항은 잘 빌드되지만 응용 프로그램이 실행될 때 다시이 오류가 발생합니다.

잘못된 열 이름 UserId

UserId 내 정수 속성입니다 Artist.cs


문제는 ApplicationUser 가 다음과 같이 정의 된 IdentityUser 에서 상속 한다는 것입니다 .

IdentityUser : IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUser
....
public virtual ICollection<TRole> Roles { get; private set; }
public virtual ICollection<TClaim> Claims { get; private set; }
public virtual ICollection<TLogin> Logins { get; private set; }

기본 키는 IdentityDbContext 클래스의 OnModelCreating 메서드에 매핑됩니다 .

modelBuilder.Entity<TUserRole>()
            .HasKey(r => new {r.UserId, r.RoleId})
            .ToTable("AspNetUserRoles");

modelBuilder.Entity<TUserLogin>()
            .HasKey(l => new {l.LoginProvider, l.ProviderKey, l.UserId})
            .ToTable("AspNetUserLogins");

DXContext가 파생되지 않기 때문에 해당 키가 정의되지 않습니다.

당신이 파고 경우 소스Microsoft.AspNet.Identity.EntityFramework, 당신은 모든 것을 이해할 것이다.

나는 언젠가이 상황을 만났고 세 가지 가능한 해결책을 찾았습니다.

  1. 두 개의 다른 데이터베이스 또는 동일한 데이터베이스이지만 다른 테이블에 대해 별도의 DbContexts를 사용하십시오.
  2. DXContext를 ApplicationDbContext와 병합하고 하나의 데이터베이스를 사용합니다.
  3. 동일한 테이블에 대해 별도의 DbContext를 사용하고 그에 따라 마이그레이션을 관리합니다.

옵션 1 : 하단 업데이트 참조.

옵션 2 : 다음 과 같은 DbContext가 생성됩니다.

public class DXContext : IdentityDbContext<User, Role,
    int, UserLogin, UserRole, UserClaim>//: DbContext
{
    public DXContext()
        : base("name=DXContext")
    {
        Database.SetInitializer<DXContext>(null);// Remove default initializer
        Configuration.ProxyCreationEnabled = false;
        Configuration.LazyLoadingEnabled = false;
    }

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

    //Identity and Authorization
    public DbSet<UserLogin> UserLogins { get; set; }
    public DbSet<UserClaim> UserClaims { get; set; }
    public DbSet<UserRole> UserRoles { get; set; }

    // ... your custom DbSets
    public DbSet<RoleOperation> RoleOperations { get; set; }

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

        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

        // Configure Asp Net Identity Tables
        modelBuilder.Entity<User>().ToTable("User");
        modelBuilder.Entity<User>().Property(u => u.PasswordHash).HasMaxLength(500);
        modelBuilder.Entity<User>().Property(u => u.Stamp).HasMaxLength(500);
        modelBuilder.Entity<User>().Property(u => u.PhoneNumber).HasMaxLength(50);

        modelBuilder.Entity<Role>().ToTable("Role");
        modelBuilder.Entity<UserRole>().ToTable("UserRole");
        modelBuilder.Entity<UserLogin>().ToTable("UserLogin");
        modelBuilder.Entity<UserClaim>().ToTable("UserClaim");
        modelBuilder.Entity<UserClaim>().Property(u => u.ClaimType).HasMaxLength(150);
        modelBuilder.Entity<UserClaim>().Property(u => u.ClaimValue).HasMaxLength(500);
    }
}

옵션 3 : 옵션 2와 동일한 DbContext 하나를 갖게됩니다. 이름을 IdentityContext로 지정하겠습니다. 그리고 DXContext라는 또 다른 DbContext가 있습니다.

public class DXContext : DbContext
{        
    public DXContext()
        : base("name=DXContext") // connection string in the application configuration file.
    {
        Database.SetInitializer<DXContext>(null); // Remove default initializer
        Configuration.LazyLoadingEnabled = false;
        Configuration.ProxyCreationEnabled = false;
    }

    // Domain Model
    public DbSet<User> Users { get; set; }
    // ... other custom DbSets

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

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

        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

        // IMPORTANT: we are mapping the entity User to the same table as the entity ApplicationUser
        modelBuilder.Entity<User>().ToTable("User"); 
    }

    public DbQuery<T> Query<T>() where T : class
    {
        return Set<T>().AsNoTracking();
    }
}

사용자는 :

public class User
{
    public int Id { get; set; }

    [Required, StringLength(100)]
    public string Name { get; set; }

    [Required, StringLength(128)]
    public string SomeOtherColumn { get; set; }
}

이 솔루션을 사용하여 엔터티 User를 엔터티 ApplicationUser와 동일한 테이블에 매핑합니다.

그런 다음, 코드 첫 번째 마이그레이션을 사용하면 IdentityContext에 대한 마이그레이션을 생성해야합니다 THEN 쉐일 렌드 라 차우에서 위대한 게시물이 다음의 DXContext을 위해 : 여러 데이터 컨텍스트와 코드 첫 번째 마이그레이션을

DXContext에 대해 생성 된 마이그레이션을 수정해야합니다. ApplicationUser와 User간에 공유되는 속성에 따라 다음과 같이됩니다.

        //CreateTable(
        //    "dbo.User",
        //    c => new
        //        {
        //            Id = c.Int(nullable: false, identity: true),
        //            Name = c.String(nullable: false, maxLength: 100),
        //            SomeOtherColumn = c.String(nullable: false, maxLength: 128),
        //        })
        //    .PrimaryKey(t => t.Id);
        AddColumn("dbo.User", "SomeOtherColumn", c => c.String(nullable: false, maxLength: 128));

그런 다음이 사용자 정의 클래스를 사용하여 global.asax 또는 애플리케이션의 다른 위치에서 순서대로 마이그레이션을 실행합니다 (먼저 ID 마이그레이션).

public static class DXDatabaseMigrator
{
    public static string ExecuteMigrations()
    {
        return string.Format("Identity migrations: {0}. DX migrations: {1}.", ExecuteIdentityMigrations(),
            ExecuteDXMigrations());
    }

    private static string ExecuteIdentityMigrations()
    {
        IdentityMigrationConfiguration configuration = new IdentityMigrationConfiguration();
        return RunMigrations(configuration);
    }

    private static string ExecuteDXMigrations()
    {
        DXMigrationConfiguration configuration = new DXMigrationConfiguration();
        return RunMigrations(configuration);
    }

    private static string RunMigrations(DbMigrationsConfiguration configuration)
    {
        List<string> pendingMigrations;
        try
        {
            DbMigrator migrator = new DbMigrator(configuration);
            pendingMigrations = migrator.GetPendingMigrations().ToList(); // Just to be able to log which migrations were executed

            if (pendingMigrations.Any())                
                    migrator.Update();     
        }
        catch (Exception e)
        {
            ExceptionManager.LogException(e);
            return e.Message;
        }
        return !pendingMigrations.Any() ? "None" : string.Join(", ", pendingMigrations);
    }
}

이렇게하면 n 계층 교차 절단 엔터티가 AspNetIdentity 클래스에서 상속되지 않으므로 사용하는 모든 프로젝트에서이 프레임 워크를 가져올 필요가 없습니다.

광범위한 게시물에 대해 죄송합니다. 이에 대한 지침을 제공 할 수 있기를 바랍니다. 프로덕션 환경에서 이미 옵션 2와 3을 사용했습니다.

업데이트 : 옵션 1 확장

마지막 두 프로젝트의 경우 첫 번째 옵션을 사용했습니다. IdentityUser에서 파생되는 AspNetUser 클래스와 AppUser라는 별도의 사용자 지정 클래스가 있습니다. 필자의 경우 DbContext는 각각 IdentityContext와 DomainContext입니다. 그리고 다음과 같이 AppUser의 ID를 정의했습니다.

public class AppUser : TrackableEntity
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    // This Id is equal to the Id in the AspNetUser table and it's manually set.
    public override int Id { get; set; }

(TrackableEntity는 내 DomainContext 컨텍스트의 재정의 된 SaveChanges 메서드에서 사용하는 사용자 지정 추상 기본 클래스입니다.)

먼저 AspNetUser를 만든 다음 AppUser를 만듭니다. 이 접근 방식의 단점은 "CreateUser"기능이 트랜잭션 적이라는 것입니다 (SaveChanges를 개별적으로 호출하는 두 개의 DbContext가 있음을 기억하십시오). TransactionScope를 사용하는 것이 어떤 이유로 저에게 효과가 없었기 때문에 결국 추악한 일을했지만 그것은 저에게 효과적입니다.

        IdentityResult identityResult = UserManager.Create(aspNetUser, model.Password);

        if (!identityResult.Succeeded)
            throw new TechnicalException("User creation didn't succeed", new LogObjectException(result));

        AppUser appUser;
        try
        {
            appUser = RegisterInAppUserTable(model, aspNetUser);
        }
        catch (Exception)
        {
            // Roll back
            UserManager.Delete(aspNetUser);
            throw;
        }

(누군가이 부분을 수행하는 더 나은 방법을 제공한다면이 답변에 대한 의견을 보내거나 편집을 제안하는 것에 감사드립니다)

이점은 마이그레이션을 수정할 필요가 없으며 AspNetUser를 엉망으로 만들지 않고도 AppUser에 대해 미친 상속 계층을 사용할있다는 것 입니다. 그리고 실제로 내 IdentityContext (IdentityDbContext에서 파생 된 컨텍스트)에 대해 자동 마이그레이션을 사용합니다.

public sealed class IdentityMigrationConfiguration : DbMigrationsConfiguration<IdentityContext>
{
    public IdentityMigrationConfiguration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = false;
    }

    protected override void Seed(IdentityContext context)
    {
    }
}

이 접근 방식은 AspNetIdentity 클래스에서 상속되는 n 계층 교차 절단 엔터티를 피할 수 있다는 이점도 있습니다.


제 경우에는 IdentityDbContext에서 올바르게 상속했지만 (내 사용자 지정 형식과 키가 정의되어 있음) 실수로 기본 클래스의 OnModelCreating에 대한 호출을 제거했습니다.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder); // I had removed this
    /// Rest of on model creating here.
}

그런 다음 ID 클래스에서 누락 된 인덱스를 수정 한 다음 마이그레이션을 생성하고 적절하게 마이그레이션을 활성화 할 수 있습니다.


ASP.NET Identity 2.1을 사용하고 기본 키를 기본값 string에서 int또는로 변경 한 Guid사용자를 위해

EntityType 'xxxxUserLogin'에 정의 된 키가 없습니다. 이 EntityType에 대한 키를 정의하십시오.

EntityType 'xxxxUserRole'에 정의 된 키가 없습니다. 이 EntityType에 대한 키를 정의하십시오.

에 새 키 유형을 지정하는 것을 잊었을 것입니다 IdentityDbContext.

public class AppIdentityDbContext : IdentityDbContext<
    AppUser, AppRole, int, AppUserLogin, AppUserRole, AppUserClaim>
{
    public AppIdentityDbContext()
        : base("MY_CONNECTION_STRING")
    {
    }
    ......
}

당신이 가지고 있다면

public class AppIdentityDbContext : IdentityDbContext
{
    ......
}

또는

public class AppIdentityDbContext : IdentityDbContext<AppUser>
{
    ......
}

마이그레이션을 추가하거나 데이터베이스를 업데이트하려고 할 때 '정의 된 키 없음'오류가 발생합니다.


아래와 같이 DbContext를 변경하여;

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
        modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
    }

OnModelCreatingbase.OnModelCreating (modelBuilder); 메서드 호출을 추가 하기 만하면됩니다. 그리고 괜찮아집니다. EF6을 사용하고 있습니다.

#The Senator에게 특별히 감사드립니다.


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

            //foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
            //    relationship.DeleteBehavior = DeleteBehavior.Restrict;

            modelBuilder.Entity<User>().ToTable("Users");

            modelBuilder.Entity<IdentityRole<string>>().ToTable("Roles");
            modelBuilder.Entity<IdentityUserToken<string>>().ToTable("UserTokens");
            modelBuilder.Entity<IdentityUserClaim<string>>().ToTable("UserClaims");
            modelBuilder.Entity<IdentityUserLogin<string>>().ToTable("UserLogins");
            modelBuilder.Entity<IdentityRoleClaim<string>>().ToTable("RoleClaims");
            modelBuilder.Entity<IdentityUserRole<string>>().ToTable("UserRoles");

        }
    }

내 문제는 비슷했습니다. ID 사용자와 연결하기 위해 ahd를 만드는 새 테이블이있었습니다. 위의 답변을 읽은 후 IsdentityUser 및 상속 된 속성과 관련이 있음을 깨달았습니다. 이미 Identity를 자체 컨텍스트로 설정 했으므로 관련 사용자 테이블을 실제 EF 속성으로 사용하는 대신 본질적으로 두 가지를 함께 묶지 않도록 관련 엔터티를 가져 오는 쿼리와 함께 매핑되지 않은 속성을 설정했습니다. (DataManager는 OtherEntity가 존재하는 현재 컨텍스트를 검색하도록 설정됩니다.)

    [Table("UserOtherEntity")]
        public partial class UserOtherEntity
        {
            public Guid UserOtherEntityId { get; set; }
            [Required]
            [StringLength(128)]
            public string UserId { get; set; }
            [Required]
            public Guid OtherEntityId { get; set; }
            public virtual OtherEntity OtherEntity { get; set; }
        }

    public partial class UserOtherEntity : DataManager
        {
            public static IEnumerable<OtherEntity> GetOtherEntitiesByUserId(string userId)
            {
                return Connect2Context.UserOtherEntities.Where(ue => ue.UserId == userId).Select(ue => ue.OtherEntity);
            }
        }

public partial class ApplicationUser : IdentityUser
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }

        [NotMapped]
        public IEnumerable<OtherEntity> OtherEntities
        {
            get
            {
                return UserOtherEntities.GetOtherEntitiesByUserId(this.Id);
            }
        }
    }

참고 URL : https://stackoverflow.com/questions/28531201/entitytype-identityuserlogin-has-no-key-defined-define-the-key-for-this-entit

반응형