I'm following clean architecture and Domain Driven Design in my project. I'm using strongly typed Id for my User entity. User entity is extended from ASP.NET
IdentityUser
class. But my issue is when I use a Strongly typed Id for my IdentityUser
, I also have to use same type for IdentityRole
type. IdentityRole<UserId>
. I'm thinking whether this is correct or not. Because IdentityRole doesn't need to have UserId type right?
When I need to create a User role it looks like this,
new IdentityRole<UserId>(UserRoles.User) { Id = new UserId(Guid.NewGuid()) }
new IdentityRole<UserId>(UserRoles.Admin) { Id = new UserId(Guid.NewGuid()) }
Is there any other proper way to use Strongly typed Ids with IdentityUser? Or is there anything I'm doing wrong?
User Entity
public class User : IdentityUser <UserId>
{
public override UserId Id { get; set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
private User(string firstName, string lastName, string email, string passwordHash)
{
// assignments...
}
public static Result<User> Create(string firstName, string lastName, string email, string password)
{
// user creation code...
}
}
UserId
public record UserId (Guid Value);
IdentityDbContext
public class ApplicationUserIdentityDbContext : IdentityDbContext<User, IdentityRole<UserId>,UserId>
{
public ApplicationUserIdentityDbContext(DbContextOptions<ApplicationUserIdentityDbContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.HasDefaultSchema("User");
builder.Entity<User>()
.Property(u => u.Id)
.HasConversion(
id => id.Value,
value => new UserId(value));
builder.Entity<IdentityRole<UserId>>()
.Property(userRole => userRole.Id)
.HasConversion(
userRoleId => userRoleId.Value,
value => new UserId(value));
}
}
Program.cs
builder.Services.AddDbContext<ApplicationUserIdentityDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DBConnection")));
builder.Services.AddIdentityCore<User>()
.AddRoles<IdentityRole<UserId>>()
.AddEntityFrameworkStores<ApplicationUserIdentityDbContext>();
PS: I'm using AddIdentityCore instead AddIdentity because I've two IdentityUser types in my system. Because I can't call AddIdentity twice I'm using AddIdentityCore.
I have got a proper solution for my issue.
public class User : IdentityUser
{
[NotMapped]
public new UserId Id
{
get => new UserId(new Guid(base.Id).ToString());
private set => base.Id = value.Value.ToString();
}
public string FirstName { get; private set; }
public string LastName { get; private set; }
private User(string firstName, string lastName, string email, string passwordHash)
{
// assignments...
}
public static Result<User> Create(string firstName, string lastName, string email, string password)
{
// user creation code...
}
}
with this approach I can use strongly-typed id with my Entity following clean architecture and Domain Drive Design. using new
keyword for Id field, I'm hiding my base class Id field to external. Because I'm using NotMapped
annotation it won't map this strongly-typed Id column to database.