Reputation: 399
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
Upvotes: 4
Views: 3236
Reputation: 28290
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
).
Upvotes: 4
Reputation: 3009
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.
Upvotes: 2