Search code examples
asp.net-mvcentity-frameworkasp.net-mvc-4simplemembership

Customizing SimpleMembership with my own Database in MVC 4 / EF


I am working on a project using ASP.Net MVC4 where I use SimpleMembership to deal with membership system .

In addition of UserProfile table where should I store default data, I have two tables in my own database (Student , Teacher ) .

I customized the registration adding a new field where I ask the new user to define whether he is a teacher or a student .

public class UsersContext : DbContext
{
    //public UsersContext()
    //    : base("DefaultConnection")
    //{
    //}
    public DbSet<Student> Students { get; set; }
    public DbSet<Teacher> Teachers { get; set; }
    public DbSet<UserProfile> UserProfiles { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<UserProfile>()
            .HasRequired(u => u.Student)
            .WithOptional(p => p.UserProfile)
            .Map(m => m.MapKey("IDStudent"));

        modelBuilder.Entity<UserProfile>()
            .HasRequired(u => u.Teacher)
            .WithOptional(p => p.UserProfile)
            .Map(m => m.MapKey("IDTeacher"));
    }
}

And the Table of User Profile is becoming like this

[Table("UserProfile")]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string UserName { get; set; }
    public string AccountType { get; set; }
    public virtual Student student { get; set; }
    public virtual Teacher teacher { get; set; }
}

I have added this a foreign key in the both classes Student , Teacher

public virtual UserProfile UserProfile { get; set; }

On Account Controller / Register Class

WebSecurity.CreateUserAndAccount(
    model.UserName,
    model.Password,
    new { AccountType = model.AccountType },
    false);

WebSecurity.Login(model.UserName, model.Password);
UsersContext db = new UsersContext();

var membership = (SimpleMembershipProvider)Membership.Provider;
membership.ValidateUser(model.UserName, model.Password);
membership.ConfirmAccount(model.UserName, model.Password);
UserProfile user = db.UserProfiles.FirstOrDefault(u => u.UserName.ToLower() == model.UserName.ToLower());

if (model.AccountType == "Teacher")
{
    if (userid != null) 
        CreateNewTeacher(user.UserId , model.UserName);
}

if (model.AccountType == "Student")
{
    if (userid != null) 
        CreateNewStudentt(user.UserId , model.UserName);
}

After writing those two classes

public void CreateNewStudent( int id, string username)
{
    // Attempt to register the user
    using (UsersContext db = new UsersContext())
    {
        Student student = new Student {User_Name = username};
        student.id=id;
        db.Students.Add(student);
        db.SaveChanges();
    }
}

public void CreateNewTeacher(int id,string username)
{
    using (UsersContext db = new UsersContext())
    {    
        Teacher teacher = new Teacher();
        teacher.id=id;
        db.Teacher.Add(teacher);
        db.SaveChanges();
    }
}

My Question is : At what level should I create the user in my own table (I would like to keep the same ID ) . So , I would like to know when the simple membership create the user in its tables to copy it in mine .

How Can I get the ID of the user !

Does my strategy to deal with the Accounting good ! or am I wrong in thinking that way .

UPDATE I have added [key] (It hasn't been generated )

 public partial class Agent
    {
        public Teacher()
        {
            this.Tasks = new HashSet<Tasks>();
        }

         [Key]
        public int ID_Teacher { get; set; }
        public string Name_Teacher { get; set; }
        public System.DateTime Bday_Teacher { get; set; }
        public string User_Name { get; set; }

        public virtual ICollection<Task> Tasks { get; set; }


    }


  public partial class Task
    {
         [Key, ForeignKey("Teacher"), Column(Order = 0)]
        public int ID_Teacher { get; set; }

         [Key, ForeignKey("Student"), Column(Order = 1)]
        public int ID_Student { get; set; }

         [Key, ForeignKey("DateT"), Column(Order = 2)]
        public int ID_DateT { get; set; }

         [Key]
        public Nullable<int> ID_Comment { get; set; }

        public virtual Teacher Teacher { get; set; }
        public virtual Student Student { get; set; }
        public virtual DateT DateT { get; set; }
    }

I appreciate your time and efforts !

Regards ,


Solution

  • You need to created a one-to-one relationship between UserProfile and Student, also between UserProfile and Teacher. I usually use Property Mapping rather than fluent api

    Try this for Entities, i went ahead and created a relationship between Teacher and Student for you:

    [Table("UserProfile")]
    public class UserProfile
    {
      [Key]
      [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
      public int UserId { get; set; }
      public string UserName { get; set; }
      public string AccountType { get; set; }   
    }
    
    
    public class Teacher
    {
       [Key]
       [ForeignKey("UserProfile")]
       [DatabaseGeneratedAttribute(DatabaseGeneratedOption.none)]
       public int UserId { get; set; }
    
       public string Name { get; set; }
    
       public virtual ICollection<Student> Student{ get; set; }
       public virtual UserProfile UserProfile { get; set; }
    }
    
    public class Student
    {
       [Key]
       [ForeignKey("UserProfile")]
       [DatabaseGeneratedAttribute(DatabaseGeneratedOption.none)]
       public int UserId { get; set; }
    
       [ForeignKey("Teacher")]       
       public int? TeacherId{ get; set; }
    
       public string Name { get; set; }
    
       public virtual UserProfile UserProfile{ get; set; }
       public virtual Teacher teacher { get; set; }
    }
    

    In your Account Controller try this:

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // Attempt to register the user
                try
                {
                    WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new { AccountType = model.AccountType }, false);
                    WebSecurity.Login(model.UserName, model.Password);
    
                    var userProfile = db.UserProfiles.Local.SingleOrDefault(u => u.UserName == User.Identity.Name)
                                    ?? db.UserProfiles.SingleOrDefault(u => u.UserName == User.Identity.Name);
    
                    if (userProfile.AccountType.ToLower() == "teacher")
                    {
                        return RedirectToAction("Create", "Teacher"); //Created a Teacher Controller separate
                    }
    
                    return RedirectToAction("Create", "Student"); //same with Student
                }
                catch (MembershipCreateUserException e)
                {
                    ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
                }
            }
    
            // If we got this far, something failed, redisplay form
            return View(model);
        }
    

    Your Student or Teacher Create Actions should look like this inside Student or teacher Controllers:

    //
        // GET: /Student/
        [HttpGet]
        public ActionResult Create()
        {            
            return View();
        }
    
        //
        // POST: /Student/
        [HttpPost]
        public ActionResult Create(StudentViewModel model)
        {
            if (ModelState.IsValid)
            {
                using (UsersContext db = new UsersContext())
                {
                    var userProfile = db.UserProfiles.Local.SingleOrDefault(u => u.UserName == User.Identity.Name)
                                    ?? db.UserProfiles.SingleOrDefault(u => u.UserName == User.Identity.Name);
                    if (userProfile != null)
                    {
                        var student= new Student
                                         {
                                             UserProfile = userProfile,
                                             Name= model.Name                                                                                          
                                         };                 
    
                        db.Students.Add(student);                          
    
                        db.SaveChanges();
                    }
                }
            }
            return View(model);
        }