https://docs.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-2.2 Explains how to extend the default Identity by subclassing. I have my code setup as explained and it works but now I would like to extend it further because depending on the user role there are different attributes. E.g. An ApplicationUser can have favorites, but the extended ApplicationUser has Elements.
I would like to create more subclasses of ApplicationUser. Is that a good idea?
The model is Element. ApplicationUser has a Set of Element and is allowed to favorite them.
ExtendedApplicationUser inherits from ApplicationUser and can add instances of Element that ApplicationUser can favorite.
What should I do?
Last question, how do I declare the sets in the ApplicationUser entity? Do I use HashSet or DbSet?
Yes, in general, inheritance is the correct way to model this. You need one and only one class that will inherit from
IdentityUser. This would be your base
ApplicationUser. Then, other types of users would inherit from
That said, EF Core's handling of inheritance leaves a lot to be desired. In particular, it only supports TPH (table-per-hierarchy), also known as single-table inheritance. In other words, regardless of what you do, you're only ever going to have one user table, and all the props for all user types will have to reside in that one table. Practically, that just means that you can't have non-nullable properties on your subclasses, since other subclasses would be incapable of providing a value for those, and inserts would then fail. This can be mostly mitigated, though, by using view models, and applying your business rules for what's required and what's not to those instead. You won't get validation at the DB level, but input via forms and such would always be validated.
The above is worth mentioning, given that you can't using more appropriate database modeling strategies such as table-per-type (TPT) or table-per-concrete type (TPC), the use of inheritance is pretty meaningless from a database perspective. Unless, you need to enforce in code that only some set of properties or another can be modified for a particular user type, you can just stick with a single user class. Having multiple user types is not trivial, so there's an argument to be made to keep it simple unless you have a strong need to do otherwise.
In particular, it's easy to mess up the user typing if you're not careful. Single table inheritance works by adding a discriminator column that holds the actual type that is being saved. In other words, if you save an
ApplicationUser, then the value will be "ApplicationUser", or if you save an
ExtendedApplicationUser, then the value will be "ExtendedApplicationUser". EF uses this value to determine what type it should instantiate. Where it gets tricky is with
UserManager<TUser>. The generic type param, not what's passed to methods like
CreateAsync determines the type that will be saved. For example, if you new up an
ExtendedApplicationUser, but save it via a
UserManager<ApplicationUser> instance, then it will be upcast to
ApplicationUser and then saved, resulting in "ApplicationUser" being written to the discriminator column. If you need to work with
ExtendedApplicationUser, then you'll need a
UserManager<ExtendedApplicationUser> instance, and you might end up having multiple such user managers, each for working with a particular type of user. As such, you'll need to be careful that you're using the right user manager for the right scenario.
For you last question, the answer is neither. Collection properties should be either
HashSet can be used to satisfy
ICollection<TEntity> when writing to the property, but don't type it as that.
DbSet is only for the context.