实体类型' Microsoft.AspNetCore.Identity.IdentityRole'处于阴影状态

时间:2018-05-16 09:11:32

标签: c# entity-framework-core asp.net-core-2.0 identityserver4

我一直在玩这个quickstart example&一直试图看我能在多大程度上自定义数据库(我有一个现有的数据库,我已经尝试复制了一半)。

我设法触发了下面的例外并且无法修复它,部分原因是因为我不明白该消息告诉我的是什么。

  

InvalidOperationException:实体类型   ' Microsoft.AspNetCore.Identity.IdentityRole'处于阴影状态。一个   有效模型要求所有实体类型都具有相应的CLR类型。

我的ApplicationDbContext如下:

using IdentityServerWithAspIdAndEF.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System;

namespace IdentityServerWithAspIdAndEF.Data
{
    public class ApplicationDbContext : IdentityDbContext<User, Role, int>
    {
        public ApplicationDbContext(
            DbContextOptions<ApplicationDbContext> Options
        ) : base(Options) { }

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

            // Customisations

            // "IdentityServer4AspNetIdentity.Models.ApplicationUser"
            ModelBuilder.Entity<User>(B =>
            {
                B.Property<int>(P => P.Id)
                    .HasColumnName("AccountId")
                    .ValueGeneratedOnAdd();

                B.Property<string>("ConcurrencyStamp")
                    .HasMaxLength(512)
                    .IsConcurrencyToken();

                B.Property<string>("Email")
                    .HasMaxLength(512)
                    .IsRequired();

                B.Property<bool>("EmailConfirmed")
                    .ValueGeneratedOnAdd();

                B.Property<string>("NormalisedEmail")
                    .HasMaxLength(512)
                    .IsRequired();

                B.Property<string>("NormalisedUserName")
                    .HasMaxLength(256)
                    .IsRequired();

                B.Property<string>("PasswordHash");

                B.Property<string>("SecurityStamp")
                    .IsRequired();

                B.Property<bool>("TwoFactorEnabled")
                    .ValueGeneratedOnAdd();

                B.Property<string>("UserName")
                    .HasMaxLength(256)
                    .IsRequired();

                B.Property<DateTime>("Registered")
                    .ValueGeneratedOnAdd();

                B.Property<DateTime>("LastVisit")
                    .IsRequired();

                B.HasKey("AccountId");

                B.HasIndex("NormalisedEmail")
                    .HasName("IX_Users_NormalisedEmail");

                B.HasIndex("NormalisedUserName")
                    .IsUnique()
                    .HasName("IX_Users_NormalisedUserName");

                B.ToTable("Users");
            });

            // "Microsoft.AspNetCore.Identity.IdentityRole"
            ModelBuilder.Entity<Role>(B =>
            {
                B.Property<int>(P => P.Id)
                    .HasColumnName("RoleId")
                    .ValueGeneratedOnAdd();

                B.Property<string>("ConcurrencyStamp")
                    .HasMaxLength(512)
                    .IsConcurrencyToken();

                B.Property<string>("Name")
                    .HasMaxLength(256);

                B.Property<string>("NormalisedName")
                    .HasMaxLength(256);

                B.HasKey(P => P.Id);

                B.HasIndex("NormalisedName")
                    .IsUnique()
                    .HasName("IX_Roles_NormalisedName");

                B.ToTable("Roles");
            });

            // "Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>"
            ModelBuilder.Entity<RoleClaim>(B =>
            {
                B.Property<int>(P => P.Id)
                    .HasColumnName("ClaimId")
                    .ValueGeneratedOnAdd();

                B.Property<string>("ClaimType")
                    .HasMaxLength(128);

                B.Property<string>("ClaimValue")
                    .HasMaxLength(128);

                B.Property<int>("RoleId")
                    .IsRequired();

                B.HasIndex(P => P.RoleId)
                    .HasName("IX_RoleClaims_RoleId");

                B.HasOne(D => D.Claim)
                    .WithMany()
                    .HasForeignKey(P => P.RoleId)
                    .OnDelete(DeleteBehavior.Cascade);

                B.ToTable("RoleClaims");
            });

            // "Microsoft.AspNetCore.Identity.IdentityUserClaim<string>"
            ModelBuilder.Entity<UserClaim>(B =>
            {
                B.Property<int>(P => P.Id)
                    .HasColumnName("ClaimId")
                    .ValueGeneratedOnAdd();

                B.Property<string>("ClaimType")
                    .HasMaxLength(128);

                B.Property<string>("ClaimValue")
                    .HasMaxLength(128);

                B.Property<int>(P => P.UserId)
                    .HasColumnName("AccountId")
                    .IsRequired();

                B.HasIndex("AccountId")
                    .HasName("IX_UserClaims_AccountId");

                B.HasOne(D => D.Account)
                    .WithMany()
                    .HasForeignKey(P => P.AccountId)
                    .OnDelete(DeleteBehavior.Cascade);

                B.ToTable("UserClaims");
            });

            // "Microsoft.AspNetCore.Identity.IdentityUserLogin<string>"
            ModelBuilder.Entity<Login>(B =>
            {
                B.Property<int>(P => P.UserId)
                    .HasColumnName("LoginId")
                    .ValueGeneratedOnAdd();

                B.Property<string>("LoginProvider")
                    .HasMaxLength(450)
                    .IsRequired();

                B.Property<string>("ProviderKey")
                    .HasMaxLength(450)
                    .IsRequired();

                B.Property<string>("ProviderDisplayName");

                B.Property<int>("AccountId")
                    .IsRequired();

                B.HasIndex("LoginProvider")
                    .HasName("IX_Logins_LoginProvider");

                B.HasIndex("ProviderKey")
                    .HasName("IX_Logins_ProviderKey");

                B.HasIndex("AccountId")
                    .HasName("IX_Logins_AccountId");

                B.HasOne(D => D.Account)
                    .WithMany()
                    .HasForeignKey(P => P.AccountId)
                    .OnDelete(DeleteBehavior.Cascade);

                B.ToTable("Logins");
            });

            // "Microsoft.AspNetCore.Identity.IdentityUserRole<string>"
            ModelBuilder.Entity<UserRole>(B =>
            {
                B.Property<int>(P => P.UserId)
                    .HasColumnName("AccountId")
                    .IsRequired();

                B.Property<int>("RoleId")
                    .IsRequired();

                B.HasIndex("AccountId")
                    .HasName("IX_RoleMap_AccountId");

                B.HasIndex("RoleId")
                    .HasName("IX_RoleMap_RoleId");

                B.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
                    .WithMany()
                    .HasForeignKey("RoleId")
                    .OnDelete(DeleteBehavior.Cascade);

                B.HasOne(P => P.Account)
                    .WithMany()
                    .HasForeignKey("AccountId")
                    .OnDelete(DeleteBehavior.Cascade);

                B.ToTable("RoleMap");
            });

            // "Microsoft.AspNetCore.Identity.IdentityUserToken<string>"
            ModelBuilder.Entity<Token>(B =>
            {
                B.Property<int>(P => P.UserId)
                    .HasColumnName("AccountId")
                    .IsRequired();

                B.Property<string>("LoginProvider")
                    .HasMaxLength(128)
                    .IsRequired();

                B.Property<string>("Name")
                    .HasMaxLength(64);

                B.Property<string>("Value");

                B.HasOne(P => P.Account)
                    .WithMany()
                    .HasForeignKey(P => P.AccountId)
                    .OnDelete(DeleteBehavior.Cascade);

                B.ToTable("UserTokens");
            });

            // Non-identity extras

            /* snipped */
        }
    }
}

与这些DbSets对应的实体如下:

using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;

namespace IdentityServerWithAspIdAndEF.Models
{
    public class User : IdentityUser<int>
    {
        public int AccountId
        {
            get => base.Id;
            set => base.Id = value;
        }
        public string NormalisedEmail
        {
            get => base.NormalizedEmail;
            set => base.NormalizedEmail = value;
        }
        public string NormalisedUserName
        {
            get => base.NormalizedUserName;
            set => base.NormalizedUserName = value;
        }
        public DateTime Registered { get; set; }
        public DateTime LastVisit { get; set; }

        public AccountDetail UserDetails { get; set; }
        public AccountLockout Lockout { get; set; }
        public PasswordReset PasswordResetRequested { get; set; }
        public Concierge ConciergeAccountFlag { get; set; }
        public NotValidated AccountValidatation { get; set; }

        public ICollection<UserRole> AssignedRoles { get; set; }
        public ICollection<UserClaim> ClaimsCollection { get; set; }
        public ICollection<Login> Logins { get; set; }
        public ICollection<LoginAttempt> LoginAttempts { get; set; }
        public ICollection<Token> TokenCollection { get; set; }

        public new int Id => throw new NotImplementedException();
        public override string NormalizedEmail => throw new NotImplementedException();
        public override string NormalizedUserName => throw new NotImplementedException();
    }

    public class Role : IdentityRole<int>
    {
        public int RoleId
        {
            get => base.Id;
            set => base.Id = value;
        }
        public string NormalisedName
        {
            get => base.NormalizedName;
            set => base.NormalizedName = value;
        }

        public ICollection<RoleClaim> ClaimsCollection { get; set; }

        private new int Id => throw new NotImplementedException();
        private new int NormalizedName => throw new NotImplementedException();
    }

    public class RoleClaim : IdentityRoleClaim<int>
    {
        public int ClaimId
        {
            get => base.Id;
            set => base.Id = value;
        }

        public Role Claim { get; set; }

        private new int Id => throw new NotImplementedException();
    }

    public class UserClaim : IdentityUserClaim<int>
    {
        public int ClaimId
        {
            get => base.Id;
            set => base.Id = value;
        }
        public int AccountId
        {
            get => base.UserId;
            set => base.UserId = value;
        }

        public User Account { get; set; }

        private new int Id => throw new NotImplementedException();
        private new int UserId => throw new NotImplementedException();
    }

    public class Login : IdentityUserLogin<int>
    {
        public int AccountId
        {
            get => base.UserId;
            set => base.UserId = value;
        }

        public User Account { get; set; }

        private new int UserId => throw new NotImplementedException();
    }

    public class UserRole : IdentityUserRole<int>
    {
        public int AccountId
        {
            get => base.UserId;
            set => base.UserId = value;
        }

        public User Account { get; set; }

        private new int UserId => throw new NotImplementedException();
    }

    public class Token : IdentityUserToken<int>
    {
        public int AccountId
        {
            get => base.UserId;
            set => base.UserId = value;
        }

        private new int UserId => throw new NotImplementedException();

        public User Account { get; set; }
    }
}

我已阅读帖子"What does it mean for an entity type to be in “shadow state”?""Entity type 'type' is in shadow-state. A valid model requires all entity types to have corresponding CLR type"

documentation认为判断我可能错过或错误引用某个地方的Role实体,但对我来说并不清楚其中。

提前致谢!

编辑:

重新读取影子属性文档,该行&#34; 按照惯例,只有在发现关系但在依赖实体类中找不到外键属性时才会创建影子属性。在这种情况下,将引入影子外键属性。&#34;似乎支持我已经搞砸了实体。

我尝试通过将RoleRoleClaim模型构建器实体声明中的所有属性引用更改为表达式来排除属性名称不匹配,以便查看硬引用是否有帮助:

// "Microsoft.AspNetCore.Identity.IdentityRole"
ModelBuilder.Entity<Role>(B =>
{
    B.Property(P => P.RoleId)
        .ValueGeneratedOnAdd();

    B.Property(E => E.ConcurrencyStamp)
        .HasMaxLength(512)
        .IsConcurrencyToken();

    B.Property(E => E.Name)
        .HasMaxLength(256);

    B.Property(E => E.NormalisedName)
        .HasMaxLength(256);

    B.HasKey(P => P.Id);

    B.HasIndex(E => E.NormalisedName)
        .IsUnique()
        .HasName("IX_Roles_NormalisedName");

    B.ToTable("Roles");
});

// "Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>"
ModelBuilder.Entity<RoleClaim>(B =>
{
    B.Property(P => P.ClaimId)
        .ValueGeneratedOnAdd();

    B.Property(E => E.ClaimType)
        .HasMaxLength(128);

    B.Property(E => E.ClaimValue)
        .HasMaxLength(128);

    B.Property(E => E.RoleId)
        .IsRequired();

    B.HasIndex(P => P.RoleId)
        .HasName("IX_RoleClaims_RoleId");

    B.HasOne(D => D.Claim)
        .WithMany()
        .HasForeignKey(P => P.RoleId)
        .OnDelete(DeleteBehavior.Cascade);

    B.ToTable("RoleClaims");
});

但到目前为止没有运气。

1 个答案:

答案 0 :(得分:0)

好像我犯了几个错误,最相关的是我实际上没有为ApplicationDbContext示例中的UserRole对象提供代码;这实际上是错误发生的地方......

代码仍引用原始IdentityRole模型:

// "Microsoft.AspNetCore.Identity.IdentityUserRole<string>"
ModelBuilder.Entity<UserRole>(E =>
{
    E.Property<int>(P => P.UserId)
        .HasColumnName("AccountId")
        .IsRequired();

    E.Property<int>("RoleId")
        .IsRequired();

    E.HasIndex("AccountId")
        .HasName("IX_RoleMap_AccountId");

    E.HasIndex("RoleId")
        .HasName("IX_RoleMap_RoleId");

    E.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")    // Argh!
        .WithMany()
        .HasForeignKey("RoleId")
        .OnDelete(DeleteBehavior.Cascade);

    E.HasOne(P => P.Account)
        .WithMany()
        .HasForeignKey("AccountId")
        .OnDelete(DeleteBehavior.Cascade);

    E.ToTable("RoleMap");
});

这已更新为引用属性,而不是使用魔术字符串来指定字段名称;这突出了我所犯的错误。

// "Microsoft.AspNetCore.Identity.IdentityUserRole<string>"
ModelBuilder.Entity<UserRole>(E =>
{
    E.Property(P => P.UserId)
        .HasColumnName("AccountId")
        .IsRequired();

    E.Property(P => P.RoleId)
        .IsRequired();

    E.HasIndex(P => P.AccountId)
        .HasName("IX_RoleMap_AccountId");

    E.HasIndex(P => P.RoleId)
        .HasName("IX_RoleMap_RoleId");

    E.HasOne(P => P.Role)            // 'UserRole' does not contain a definition for 'Role'
        .WithMany()
        .HasForeignKey(P => P.RoleId)
        .OnDelete(DeleteBehavior.Cascade);

    E.HasOne(P => P.Account)
        .WithMany()
        .HasForeignKey(P => P.AccountId)
        .OnDelete(DeleteBehavior.Cascade);

    E.ToTable("RoleMap");
});

public class UserRole : IdentityUserRole<int>
{
    public int AccountId
    {
        get => base.UserId;
        set => base.UserId = value;
    }

    public User Account { get; set; }
    public Role Role { get; set; }    // Addition
}

此时通知我IdentityRole在阴影状态下运行的异常似乎已经消失并被其他人取代。

此外(我并非100%确定这导致了我所看到的异常)我在Startup中错误配置了我的RoleStore-&gt; ConfigureServices。

Services.AddIdentity<User, Role>()
    .AddUserStore<CustomerUserStore>()
    .AddUserManager<CustomerManager>()
    .AddRoleStore<Role>()                // Should have been CustomerRoleStore
    .AddRoleManager<RoleManager>()
    .AddSignInManager<CustomerSignInManager>()
    .AddDefaultTokenProviders();

CustomerRoleStore还需要覆盖以允许IdentityServer在我的自定义后理解角色,如下所示:

public class CustomerRoleStore
    : RoleStore<Role, ApplicationDbContext, int, UserRole, RoleClaim>
{
    public CustomerRoleStore(
        ApplicationDbContext context,
        IdentityErrorDescriber describer = null
    ) : base(
        context,
        describer
    )
    { }

    protected override RoleClaim CreateRoleClaim(Role role, Claim claim)
    {
        return new RoleClaim
        {
            RoleId = role.RoleId,
            ClaimType = claim.Type,
            ClaimValue = claim.Value
        };
    }
}