8

I'm creating a simple application for university where a student can make some type of request which is then processed by an employee of particular speciality.

I would like to use default MVC5 identity system and to extend the ApplicationUser class using TPH pattern. So I added the common properties to the ApplicationUser:

public class ApplicationUser : IdentityUser
    {
        [Required]
        public string FirstName { get; set; }
        [Required]
        public string LastName { get; set; }
        [Required]
        public string Email { get; set; }
    }

then I created two classes which inherits the ApplicationUser:

public class Student : ApplicationUser
    {
        public string PersonalNumber { get; set; }
        public bool Monitor { get; set; }
        public virtual Group Group { get; set; }
        public virtual ICollection<Request> Requests { get; set; }
    }

public class Employee : ApplicationUser
    {
        public virtual EmployeeSpeciality EmployeeSpeciality { get; set; }
        public virtual ICollection<Request> Requests { get; set; }
    }

what I currently want is to make both types of users register as a base Identity and to keep both in a single table as in the inheritance example on asp.net

As I thought, it would be enough to initialize user var in AccountController which is then passes to the UserManager as a Student or as an Employee. But after trying to register as a Student i'm getting this exception:

Exception Details: System.Data.SqlClient.SqlException: Invalid column name 'PersonalNumber'.
Invalid column name 'Monitor'.
Invalid column name 'EmployeeSpeciality_Id'.
Invalid column name 'Group_Id'.

My context class:

public class EntityContext : IdentityDbContext
    {
        public EntityContext()
            : base("DbConnection")
        {
        }

        public DbSet<ApplicationUser> ApplicationUsers { get; set; }
        public DbSet<Student> Students { get; set; }
        public DbSet<Employee> Employees { get; set; }
        ...
    }

and a part of controller's action:

public async Task<ActionResult> Register(RegisterViewModel model)
    {
        if (ModelState.IsValid)
        {
            var user = new Student()
            {
                UserName = model.UserName,
                FirstName = model.FirstName,
                LastName = model.LastName,
                Email = model.Email
            };
            var result = await UserManager.CreateAsync(user, model.Password);

I tried setting ApplicationClass to an abstract class, but no luck. Any help would be appreciated.

UPDATE: The problem wasn't in the code itself. I simply haven't dropped (or updated) the database after making these changes to the model. After it everything works just fine.

2 Answers2

1

@Dragonheart: I tried this repro and it would work fine if you remove the DBSet declarations in you context class. The IdentityDbContext would handle you TPH pattern and add a Discriminator column in the table to differentiate the child classes.

Suhas Joshi
  • 1,042
  • 9
  • 11
  • 1
    After your comment I tried to reproduce evertything (because I've already moved to the TPT pattern by holding Student and Employee in their own tables and having a navigation property in ApplicationUser for both of them) and it works even without removing DBSet's for Student and Employee. The real problem was kinda silly - I simply forgot to drop or update the DB via migrations. It's a facepalm, I know. Thx for your attention anyway :) – Vladimir Glushkov Jan 15 '14 at 11:13
  • 4
    @Dragonheart If this is what solved your problem, please post it as an answer and accept it, for the benefit of future readers. Thanks. – Asad Saeeduddin Apr 21 '14 at 19:32
0

As ApplicationUser is inherited from IdentityUser, remove it from your DbContext class. On the other hand, there is no need to create an abstract class (you can create if you prevent from creating a user except from Student or Employee classes. For more information you might have a look at Table-per-Hierarchy.

For Register part, try something like that:

Student user = new Student
{
    UserName = model.UserName,
    FirstName = model.FirstName,
    LastName = model.LastName,
    Email = model.Email
};
var result = UserManager.Create(user, model.Password);

Hope this helps...

Murat Yıldız
  • 11,299
  • 6
  • 63
  • 63
  • Why are you creating a new `ApplicationUser` instance, just to throw it away on the next line, replacing it with a `Student` instance? – Bradley Uffner Oct 06 '16 at 18:38
  • I was using this code at the same Controller for both type of users but for this situation there is no need to create ApplicationUser instance. and for this reason I wanted to create a shared property. Thanks for informing... – Murat Yıldız Oct 06 '16 at 21:46