ASP.NET MVC – How to Model User Accounts with Entity Framework Code-First

asp.net-mvccodefirstdesignentity-framework

How do you design your code-first approach in entity framework when your code should include user data (name, password…) without repeating/overwriting what the MVC framework will generate in terms of user tables, classes, authorization and authentication

As simple example, if i have an application that will manage user projects, my models will contain the following classes, and have the following relationship:

- One user may be assigned one or MANY projects
- One project may be assigned to one or MANY users

( just to show the many to many ERD relationship nature)

class User{
    //implementation of the class members and 
    // navigation properties to alow Entity Framework to create the DB tables
}

class Project{
    //implementation of the class members and 
    // navigation properties to alow Entity Framework to create the DB tables
}

Now the problem i am facing is that EF and ASP .net MVC framework takes care of creating user tables, profiles and roles entities…

I am not sure whether it is possible to design my classes in a way to show an automatic (conventional) relationship with the rest of the tables created by MVC.

In the case this needs to be done by writting custom Authentication classes, can you please explain how? because i have found many articles that discuss the same issue but without pointing out the relationship between the project classes and the MVC created classes / tables, plus if the developer choses to use the MVC way he will end up with two data contexts, even after merging both tables in one sql server catalog.

Best Answer

If I'm reading your question correctly, then what you're looking for is SimpleMembership.

Here's an explanation of SimpleMembership; from the section Easy to with[sic] Entity Framework Code First:

SimpleMembership ... allows[changed] you to use any table as a user store. That means you're in control of the user profile information, and you can access it however you'd like ....

So you can define your own User class to hold extra user data and relationships, e.g.:

[Table("UserProfile")]
public class UserProfile
{
    // required fields:
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int      UserId { get; set; }
    public string   UserName { get; set; }

    // your extra fields:
    public List<Project> Projects { get; set; }
    // more here...
}

and the regular, built-in Membership stuff is used for handling logins, passwords, and everything else behind the scenes.

There's a little more to be done to integrate it, but it wasn't too bad when I used it.

Note that this works from MVC4 on.


Edit to address questions asked in the comments:

From what I can tell looking at my code (it's been a while since I worked on the project and, between it being my first MVC project and other projects scrambling my brain since then, things are a bit fuzzy):

It looks like I ended up creating my own context that inherited from the DbContext class (as outlined in this tutorial) and contained all my application data; e.g.:

public class MyContext : DbContext
{
    public MyContext()
        : base("DefaultConnection")
    {

    }

    public DbSet<UserProfile> UserProfiles { get; set; }
    public DbSet<Projects> Projects { get; set; }
    // more here
}

(IIRC, by inheriting from the DbContext type we get all the built-in membership stuff.)

Then the rest of the app had to be configured to use this context (as given in the tutorial). (All of my controllers create an instance of my derived context, but this could be set up using dependency injection as well.)

It looks like the database connection is initialized in the InitializeSimpleMembershipAttribute by default -- this attribute gets applied to all the controllers.

I ran into the issue stated in this question with this setup, so to fix it I followed the advice in the accepted answer and moved the database connection initialization code to a central point (Global.asax.cs) that ran only once when the web app started.

I'm afraid that I can't remember many more details than this, so you'll probably have to do a bit more research (and probably troubleshooting) to get this working, but it should give you a decent idea of what to look for.

I ended up reading a lot of tutorials on asp.net, so you might check that site out if you've not already.