0

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 ,

tereško
  • 58,060
  • 25
  • 98
  • 150
ImnotaGeek
  • 207
  • 1
  • 4
  • 13
  • Seems like you already found the user's Id. This seems more like a question for [Code Review](http://codereview.stackexchange.com/) – Artless Apr 03 '13 at 17:50
  • I appreciate your help, I got a problem whenever I save (Teacher/Client ) " EntityType 'Teacher' has no key defined. Define the key for this EntityType." . knowing that I generate my own database from SQL Server Database . Any suggestions ! – ImnotaGeek Apr 03 '13 at 18:15
  • Can you post your `Teacher` class? – Artless Apr 03 '13 at 18:18
  • 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` – Komengem Apr 03 '13 at 18:33
  • @Trickery : I have just shared the code that u ve asked ! – ImnotaGeek Apr 03 '13 at 18:45

1 Answers1

0

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);
    }
Komengem
  • 3,662
  • 7
  • 33
  • 57
  • Thanks ! I have tried that , it doesn't work out . The id of the 'teacher' has not been auto-incremented (All the time get val 0 , I am going with this strategy , it's simpler :: http://stackoverflow.com/questions/15333324/how-to-populate-custom-properties-tables-when-using-simplemembership-in-asp-net ) . I got this exception "Unable to determine composite primary key ordering for type 'Task'. Use the ColumnAttribute or the HasKey method to specify an order for composite primary keys." – ImnotaGeek Apr 03 '13 at 18:54
  • I am still writing it up, give me a sec and i will write you an action that works – Komengem Apr 03 '13 at 18:56
  • Thank you , I am trying it out ; If I am not wrong we need to add " public int TeacherId{ get; set; }" to "Teacher" Entity ! – ImnotaGeek Apr 03 '13 at 19:31
  • @ImnotaGeek if you set up your `Entities` like i have showed you, all you need is `UserProfile = userProfile` like i did in `Student Create Action` That inserts `UserId Foreign key` into `Teacher Object` – Komengem Apr 03 '13 at 19:37
  • Excuse me ! I m not that good in MVC (Still exploring it ) ! I would like to know if u create another "StudentViewModel" or did generate it automaticly [ public ActionResult Create(StudentViewModel model)] ; 'cause the only corrected statement that I got here is [ public ActionResult Create(Student model)] – ImnotaGeek Apr 03 '13 at 19:57
  • you can use `public ActionResult Create(Student model)` too, but its bad practice to have your `Views` talk directly to you Entities. For now its fine, once you learn a few things, `ViewModels` are a way to go. Also, don't rely on Code generation, make sure that you study `Entity Framework Code First` It will make you understand things better as you will write most of the code. – Komengem Apr 03 '13 at 20:06
  • That's true , The only problem that I have to deal with seems to be related to EF/my DB . "Unable to determine composite primary key ordering for type 'Models.Tasks'. Use the ColumnAttribute or the HasKey method to specify an order for composite primary keys." ; I am adding annotation Column (Order = X) . Is it the right strategy ! – ImnotaGeek Apr 03 '13 at 20:26
  • I fixed the EF problems , After following your answer I realized that the user has not been stored yet . Check out this snypite shot . http://i.stack.imgur.com/7Xnbf.jpg – ImnotaGeek Apr 03 '13 at 20:49
  • (Now I am able to create the profile, but this statement return null ) [ var userProfile = db.UserProfiles.Local.SingleOrDefault(u => u.UserName == User.Identity.Name) ?? db.UserProfiles.SingleOrDefault(u => u.UserName == User.Identity.Name); ] Check out this snypite shot . http://i.stack.imgur.com/7Xnbf.jpg – ImnotaGeek Apr 03 '13 at 21:01
  • The User need to be logged in since we are using `User.Identity.Name` – Komengem Apr 03 '13 at 21:04
  • So , What do you suggest to fix the problem ! – ImnotaGeek Apr 03 '13 at 21:35
  • I don't think that this is the problem , I have changed "userProfile.AccountType.ToLower() " to "if (model.AccountType == ".. )" . So once we logged in , we go to "StudentController"/"TeacherController" but the "userProfile" still null ('cause we need to check it again to inject our "student" object ) . am I wrong with this analysis ! – ImnotaGeek Apr 03 '13 at 21:40
  • If the User is physically typing `AccountType`, its recommended that you use `userProfile.AccountType.ToLower()`. If you do as i showed you, this code should work. The reason why i used 'userProfile not model' is because we have to get data from logged in user. – Komengem Apr 03 '13 at 22:11
  • That's right , Can you please add in your answer a comment mentioning that it would be working "if (model.AccountType == "Student"){..}" , So people who will look for a solution in the future will know that ! (Just add it as a comment on the code ) .the application is working out . Thank you for your time and efforts , I do appreciate all that . – ImnotaGeek Apr 03 '13 at 23:28