sql-serverasp.net-coreasp.net-core-identity

ASP.NET Core Identity ApplicationUser Foreign Key Returning NULL


I have an old ASP.NET MVC project that uses Identity/SQL Server which has worked fine for over a decade and am now attempting to begin a new version that uses ASP.NET Core.

I have extended the IdentityUser into ApplicationUser with some custom fields that are all coming in from the database fine. But I also have a foreign key to a separate entity that returns NULL no matter what I try. All of this worked fine in the old project.

ApplicationUser class:

public partial class ApplicationUser : IdentityUser
{
    public string CustomField { get; set; }

    public Guid? EntityId { get; set; }
    [ForeignKey("EntityId")]
    public virtual EntityObject? Entity { get; set; }
}

Foreign key EntityObject class:

[PrimaryKey("EntityId")]
public class EntityObject
{
    [Key]
    public System.Guid EntityId { get; set; }
    public string Foo { get; set; }
    public string Bar { get; set; }
}

DbContext class:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

CustomField is populated but EntityObject is always NULL where it wasn't in the old project.

I have trawled through various threads here trying all different combinations (e.g. implementing OnModelCreating()) and come to no avail. I am coming from a very much database-first background and as this is a legacy database migrations are difficult so am trying to avoid using as much as possible.

Update This is the code I am using to retrieve the user information and is where I see that the foreign key Entity object is null:

ApplicationUser user = userManager.GetUserAsync(User).Result;

I noticed in the generated SQL in the console that it only queries the AspNetUsers table and does not attempt a join


Solution

  • Entity Framework Core doesn't do lazy loading out of the box -- it can still be done, but be aware of the warning on above linked page --, instead, you have to specify which navigation properties to include/load -- here the Entity property.

    When you would be working directly with the ApplicationDbContext that would look like below; notice the Include statement.

    var user = await dbc.Users
        .Include(o => o.Entity)
        .FirstOrDefaultAsync();
    

    Since you're dealing with the ASP.NET Identity UserManager instead of directly with the ApplicationDbContext, you can configure it to always eagerly load that Entity navigation property, by configuring an AutoInclude for given property in the OnModelCreating override of your ApplicationDbContext.

    Link to the documentation.

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<ApplicationUser>().Navigation(e => e.Entity).AutoInclude();
    }
    

    From the AutoInclude documentation:

    Configures whether this navigation should be automatically included in a query.


    Sidenote; don't use .Result; instead await given call.

    var user = await userManager.GetUserAsync(User);
    

    There's much info about this on StackOverflow.