How does a new ASP.NET MVC 5 application know how to create a database and how does the Account Controller access the database?

asp.net asp.net-identity asp.net-mvc asp.net-mvc-5 entity-framework

Question

I created an ASP.NET MVC 5 Application using Visual Studio 2013 Update 2. In the application, I have an Account controller. It's different from what I am used to and does not contain an instantiation of dbcontext.

public class AccountController : Controller
{
    private ApplicationUserManager _userManager;

    public AccountController()
    {
    }

    public AccountController(ApplicationUserManager userManager)
    {
        UserManager = userManager;
    }

    public ApplicationUserManager UserManager {
        get
        {
            return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
        }
        private set
        {
            _userManager = value;
        }
    }

My web.config that is created by default has a connection string like this:

  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-WebApplication3-20140417072624.mdf;Initial Catalog=aspnet-WebApplication3-20140417072624;Integrated Security=True"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

Can someone explain to me how the application knows to create a database for this application when it starts for the first time?

Also, on subsequent starts, does it use Entity Framework to access the Identity tables to do the authentication?

1
34
5/17/2015 7:43:22 PM

Accepted Answer

1) WHAT'S GOING ON HERE:

When you create a new MVC 5 application and choose "Individual User Accounts", a new ASP.NET Identity Provider is included which uses Entity Framework 6 Code-First.

Microsoft has adopted EF-Code-First to make Identity as customizable as possible.

When Identity is accessed for the first time, Entity Framework checks to see if the database exists. Unless configured otherwise, it uses the "DefaultConnection" to find the identity database. If the database does not exist when Identity is called, EF automatically created the database.

Notice your connection string contains

`AttachDbFilename=|DataDirectory|\aspnet-WebApplication3-20140417072624.mdf`

If you open your App_Data Folder, you should have a aspnet-WebApplication3-20140417072624.mdf file.

If you double click on this .mdf file, the VS2013 Server Explorer will open your DB. If you have already attempted to access any Identity functionality, you will these tables created:

  • _MigrationHistory
  • ASPNetRoles
  • ASPNetUserClaims
  • ASPNetUserLogins
  • ASPNetUsers

By default, your app is configured to use SQL Server Compact (MDF file) so you don't have to have an actual SQL Server Instance running. All of this is customizable. The name of your MDF file, the schema of Identity Database, the choice of SQL Compact vs an actual SQL Server instance. Change your Connection String, or create a new one and pass this new connection to your context.


2) WHERE IS MY CONTEXT?

All this is well and good, but an important question you asked is basically "Where is my context?", and the just as relevant implied questions regarding how you can further customize your DB or alter validation logic.

You will notice that your project references Microsoft.AspNet.Identity.EntityFramework. This assembly is an implementation of IdentityDBContext<TUser> and implentation of UserManager Class.

Open your AccountController, and notice the constructor has UserManager object passed which in turn has a new UserStore object passed, which gets passed a ApplicationDbContext.

    public AccountController()
        : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))

The ApplicationDbContext is defined in your Models Folder. Inside that folder, you will find an IdentityModels.cs file. Open it and you will see

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }
}

This is where your Identity Context is assigned. you can change the connection name passed to the ApplicationDbContext constructor, or define and use a different context in your account controller.


3) HOW DO I CUSTOMIZE MY IDENTITY SCHEMA?

Another class defined IN IdentityModels.cs file is the ApplicationUser class which inherits from IdentityUser class.

public class ApplicationUser : IdentityUser
{
}

Any properties you add to this class will be persisted in your ASPNetUsers Table. The rest of the schema is defined in IdentityDbContext class. So, while you can add more tables (e.g. Privileges) to your Identity Schema by adding a DBSet to the Context Definition,

public DBSet<Privileges> { get; set; }

Altering other tables (Roles, Claims, etc) is also possible, but far more involved. For example, to customize the Roles table, you would have to implement a NewIdentityRole inheriting from IdentityRole and add it's relationship by overriding the OnModelCreating() method for your Context.

This article on Customizing Roles Tables does a good job of describing the steps involved. Even here, you will find that there is significant trouble invested into simply ADDING new columns. Removing tables or columns from the original schema created in the IdentityDbContext class is probably as much trouble as creating your own implementation of IdentityDbContext class.

78
4/19/2014 10:37:58 PM

Popular Answer

As Melina pointed out, the original question referenced the current ASP.NET Identity 2.x model.

Dave Alperovich's answer provided valuable background information on the concepts behind ASP.NET Identity, although the examples were drawn from ASP.NET Identity 1.x, which was replaced in 2014.

Callum Linington provides the "teach a man to fish" answer. By following his advice, it's easy to see that the 2.x "ApplicationUserManager" class is derived from a 1.x-style "UserManager".

The answer is basically that the "ApplicationUserManager", which is injected as a parameter when "AccountController" is created, connects to the identity data store in its own constructor:

var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));

Note how this "hidden" 2.x code is very similar to the 1.x code as given above:

public AccountController()
    : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow