Тип объекта «Microsoft.AspNetCore.Identity.IdentityRole» находится в теневом состоянии. - PullRequest
0 голосов
/ 16 мая 2018

Я играл с этим примером быстрого запуска и пытался понять, как далеко я могу настроить базу данных (у меня есть существующая база данных, которую я пытался реплицировать наполовину).

Мне удалось вызвать приведенное ниже исключение, и у меня возникли проблемы с его исправлением, отчасти потому, что я не понимаю, о чем говорит сообщение.

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; }
    }
}

Я прочитал сообщения "Что означает, что тип сущности находится в" теневом состоянии "?" и "Тип сущности 'тип' находится в теневом состоянии. Действительный модель требует, чтобы все типы сущностей имели соответствующий тип CLR "

Судя по документации Я думаю , что я мог где-то пропустить или ошибочно сослаться на Role сущность, но мне не ясно, где.

Заранее спасибо!


Edit:

Перечитывание документации по свойствам теней, строка " По соглашению, свойства теней создаются только тогда, когда обнаруживается связь, но в классе зависимых объектов не найдено свойство внешнего ключа. В этом случае теневой сторонний объект Будет введено ключевое свойство.", кажется, поддерживает то, что я испортил сущность.

Я пытался исключить несоответствие имени свойства, изменив все ссылки на свойства в объявлениях сущностей Role и RoleClaim ModelBuilder на выражения, чтобы посмотреть, поможет ли жесткая ссылка:

// "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 голосов
/ 17 мая 2018

Похоже, что я допустил несколько ошибок, наиболее уместным является то, что я фактически не предоставлял код для моего UserRole объекта в образце ApplicationDbContext; это на самом деле, где возникла ошибка ...

Код все еще ссылается на оригинальную 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%, что это повлияло на возникшее исключение), я неправильно настроил мой RoleStore в Startup-> 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
        };
    }
}
...