2

In my project, Admin adds Instructors, then each Instructor adds his students. When they are added, they'll receive an email asks them to complete registration .

I have the following classes in my project :

1-Student class

Student: int id, int Registry number, int grade, string password, string email, string name

2-Instructor class:

Instructor: int id, string name , string email , string password

3-My database context:

 public class InstructorContext:DbContext
{
    public InstructorContext() : base("InstructorContext")
    {
    }

    public DbSet<Instructor> Instructors { get; set; }
    public DbSet<Student> Students { get; set; }}

When a user loges in , I must determine whether he is an Admin or Instructor or Student. Do I have to use role-based authentication? I already have 2 separate classes for different roles. Is it possible for both of them to inherit from IdentityUser?

1 Answer 1

9

No, you cannot have multiple user tables with Identity, at least not technically. All the other core components of Identity (roles, claims, logins, etc.) are setup with foreign keys to one user table.

For your scenario here, you should use inheritance. For example:

public class ApplicationUser : IdentityUser

public class Instructor : ApplicationUser

public class Student : ApplicationUser

By default, Entity Framework will create the one table for ApplicationUser and add a Discriminator column to it. This column will have one of three possible values: "ApplicationUser", "Instructor", and "Student". When EF reads from this table, it will use this column to instantiate the right class. This is what's known as single-table inheritance (STI) or alternatively as table-per-hierarchy (TPH). The main downside to this approach is that all of the properties for all of the classes must be represented on the same table. If you're creating a new Student for example, the columns for an Instructor would still be on the record, only with nulls or defaults for those values. This also means that you can't enforce a property on something like Instructor be required at the database level, as that would prevent saving ApplicationUser and Student instances which are unable to provide those values. In other words, all your properties on your derived classes must be nullable. However, you can always still enforce something like a property being required for the purpose of a form using view models.

If you really want to have separate tables, you can somewhat achieve that goal by changing the inheritance strategy to what's called table-per-type (TPT). What this will do is keep the table for ApplicationUser, but add two additional tables, one each for Instructor and Student. However, all the core properties, foreign keys, etc. will be on the table for ApplicationUser, since that is where those are defined. The tables for Instructor and Student would house only properties that are defined on those classes (if any) and a foreign key to the table for ApplicationUser. When querying, EF will then do joins to bring in the data from all of these tables and instantiate the appropriate classes with the appropriate data. Some purists like this approach better as keeps the data normalized in the database. However, it's necessarily heavier on the query side because of the joins.

One last word of caution, as this trips people up constantly dealing with inheritance with Identity. The UserManager class is a generic class (UserManager<TUser>). The default instance in AccountController, for example, is an instance of UserManager<ApplicationUser>. As a result, if you use that instance, all users returned from queries will be ApplicationUser instances, regardless of the value of the Discriminator column. To get Instructor instances, you would need to instantiate UserManager<Instructor> and use that for your Instructor-related queries.

This is especially true with creating users for the first time. Consider the following:

var user = new Instructor();
UserManager.Create(user);

You might expect that the user would be saved with a discriminator value of "Instructor", but it will actually be saved with "ApplicationUser". This is because, again, UserManager is an instance of UserManager<ApplicationUser> and your Instructor is being upcasted. Again, as long as you remember to use the appropriate type of UserManager<TUser> you'll be fine.

Sign up to request clarification or add additional context in comments.

11 Comments

If I have only 1 Admin , Do I have to add "Admin" class ? or is it enough to check for the email and password?
No. I mean you could, but "Admin" is more typically considered a role. A good way to draw a distinction is to consider overlap. If something like an Instructor could also be an admin (whether or not you're actually going to allow that), then it's probably better as a role. That way you could make an instructor an admin if you needed to, without effecting the rest of the system. Otherwise you'd end up with stuff like Instructor, Admin and AdminInstructor, which would obviously get messy very quickly,
Also, by using a role you can then add something like [Authorize(Roles = "Admin")] to controllers/actions that are only for admins. As another derived type, you would need to create something custom to determine the level of access.
Oh sorry. I think I misread your question to begin with. You could technically have Instructor and Student as roles as well. I think it's better to have them as derived types because they're likely to have their own data. At the very least, it might be nice to have something like ICollection<Student> on your Instructor class, for example, which would not make sense for ApplicationUser or Student. Something like Admin is more of a special case that doesn't really imply a lot of specific data needing to be stored. It's more about privilege.
Then you're doing something wrong. Most likely, you're not using view models as you should be and jacking up the entity change tracking. You should open a question.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.