Skip to content

Owned Entity Type Requires Primary Key to be defined when the Parent Entity has relationship with Asp.Net Core Identity User #17985

@fingers10

Description

@fingers10

I have two context in my application. One is AppIdentityDbContext for identity related works and other is AppContext for app related works.

I have an identity user class - ApplicationUser - which has a relation to Profile entity from AppContext and the Profile entity in turn owns a value object ProfileContact.

Steps to reproduce

ApplicationUser

public class ApplicationUser : IdentityUser<Guid>
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Designation { get; set; }
        
    public Guid ProfileId { get; set; }
    public Profile Profile { get; set; }
}

Profile

public class Profile : BaseEntity<Guid>, IAggregateRoot
{
    private Profile()
    {
        // required by EF
    }

    public Profile(string brandName, ProfileContact profileContact)
    {
        BrandName = brandName;
        ProfileContact = profileContact;
    }

    public string BrandName { get; set; }
        
    public ProfileContact ProfileContact { get; private set; }
}

ProfileContact

public class ProfileContact // ValueObject
{
    private ProfileContact()
    {
        // required by EF
    }

    public ProfileContact(string email, string phone, string mobile)
    {
        Email = email;
        Phone = phone;
        Mobile = mobile;
    }

    public string Email { get; private set; }
    public string Phone { get; private set; }
    public string Mobile { get; private set; }
}

Here is the AppContext.

public class AppContext: DbContext
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    private ILoggerFactory GetLoggerFactory()
    {
        IServiceCollection serviceCollection = new ServiceCollection();
        serviceCollection.AddLogging(builder =>
            builder.AddConsole()
                .AddFilter(DbLoggerCategory.Database.Command.Name,
                    LogLevel.Information));
        return serviceCollection.BuildServiceProvider()
            .GetService<ILoggerFactory>();
    }

    public AppContext(DbContextOptions<AppContext> options, IHttpContextAccessor httpContextAccessor) : base(options)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public DbSet<Profile> Profiles { get; set; }
        
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder
                .UseLoggerFactory(GetLoggerFactory())
                .EnableSensitiveDataLogging(true);
        }
    }

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

        builder.Entity<Profile>(ConfigureProfile);
        builder.Entity<ProfileContact>(ConfigureProfileContact);
    }

    private void ConfigureProfile(EntityTypeBuilder<Profile> builder)
    {
        builder.Property(p => p.BrandName)
            .IsRequired()
            .HasMaxLength(50);

        builder.OwnsOne(p => p.ProfileContact);
    }

   private void ConfigureProfileContact(EntityTypeBuilder<ProfileContact> builder)
    {
        builder.Property(pc => pc.Email)
            .IsRequired()
            .HasMaxLength(50);

        builder.Property(pc => pc.Phone)
            .IsRequired()
            .HasMaxLength(10);

        builder.Property(pc => pc.Mobile)
            .IsRequired()
            .HasMaxLength(10);
    }

}

I have configured ProfileContact as owned by Profile entity.

Here is the AppIdentityDbContext.

public class AppIdentityDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
    public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // Customize the ASP.NET Identity model and override the defaults if needed.
        // For example, you can rename the ASP.NET Identity table names and more.
        // Add your customizations after calling base.OnModelCreating(builder);
        builder.Entity<ApplicationUser>().ToTable("Users", "Account");
        builder.Entity<ApplicationRole>().ToTable("Roles", "Account");
        builder.Entity<IdentityUserRole<Guid>>().ToTable("UserRoles", "Account");
        builder.Entity<IdentityUserClaim<Guid>>().ToTable("UserClaims", "Account");
        builder.Entity<IdentityUserLogin<Guid>>().ToTable("UserLogins", "Account");
        builder.Entity<IdentityUserToken<Guid>>().ToTable("UserTokens", "Account");
        builder.Entity<IdentityRoleClaim<Guid>>().ToTable("RoleClaims", "Account");
    }
}

Whenever I try to run seeding, I'm getting the same error when user logs in.

Now whenever user logs in, I get this error:

> Error Description

> Error Stack

Here is my login Post method. I'm using Asp.Net Core Identity scaffolded template.

Login - OnPostAsync

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
   returnUrl = returnUrl ?? Url.Content("~/");

   if (ModelState.IsValid)
   {
       // This doesn't count login failures towards account lockout
       // To enable password failures to trigger account lockout, set lockoutOnFailure: true
       var result = await _signInManager.PasswordSignInAsync(Input.MobileNumber, Input.Password, Input.RememberMe, lockoutOnFailure: true);
       if (result.Succeeded)
       {
         ....

    // If we got this far, something failed, redisplay form
    return Page();
}

Does the value object/owned type needs an key to be defined? What I read and understood about OwnedTypes is that they belong to the same table but can be used as object. Am I doing anything wrong? or I need to add Id property to ProfileContact. If so why? Please assist.

Further technical details

EF Core version: 2.2.6
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: ASP.NET Core 2.2
Operating system: Windows 10 x64
IDE: Visual Studio 2017 15.9.16

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions