However, there are a few issues to be aware of if you build an application on top of the existing code in the AccountController.
The InitializeSimpleMembershipAttribute (let’s call it ISMA) is part of the code generated by the MVC 4 Internet template. ISMA exists to provide a lazy initialization of the underlying providers, as well as potentially create the database for storing membership, roles, and OAuth logins. You’ll find it decorating the AccountController as an action filter, so it can jump in and initialize all the bits before the controller starts to interact with the security related classes.
[Authorize]
[InitializeSimpleMembership]
public class AccountController : Controller
{
// ...
}
As an action filter, ISMA hooks into OnActionExecuting to perform the lazy initialization work, but this can be too late in the life cycle. The Authorize attribute will need the providers to be ready earlier if it needs to perform role based access checks (during OnAuthorization). In other words, if the first request to a site hits a controller action like the following:
[Authorize(Roles="Sales")]
.. then you’ll have an exception as the filter checks the user’s role but the providers aren’t initialized. We’ll talk about concerns over the exception message details later.
My recommendation is to remove ISMA from the project, and initialize WebSecurity during the application start event.
The MVC 4 Internet project template uses EF code-first classes to manage user profiles, so it includes a DbContext derived class (UsersContext) and a UserProfile entity, both of which are defined in the Models\AccountModel.cs file.
If you plan on customizing the UserProfile class, make sure you let the application create the database for you. You can see the database creation code in the ISMA also.
If you create a database yourself, make sure to include all the tables needed for the WebSecurity class. There are not scripts published that I have found, but you can always generate DDL scripts after the application runs.
If you create an empty database and run the application to let WebSecurity create its own tables, it will not include any of your user profile customizations (it will only do that for you if the database doesn’t exist).
There is a strange tension in a new application because it’s not clear who is responsible for getting all the pieces to work together. WebSecurity can create the database schema it needs to operate, but can miss creating something you add to the UserProfile entity, which is code you can customize, but you wouldn’t know that at a first glance.
If you are using the Entity Framework for an application, my recommendation is to remove the DbContext derived class from AccountModel.cs and take ownership of the UserProfile class. You’ll need to fix places in the AccountController that are looking to use the context class, but replace those pieces of code with your own context class.
If you are not using the Entity Framework, you’ll want to remove the DbContext derived class in any case.
Because the Internet project template includes an Entity Framework DbContext class in the project, if you add your own DbContext to the project and try to enable EF migrations, you’ll be greeted with an error.
PM> enable-migrations
More than one context type was found in the assembly.
To enable migrations for [Context], use Enable-Migrations –ContextTypeName [Context].
The problem is easy to solve. As the error states, you can use the –ContextTypeName flag to specify your context class name. Note that you can only have migrations for one context in a project, so if you want to have migrations for both contexts you’ll need to move one to a different project. Again, my recommendation is to just remove the existing UsersContext the Internet project template creates, and take ownership of the user profile in your own context.
The previous ASP.NET membership providers were easy to work with from the Seed method in an EF migrations class. With this new approach you need to perform some additional configuration steps. I covered these steps in a previous post: “Seeding Membership and Roles in ASP.NET MVC 4”.
It’s possible to run into an exception message like the following (like when the ISMA code runs too late):
You must call the "WebSecurity.InitializeDatabaseConnection" method before you call any other method of the "WebSecurity" class. This call should be placed in an _AppStart.cshtml file in the root of your site.
We don’t use an _AppStart.cshtml file in ASP.NET MVC, so look to add code to Application_Start in global.asax.cs instead.
A few people have written me to ask if the AccountController created by the Internet application template is the future of ASP.NET MVC coding. A representative sample appears below:
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Specifically, people are asking about the number of static method calls through WebSecurity and trying to figure out the impact on testing and extensibility, as well as the impact on impressionable young minds who might read too much into the code.
I’ve assured everyone the future is not full of static methods. Or, at least not my future.
In some upcoming posts we’ll explore an alternative approach to membership, roles, and OAuth in ASP.NET MVC 4, and see if there is an approach that is simple, testable, and extensible enough to work with more than a relational database for storage.