Search code examples
c#asp.net-mvc-4entity-framework-5simplemembership

Customizing SimpleMembership


After reading this good BLOG about adding custom data to UserProfile table, I wanted to change it the way that UserProfile table should store default data + another class where all additional info is stored.

After creating new project using Interenet application template, I have created two classes:

Student.cs

[Table("Student")]
public class Student
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public virtual int StudentId { get; set; }
    public virtual string Name { get; set; }
    public virtual string Surname { get; set; }
    public virtual int UserId { get; set; }
}

UserProfile.cs

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

also, I've deleted the UserProfile definition from AccountModel.cs. My DB context class looks like this:

MvcLoginDb.cs

public class MvcLoginDb : DbContext
{
    public MvcLoginDb()
        : base("DefaultConnection")
    {
    }

    public DbSet<UserProfile> UserProfiles { get; set; }
    public DbSet<Student> Students { get; set; }
}

again, I have deleted db context definition from AccountModel.cs.

Inside Package-Manager-Console I've written:

Enable-Migrations

and my Configuration.cs looks like this:

internal sealed class Configuration : DbMigrationsConfiguration<MvcLogin.Models.MvcLoginDb>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
    }

    protected override void Seed(MvcLogin.Models.MvcLoginDb context)
    {
        WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);

        if (!WebSecurity.UserExists("banana"))
            WebSecurity.CreateUserAndAccount(
                "banana",
                "password",
                new
                {
                   Student = new Student { Name = "Asdf", Surname = "Ggjk" }
                });

    }
}

That was the idea of adding student data as creating a new class, but this approach is not working because after running Update-Database -Verbose I'm getting the error: No mapping exists from object type MvcLogin.Models.Student to a known managed provider native type.

Can anyone expain why I'm getting this error, shoud I use a different approach for storing additional data in different table?


Solution

  • I struggled a lot with the same issue. The key is to get the relation between the UserProfile class and your Student class correct. It have to be a one-to-one relationship in order to work correct.

    Here is my solution:

    Person.cs

    public class Person
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }
        /* and more fields here */
    
        public virtual UserProfile UserProfile { get; set; }
    }
    

    UserProfile.cs

    public class UserProfile
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int UserId { get; set; }
        public string UserName { get; set; }
    
        public virtual Person Person { get; set; }
    }
    

    MyDbContext.cs

    public class MyDbContext : DbContext
    {
        public DbSet<Person> Person { get; set; }
        public DbSet<UserProfile> UserProfile { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
         modelBuilder.Entity<UserProfile>()
                        .HasRequired(u => u.Person)
                        .WithOptional(p => p.UserProfile)
                        .Map(m => m.MapKey("PersonId"));
        }
    }
    

    However, I also struggled with creating users with the WebSecurity.CreateUserAndAccount method. So I ended up creating my Person objects with Entity Framework and then create the user account with the membership provider method:

    AccountRepository.cs

     public Person CreatePerson(string name, string username) {
    
         Person person = new Person { Name = name };
         _dbContext.Person.add(person);
         _dbContext.SaveChanges();
    
         var membership = (SimpleMembershipProvider)Membership.Provider;
         membership.CreateUserAndAccount(
             model.UserName, 
             createRandomPassword(), 
             new Dictionary<string, object>{ {"PersonId" , person.Id}}
         );
    
     }