Customizing SimpleMembership

2019-02-15 01:17发布

问题:

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?

回答1:

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}}
     );

 }


回答2:

HenningJ, I'm searching for an answer to the same thing. Unfortunately (I'm sure you're aware) the problem with the way you did this is that there is no SQL transaction. Either your SaveChanges() or CreateUserAndAccount() could fail leaving your user database in an invalid state. You need to rollback if one fails.

I'm still searching for the final answer, but I'll post whatever solution I end up going with. Hoping to avoid having to write a custom membership provider (AGAIN!), but starting to think it would be faster.