I am developing a code first web app in Visual Studio 2012 Express.
I use this connection string in the web.config:
To make sure that the WebSecurity.InitializeDatabaseConnection is not called twice just use the WebSecurity.Initialized to check if it was already called. This blog post provides detailed instructions on seeding and customizing SimpleMembership. There is a series in this blog on using SimpleMembership and I would also recommend looking at decoupling SimpleMembership from your ASP.NET MVC Application. You can get the complete source code for these examples here.
Add this code to Global.asax.cs
. This will makes sure that your database is always Initialized before any other executions. Also make sure its the first registration in Application_Start()
if (!WebSecurity.Initialized)
WebSecurity.InitializeDatabaseConnection("DefaultConnection",
"UserProfile", "UserId", "UserName", autoCreateTables: true);
Get rid of Filters/InitializeSimpleMembershipAttribute.cs
or just comment the code inside, in case you would like to go back to it.
Remove [InitializeSimpleMembership]
at the top of AccountController.cs
Also if you haven't already enabled migrations, i would encourage you do so. That way, you can do your seeds in Configuration.cs
created inside the Migration folder
when you run Enable-Migrations
Do you also have the InitializeSimpleMembership attribute on your AccountController class? (this is the default). If so, you are initializing twice.
is that InitializeDatabaseConnection
calls WebSecurity.InitializeProviders
internally, and this method is not thread-safe, then combine this with the fact that WebSecurity typically needs initializing from various places. This has implications as web applications are inherently multi-threaded environments ... and WebSecurity.Initialized
and WebSecurity.InitializeDatabaseConnection
are not thread-safe when used together - they create a typical race condition.
Seeding (for migrations) means that your WebSecurity
can be initialized more than once as you may also need to initialise it in Global.asax.cs for deployments with seeding turned off, and your InitializeSimpleMembershipAttribute
can potentially be called multiple times simultaneusly by http requests in live deployments etc.
Putting the initialisation code in multiple places also breaks your DRYness
Make sure your init calls are thread-safe, and only occur once per instance of an AppDomain. Use a thread-safe singleton class to do this; and reduce duplication of code.
Call the singleton's EnsureInitialize
method from any/all of the following, as appropriate for your application:
Application_Start
method before anything else)Seed
method (before creating the users)SimpleMembershipInitializer
constructor after the context is initialised)Here is a simple example singleton:
// Call this with WebSecurityInitializer.Instance.EnsureInitialize()
public class WebSecurityInitializer {
private WebSecurityInitializer() { }
public static readonly WebSecurityInitializer Instance = new WebSecurityInitializer();
private bool isNotInit = true;
private readonly object SyncRoot = new object();
public void EnsureInitialize() {
if (isNotInit) {
lock (this.SyncRoot) {
if (isNotInit) {
isNotInit = false;
WebSecurity.InitializeDatabaseConnection("MyContextName",
userTableName: "UserProfile", userIdColumn: "UserId", userNameColumn: "UserName",
autoCreateTables: true);
}
}
}
}
}
Once I had done this, my case of the error you mention disappeared, not to be seen again.
Footnotes
The singleton class also keeps your code DRY, which is especially useful during early stage app development if you need to later change the configuration of your WebSecurity.InitializeDatabaseConnection
as it will only be in one place (end edit)
I also keep the SimpleMembershipInitializer
clean, and instead seed my users along with the common seeding in Migrations\Configuration.cs Seed
method. This helps with testability of seeding through my migrations by keeping everything in one place. I use unit testing to make sure we can always go up and down the migrations tree, so this makes it easier to do that.
However the location of your seeding code won't matter, it is more just making sure that, globally, you have initialised WebSecurity only once within your AppDomain, and that any call to InitializeDatabaseConnection
is thread-safe.
If it is already initialized then make sure your first call:
if (!WebSecurity.Initialized)
{
// Do the initialization first.
}
// The rest of the code
This way you'll be sure that you don't repeat the initialization process.