问题
I am working from an example to set up IdentityServer4 using Asp.net Core and EF core. During the process I wanted to separate my data contracts and data access from the core project. To do that I created two related projects, one containing the DbContext and one containing the contracts.
Eventually I'd like to make things more complicated, but to start I am simply trying to add a related object to the IdentityUser, which is defaulted to ApplicationUser and stored as ApsnetUsers in default projects involving Asp.net Identity.
To accomplish this I have created a base ApplicationUserDto class that inherits IdentityUser. I had added the supplemental object/table to that object and tested the application by updating the ModelBuilder and, eventually, everything worked as expected.
Now the problem. My end goal is two have two separate classes that both inherit ApplicationUserDto, one for internal and one for external users. Each will have their own supplemental one-to-one data table. To start constructing this i created two classes, InternalUserDto and InternalUserProfilePropertiesDto.
ApplicationUserDto class:
namespace SingleSignOn.Identity.DataAccess.Contracts
{
public class ApplicationUserDto: IdentityUser {}
}
InternalUserDto.cs
namespace SingleSignOn.Identity.DataAccess.Contracts
{
public class InternalUserDto : ApplicationUserDto
{
public virtual InternalUserProfilePropertiesDto InternalUserProfilePropertiesDto { get; set; }
public InternalUserDto() { }
}
}
InternalUserProfilePropertiesDto.cs
namespace SingleSignOn.Identity.DataAccess.Contracts.UserProperties
{
public class InternalUserProfilePropertiesDto
{
public int Id { get; set; }
public string EmployeeId { get; set; }
public virtual InternalUserDto InternalUserDto { get; set; }
}
}
Using these classes when i attempt to register using the template application for core using Single User Authentication I hit the error
Entity type 'SingleSignOn.Identity.DataAccess.Contracts.InternalUserDto' is in shadow-state. A valid model requires all entity types to have corresponding CLR type
after the ModelBuilder is run.
Below is what i think are the relevant sections of the OnModelCreating from the DbContext:
namespace SingleSignOn.Identity.DataAccess
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder
.HasAnnotation("ProductVersion", "1.0.0-rc3")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
//Snipped
modelBuilder.Entity("SingleSignOn.Identity.DataAccess.Contracts.ApplicationUserDto", b =>
{
b.Property<string>("Id");
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasAnnotation("MaxLength", 256);
b.Property<bool>("EmailConfirmed");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasAnnotation("MaxLength", 256);
b.Property<string>("NormalizedUserName")
.HasAnnotation("MaxLength", 256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasAnnotation("MaxLength", 256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
b.ToTable("Users");
});
modelBuilder.Entity("SingleSignOn.Identity.DataAccess.Contracts.InternalUserDto", b => {
b.ToTable("Users");
});
modelBuilder.Entity("SingleSignOn.Identity.DataAccess.Contracts.UserProperties.InternalUserProfilePropertiesDto", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("EmployeeId");
b.HasKey("Id");
});
modelBuilder.Entity<InternalUserDto>()
.HasOne(typeof(InternalUserProfilePropertiesDto).ToString())
.WithOne()
.HasForeignKey(typeof(InternalUserProfilePropertiesDto).ToString(), "UserId")
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
}
I've searched for shadow-state and found the merge request that checks for this as a required validation, but I don't understand why this type doesn't have a CLR type. This same setup worked fine using just the ApplicationUserDto and the profile properties, but trying to use the inherited class immediately causes the error. Any insight about what I am doing wrong, what shadow-state is or why my types don't have CLR types? I can provide any additional code or details required.
EDIT
I appear to have solved my own problem. I did not have either the InternalUserDto or InternalUserProfilePropertiesDto as DbSets in the DbContext. When I added those and changed the HasOne to:
.HasOne(a => a.InternalUserProfilePropertiesDto)
Everything seems to work now. What i still find confusing is that i never had DbSets for the original ApplicationUserDto and i never encountered this problem, so I am still interested if anyone has any thoughts or advice on this subject and how I can avoid this kind of pitfall in the future.
来源:https://stackoverflow.com/questions/41987847/entity-type-type-is-in-shadow-state-a-valid-model-requires-all-entity-types-t