Working with SimpleMembership outside of ASP.NET
I’m using SimpleMembership, from WebMatrix’s distribution(WebMatrix.WebData), with an ASP.NET MVC 3 application. You can find the NuGet Package for SimpleMembership.Mvc3 here, and installing it is just a matter of running “Install-Package SimpleMembership.Mvc3” from the Package Manager Console in Visual Studio. Unlike the built-in Membership and Role providers for ASP.NET, SimpleMembership doesn’t require huge swaths of XML to be added to your web.config file, or much in the way of database initialization using tools like aspnet_regsql.exe. Instead, it does pretty much all of its work in code, in a static class SimpleMembershipMvc3 that gets placed in your App_Start folder. The class is run using WebActivator (another NuGet Package) and you simply provide it a few bits of information in its Initialize() method such as the name of the ConnectionString it should use to talk to the database, and what the name of your users table and some of its columns are. By default, it will automatically create the tables it needs, though of course this is easily disabled.
In the SimpleMemberMvc3 class’s Start() method, you’ll find code that looks like this:
public static void Start()
{
if (SimpleMembershipEnabled)
{
MembershipProvider provider = Membership.Providers["AspNetSqlMembershipProvider"];
if (provider != null)
{
MembershipProvider currentDefault = provider;
SimpleMembershipProvider provider2 =
CreateDefaultSimpleMembershipProvider("AspNetSqlMembershipProvider", currentDefault);
Membership.Providers.Remove("AspNetSqlMembershipProvider");
Membership.Providers.Add(provider2);
}
Roles.Enabled = true;
RoleProvider provider3 = Roles.Providers["AspNetSqlRoleProvider"];
if (provider3 != null)
{
RoleProvider provider6 = provider3;
SimpleRoleProvider provider4 =
CreateDefaultSimpleRoleProvider("AspNetSqlRoleProvider", provider6);
Roles.Providers.Remove("AspNetSqlRoleProvider");
Roles.Providers.Add(provider4);
}
}
}
Unfortunately, if you try and work with this code in an integration test using, for instance, NUnit and some code like this:
[TestFixture]
public class MembershipProviderShould
{
[SetUp]
public void SetUp()
{
SimpleMembershipMvc3.Start();
SimpleMembershipMvc3.Initialize();
}
// tests go here
}
You’ll get an exception when SimpleMembership makes a call to Membership.Providers.Remove(“AspNetSqlMembershipProvider”) claiming that the collection is read-only. You’ll encounter a similar error when you make a call to Roles.Providers.Remove(“AspNetSqlRoleProvider”). And finally, the call to Roles.Enabled will blow up as well, claiming that it cannot be called outside of App_Start or something like that.
The solution, ugly though it is, to these issues that I’ve found is to simply use some reflection to circumvent the “must only run inside a web application” goo that is embedded within these Membership providers. Specifically, there is a private read-only field that must be set to false in the ProviderCollections that hold the Membership and Role providers, and the call to Roles.Enabled can be replaced by directly setting the private static flag that this setter ultimately refers to. The updated code for Start() looks like this:
public static void Start()
{
if (SimpleMembershipEnabled)
{
MembershipProvider provider = Membership.Providers["AspNetSqlMembershipProvider"];
if (provider != null)
{
MembershipProvider currentDefault = provider;
SimpleMembershipProvider provider2 =
CreateDefaultSimpleMembershipProvider("AspNetSqlMembershipProvider", currentDefault);
// turn off read-only on Membership Providers collection
FieldInfo field = typeof(ProviderCollection).GetField("_ReadOnly",
BindingFlags.Instance | BindingFlags.NonPublic);
field.SetValue(Membership.Providers, false);
Membership.Providers.Remove("AspNetSqlMembershipProvider");
Membership.Providers.Add(provider2);
}
//Roles.Enabled = true; does not work outside of a web application - use reflection instead
FieldInfo enabledField = typeof (Roles).GetField("s_Enabled",
BindingFlags.Static | BindingFlags.NonPublic);
enabledField.SetValue(null, true);
RoleProvider provider3 = Roles.Providers["AspNetSqlRoleProvider"];
if (provider3 != null)
{
RoleProvider provider6 = provider3;
SimpleRoleProvider provider4 =
CreateDefaultSimpleRoleProvider("AspNetSqlRoleProvider", provider6);
// turn off read-only on Role Providers collection
FieldInfo field = typeof(ProviderCollection).GetField("_ReadOnly",
BindingFlags.Instance | BindingFlags.NonPublic);
field.SetValue(Roles.Providers, false);
Roles.Providers.Remove("AspNetSqlRoleProvider");
Roles.Providers.Add(provider4);
}
}
}