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.cs
및 Role.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
, 당신은 모든 것을 이해할 것이다.
나는 언젠가이 상황을 만났고 세 가지 가능한 해결책을 찾았습니다.
- 두 개의 다른 데이터베이스 또는 동일한 데이터베이스이지만 다른 테이블에 대해 별도의 DbContexts를 사용하십시오.
- DXContext를 ApplicationDbContext와 병합하고 하나의 데이터베이스를 사용합니다.
- 동일한 테이블에 대해 별도의 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>();
}
OnModelCreating
base.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);
}
}
}
'code' 카테고리의 다른 글
"여기에서 git-bash 열기…"컨텍스트 메뉴를 Windows 탐색기에 추가하는 방법은 무엇입니까? (0) | 2020.08.24 |
---|---|
jQuery를 사용하여 선택 목록에서 옵션 숨기기 (0) | 2020.08.24 |
번들에 NIB를로드 할 수 없습니다. (0) | 2020.08.24 |
누군가가 방향성 비순환 그래프가 무엇인지 간단한 용어로 설명 할 수 있습니까? (0) | 2020.08.24 |
Angular 2 아래로 스크롤 (채팅 스타일) (0) | 2020.08.24 |