I was playing around with EF Core and ASP.NET Core and stumbled into the following problem. I wanted to add some extra data to the User Object in form of a List. The problem is that the List is never updated.
Here's my DbContext:
public class ApplicationDbContext : IdentityDbContext<HostUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
}
And now my User Object:
public class HostUser : IdentityUser
{
[PersonalData]
public ICollection<GuestUser> GuestUsers { get; set; }
}
And here is the adding of a new GuestUser in the Contoller:
[HttpPost]
public async Task<IActionResult> Post([FromBody]GuestUser userToInsert)
{
if (userToInsert == null)
{
return BadRequest();
}
var currentUser = await GetCurrentUserAsync();
if (currentUser == null)
{
return Forbid();
}
if(currentUser.GuestUsers?.Any(user => user.Id == userToInsert.Id) ?? false)
{
return BadRequest();
}
if(currentUser.GuestUsers == null)
{
currentUser.GuestUsers = new List<GuestUser>();
}
currentUser.GuestUsers.Add(userToInsert);
await userManager.UpdateAsync(currentUser);
return Ok();
}
My question is if this is a complete wrong approach and I have to add a DbSet of GuestUser in the DbContext and map it to the user. If this is the case I have no idea how to achieve this. Note: The GuestUser in this case is not another IdentityUser, it's local user data
This is how it might look like:
Entitties:
public class HostUser : IdentityUser
{
public virtual ICollection<GuestUser> GuestUsers { get; set; }
}
public class GuestUser
{
public int HostUserId { get; set; }
public virtual HostUser HostUser { get; set; }
}
DB Context:
public class ApplicationDbContext : IdentityDbContext<HostUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<GuestUser> GuestUsers { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<HostUser>(
typeBuilder =>
{
typeBuilder.HasMany(host => host.GuestUsers)
.WithOne(guest => guest.HostUser)
.HasForeignKey(guest => guest.HostUserId)
.IsRequired();
// ... other configuration is needed
});
builder.Entity<GuestUser>(
typeBuilder =>
{
typeBuilder.HasOne(guest => guest.HostUser)
.WithMany(host => host.GuestUsers)
.HasForeignKey(guest => guest.HostUserId)
.IsRequired();
// ... other configuration is needed
});
}
}
Controller action:
[HttpPost]
public async Task<IActionResult> Post([FromBody] GuestUser userToInsert)
{
// All checks and validation ...
// You can get the current user ID from the user claim for instance
int currentUserId = int.Parse(User.FindFirst(Claims.UserId).Value);
// _context is your ApplicationDbContext injected via controller constructor DI
userToInsert.HostUserId = currentUserId;
// Save new guest user associated with the current host user
_context.GuestUsers.Add(userToInsert);
await _context.SaveChangesAsync();
// If you need to get the current user with all guests
List<HostUser> currentUser = await _context.Users.Include(host => host.GuestUsers).ToListAsync();
return Ok(currentUser);
}
Here I provided the full code - how to configure custom Identity based DB context (all needed custom classed inherited from base classes used by IdentityDbContext
).
There are two issue.
1.First you should separate ViewModel
from entity (don't pass GuestUser
as your web/api method for parameter)
2.then as you mentioned you should declare a DBSet
of GuestUser
in DbContext
and map it to the user table.
Define your Custom User Entity
public class ApplicationUser : IdentityUser
{
public string CustomTag { get; set; }
}
Declare your Custom DbContext
Next use this type as a generic argument for the context:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>(b =>
{
// Each User can have many UserClaims
b.HasMany(e => e.Claims)
.WithOne()
.HasForeignKey(uc => uc.UserId)
.IsRequired();
});
}
}
Update ConfigureServices to use the new ApplicationUser class:
services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
if you have any other problems let me know.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With