Using SimpleMembership with EF model-first

entity-framework simplemembership

Question

Can SimpleMembership be used with EF model-first? When I try it, I get "Unable to find the requested .NET Framework Data Provider" when I call WebSecurity.InitializeDatabaseConnection.

To put it another way: I can't get the call to WebSecurity.InitializeDatabaseConnection to work when the connection string employs the System.Data.EntityClient provider (as it does when using the model-first paradigm).

To repro the issue, create an MVC 4 app, and replace the code-first UserProfile entity class (which you get for free with the MVC 4 template) with a model-first User class that you have created in the Entity Designer:

  1. Create an MVC 4 app in VS 2012 and add a new, blank Entity Data Model.
  2. Add a new Entity named User to the model, with fields for Id, UserName, and FullName. So, at this point, the User data entity is mapped to a Users table and is accessed via a funky connection string that employs the System.Data.EntityClient provider.
  3. Verify that the EF can access the User entity. One easy way to do that is to scaffold out a Users controller based on the User table and its associated DbContext.
  4. Edit the AccountModels.cs file to remove the UserProfile class and its associated UsersContext class. Replace the references to the (now missing) UserProfile and UsersContext classes with references to your new User class and its associated DbContext class.
  5. Move the call to InitializeDatabaseConnection from the InitializeSimpleMembershipAttribute filter class to the Application_Start method in Global.asax.cs. While you're at it, modify the arguments to use your new User entity's connection string, table name, and UserId column name.
  6. Delete the (no longer used) InitializeSimpleMembershipAttribute class and the references to it.

When you run the repro, it will get an Exception at the call to InitializeDatabaseConnection.

Bob

1
24
9/30/2012 4:34:25 AM

Accepted Answer

SimpleMembership can work with model first. Here is the solution.

1.InitializeSimpleMembershipAttribute.cs from MVC 4 Internet Application templete should look like this

namespace WebAndAPILayer.Filters
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
    {
        private static SimpleMembershipInitializer _initializer;
        private static object _initializerLock = new object();
        private static bool _isInitialized;

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            // Ensure ASP.NET Simple Membership is initialized only once per app start
            LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
        }

        private class SimpleMembershipInitializer
        {
            public SimpleMembershipInitializer()
            {
                try
                {
                    WebSecurity.InitializeDatabaseConnection("ConnStringForWebSecurity", "UserProfile", "Id", "UserName", autoCreateTables: true);
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("Something is wrong", ex);
                }
            }
        }
    }
}

2.Delete CodeFirst Classes from AcountModel.cs

3.Fix AccountCotroler.cs to work with your Model-first DbContext (ExternalLoginConfirmation(RegisterExternalLoginModel model, string returnUrl) method)

4.Define your "ConnStringForWebSecurity" connection string which is not same as that funky conn string for model-first db access, notice that we use provider System.Data.SqlClient not System.Data.EntityClient

 <connectionStrings>
         <add name="ModelFirstEntityFramework" connectionString="metadata=res://*/Context.csdl|res://*/Context.ssdl|res://*/Context.msl;provider=System.Data.SqlClient;provider
 connection string=&quot;data source=.\SQLEXPRESS;Initial
 Catalog=aspnet-MVC4;Integrated
 Security=SSPI;multipleactiveresultsets=True;App=EntityFramework&quot;"
 providerName="System.Data.EntityClient" />
         <add name="ConnStringForWebSecurity" connectionString="data source=.\SQLEXPRESS;Initial Catalog=aspnet-MVC4;Integrated
 Security=SSPI" providerName="System.Data.SqlClient" />
       </connectionStrings>
24
10/23/2012 7:33:55 PM

Popular Answer

That's a bug in MVC 4. There's a workaround in this blog post.

As an action filter, InitializeSimpleMembershipAttribute 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.

My recommendation is to remove ISMA from the project, and initialize WebSecurity during the application start event.



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