Home > database >  Custom IdentityUser class property/dependent entity is always null
Custom IdentityUser class property/dependent entity is always null

Time:06-15

I am using ASP.NET Core targeting NET6.0 using the Microsoft.AspNetCore.Identity user management system with EntityFrameworkCore.

I have a custom dependent entity (Players) which I want to link to my custom ApplicationUser class in a one-to-one relationship. Every AspNetUsers record has an associated single record in the previously existing my_db_schema.tblPlayers database table. The AspNetCore identity tables are in the same database. Both are populated with real data (This project is to migrate from AspNetIdentity to AspNetCore.Identity)

[Table("tblPlayers", Schema = "my_db_schema")]
    public class Players
    {
        //[Key]
        public int Id { get; set; }
        public string PlayerName { get; set; }
        //[ForeignKey("User")]
        public string UserId { get; set; }
        public ApplicationUser User { get; set; }
    }

The UserId is a foreign key to the Id column on the AspNetUsers table. The Id column is a primary key.

My custom ApplicationUser class is defined as this:

public class ApplicationUser : IdentityUser<string>
{
    public Players Players { get; set; }
}

In my custom ApplicationDbContext OnModelCreating function, I have the following:

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Players>().HasKey(t => t.Id);

            modelBuilder.Entity<ApplicationUser>()
            .HasOne<Players>(s => s.Players)
            .WithOne(u => u.User)
            .HasForeignKey<Players>(ad => ad.UserId).HasPrincipalKey<ApplicationUser>(x => x.Id).IsRequired();

}

I am not interested in any automatic removal/management of data in the tblPlayers database table. I am not interested in running migrations or scaffolding. All that I need is that for any place in my code where I get an instance of an ApplicationUser, that the Players property is populated with data from the tblPlayers database table. No matter what combination of fluid API, or annotations I use, the ApplicationUser.Players property is always null, and the rest of the ApplicationUser properties are populated as expected.

I have seen these articles:

Why custom properties for IdentityUser custom class are always null

Custom property is null IdentityUser

But both of these require a specific instantiation of the custom ApplicationUserManager class. I would like to avoid having to do that in all areas of my code where ApplicationUserManager is referenced if possible.

Please help :)

CodePudding user response:

Implementing a custom UserManager is trivial -

public class CustomUserManager : UserManager<User>

Then register it at startup

services.AddIdentity<User, Role>()
        .AddUserManager<QuestUserManager>()

From there you'll just inject the custom user manager class where you need it.

public class SomeController : Controller {
  private readonly CustomUserManager _userManager; 

  public SomeController(CustomUserManager userManager)
  {
    _userManager = userManager;
  }
}

The only way you'll get the Players property populated is to override the method(s) that return a User and add .Include(u => u.Players) to the EF Core queries - something like

public override async Task<User> FindByIdAsync(string userId)

  return await Users.Include(u => u.Players).SingleOrDefaultAsync(u => u.Id == userId);
}

CodePudding user response:

You don't have to create your own custom UserManager in order to do that. But @Mr.T is right, it is necessary to include the dependant table in order to retrieve records filled.

So you have 3 options, except creating a custom UserManager:

  1. In case you are following the Repository pattern you would do something like the following:
//Yor 'UserRepository' or whatever
public async Task<List<ApplicationUser>> GetUsers()
{
    var set = _context.Set<ApplicationUser>()
        .Include(user => user.Players);
    
    return await set.ToListAsync();
}
  1. In case you are using the DbContext directly - you would basically do the same as within the method above.

  2. And in case you are using the default UserManager:

var filledUsers = await _userManager.Users.Include(user => user.Players).ToListAsync();
  • Related