Hi,
I am currently working on a DogCarePlatform that has both Owners as users and Dogsitters as users. I've reached a point where I need both of those entities to be seperate Identities and act like AspNetCoreUsers. I don't know how to create a second type of Identity. This is my code up till now.
Dogsitter class:
public class Dogsitter : ApplicationUser { public Dogsitter() { this.Id = Guid.NewGuid().ToString(); this.Appointments = new HashSet<Appointment>(); this.Comments = new HashSet<Comment>(); } public string Id { get; set; } public string Name { get; set; } public int Age { get; set; } public Gender Gender { get; set; } public DateTime DateOfBirth { get; set; } public string ImageUrl { get; set; } public string Description { get; set; } public decimal WageRate { get; set; } public string Address { get; set; } public decimal Rating { get; set; } public ICollection<Comment> Comments { get; set; } public ICollection<Appointment> Appointments { get; set; } }
Owner class:
public class Owner : ApplicationUser { public Owner() { this.Id = Guid.NewGuid().ToString(); this.Comments = new HashSet<Comment>(); this.Dogs = new HashSet<Dog>(); this.Appointments = new HashSet<Appointment>(); } public string Id { get; set; } public string Name { get; set; } public string ImageUrl { get; set; } public decimal Rating { get; set; } public string Address { get; set; } public ICollection<Comment> Comments { get; set; } public ICollection<Dog> Dogs { get; set; } public ICollection<Appointment> Appointments { get; set; } }
Application user class
public class ApplicationUser : IdentityUser, IAuditInfo, IDeletableEntity { public ApplicationUser() { // Application user this.Id = Guid.NewGuid().ToString(); this.Roles = new HashSet<IdentityUserRole<string>>(); this.Claims = new HashSet<IdentityUserClaim<string>>(); this.Logins = new HashSet<IdentityUserLogin<string>>(); } // Audit info public DateTime CreatedOn { get; set; } public DateTime? ModifiedOn { get; set; } // Deletable entity public bool IsDeleted { get; set; } public DateTime? DeletedOn { get; set; } public virtual ICollection<IdentityUserRole<string>> Roles { get; set; } public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; } public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; } }
And this is the appointment class that needs to do the relations properly between Dogsitter and Owner:
public class Appointment : BaseDeletableModel<string> { public bool IsHappening { get; set; } public int Timer { get; set; } public decimal TaxSoFar { get; set; } public DateTime Date { get; set; } public DateTime StartTime { get; set; } public DateTime EndTime { get; set; } public string OwnerId { get; set; } public virtual Owner Owner { get; set; } public string DogsitterId { get; set; } public virtual Dogsitter Dogsitter { get; set; } }
After all the migrations and updates have been applied I am left with AspNetCoreUsers table that has all of the Dogsitter's and Owner's properties in one place. There are no Dogsitters or Owners tables in the end.
This is the ApplicationDbContext.cs:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string> { private static readonly MethodInfo SetIsDeletedQueryFilterMethod = typeof(ApplicationDbContext).GetMethod( nameof(SetIsDeletedQueryFilter), BindingFlags.NonPublic | BindingFlags.Static); public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<Dogsitter> Dogsitters { get; set; } public DbSet<Owner> Owners { get; set; } public DbSet<Comment> Comments { get; set; } public DbSet<Dog> Dogs { get; set; } public DbSet<Appointment> Appointments { get; set; } public DbSet<Setting> Settings { get; set; } public override int SaveChanges() => this.SaveChanges(true); public override int SaveChanges(bool acceptAllChangesOnSuccess) { this.ApplyAuditInfoRules(); return base.SaveChanges(acceptAllChangesOnSuccess); } public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) => this.SaveChangesAsync(true, cancellationToken); public override Task<int> SaveChangesAsync( bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) { this.ApplyAuditInfoRules(); return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); } protected override void OnModelCreating(ModelBuilder builder) { // Needed for Identity models configuration base.OnModelCreating(builder); builder.Entity<Appointment>() .HasOne(p => p.Owner) .WithMany(t => t.Appointments) .HasForeignKey(m => m.OwnerId) .OnDelete(DeleteBehavior.Restrict); builder.Entity<Appointment>() .HasOne(p => p.Dogsitter) .WithMany(t => t.Appointments) .HasForeignKey(m => m.DogsitterId) .OnDelete(DeleteBehavior.Restrict); ConfigureUserIdentityRelations(builder); EntityIndexesConfiguration.Configure(builder); var entityTypes = builder.Model.GetEntityTypes().ToList(); // Set global query filter for not deleted entities only var deletableEntityTypes = entityTypes .Where(et => et.ClrType != null && typeof(IDeletableEntity).IsAssignableFrom(et.ClrType) && et.BaseType == null); foreach (var deletableEntityType in deletableEntityTypes) { var method = SetIsDeletedQueryFilterMethod.MakeGenericMethod(deletableEntityType.ClrType); method.Invoke(null, new object[] { builder }); } // Disable cascade delete var foreignKeys = entityTypes .SelectMany(e => e.GetForeignKeys().Where(f => f.DeleteBehavior == DeleteBehavior.Cascade)); foreach (var foreignKey in foreignKeys) { foreignKey.DeleteBehavior = DeleteBehavior.Restrict; } } private static void ConfigureUserIdentityRelations(ModelBuilder builder) { builder.Entity<ApplicationUser>() .HasMany(e => e.Claims) .WithOne() .HasForeignKey(e => e.UserId) .IsRequired() .OnDelete(DeleteBehavior.Restrict); builder.Entity<ApplicationUser>() .HasMany(e => e.Logins) .WithOne() .HasForeignKey(e => e.UserId) .IsRequired() .OnDelete(DeleteBehavior.Restrict); builder.Entity<ApplicationUser>() .HasMany(e => e.Roles) .WithOne() .HasForeignKey(e => e.UserId) .IsRequired() .OnDelete(DeleteBehavior.Restrict); } private static void SetIsDeletedQueryFilter<T>(ModelBuilder builder) where T : class, IDeletableEntity { builder.Entity<T>().HasQueryFilter(e => !e.IsDeleted); } private void ApplyAuditInfoRules() { var changedEntries = this.ChangeTracker .Entries() .Where(e => e.Entity is IAuditInfo && (e.State == EntityState.Added || e.State == EntityState.Modified)); foreach (var entry in changedEntries) { var entity = (IAuditInfo)entry.Entity; if (entry.State == EntityState.Added && entity.CreatedOn == default) { entity.CreatedOn = DateTime.UtcNow; } else { entity.ModifiedOn = DateTime.UtcNow; } } } }
I would really like some input here. Thanks in advance.