30

My web site was initially written in MVC 4.0 RC using VS2010, . I have just downloaded and installed VS2012, and upgraded my project to Dotnet Framework 4.5.

In my project I'm using a Custom MemberShipProvider and a custom RoleProvider. On VS2010 it worked like a charm. But now I'm keeping getting a strange Configuration Error:

"This method cannot be called during the application's pre-start initialization phase."

The "system.web -> membership -> providers -> add" line in my web.config is marked red as the source of the problem.

I eliminated the suspicion that the problem has something to do with the migration process, by creating a new MVC 4.0 Project (in VS2012), adding my custom Membership/Role providers, changing the web.config appropriately, and finding that the error reappears!

Digging deeper into the problem - I found the following information in the Application Log:

Exception information: Exception type: InvalidOperationException Exception message: The pre-application start initialization method Start on type WebMatrix.WebData.PreApplicationStartCode threw an exception with the following error message: This method cannot be called during the application's pre-start initialization phase. (C:\Users\dov.AD\Documents\Visual Studio 2012\Projects\MvcApplication2\MvcApplication2\web.config line 52).
at System.Web.Compilation.BuildManager.InvokePreStartInitMethodsCore(ICollection1 methods, Func1 setHostingEnvironmentCultures) at System.Web.Compilation.BuildManager.InvokePreStartInitMethods(ICollection`1 methods) at System.Web.Compilation.BuildManager.CallPreStartInitMethods(String preStartInitListPath) at System.Web.Compilation.BuildManager.ExecutePreAppStart() at System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel, Exception appDomainCreationException)

This method cannot be called during the application's pre-start initialization phase. (C:\Users\dov.AD\Documents\Visual Studio 2012\Projects\MvcApplication2\MvcApplication2\web.config line 52)
at System.Web.Configuration.ConfigUtil.GetType(String typeName, String propertyName, ConfigurationElement configElement, XmlNode node, Boolean checkAptcaBit, Boolean ignoreCase) at System.Web.Configuration.ConfigUtil.GetType(String typeName, String propertyName, ConfigurationElement configElement, Boolean checkAptcaBit, Boolean ignoreCase) at System.Web.Configuration.ProvidersHelper.InstantiateProvider(ProviderSettings providerSettings, Type providerType) at System.Web.Configuration.ProvidersHelper.InstantiateProviders(ProviderSettingsCollection configProviders, ProviderCollection providers, Type providerType)
at System.Web.Security.Membership.InitializeSettings(Boolean initializeGeneralSettings, RuntimeConfig appConfig, MembershipSection settings) at System.Web.Security.Membership.Initialize() at System.Web.Security.Membership.get_Providers() at WebMatrix.WebData.WebSecurity.PreAppStartInit() at WebMatrix.WebData.PreApplicationStartCode.Start()

This method cannot be called during the application's pre-start initialization phase. at System.Web.Compilation.BuildManager.EnsureTopLevelFilesCompiled()
at System.Web.Compilation.BuildManager.GetType(String typeName, Boolean throwOnError, Boolean ignoreCase) at System.Web.Configuration.ConfigUtil.GetType(String typeName, String propertyName, ConfigurationElement configElement, XmlNode node, Boolean checkAptcaBit, Boolean ignoreCase)

Request information: 
Request URL: http://localhost:4995/ 
Request path: / 
User host address: ::1 
User:  
Is authenticated: False 
Authentication Type:  
Thread account name: AD\dov    Thread information: 
Thread ID: 5 
Thread account name: AD\dov 
Is impersonating: False 
Stack trace:    at >System.Web.Compilation.BuildManager.InvokePreStartInitMethodsCore(ICollection`1

methods, Func1 setHostingEnvironmentCultures) at System.Web.Compilation.BuildManager.InvokePreStartInitMethods(ICollection1 methods) at System.Web.Compilation.BuildManager.CallPreStartInitMethods(String preStartInitListPath) at System.Web.Compilation.BuildManager.ExecutePreAppStart() at System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel, Exception appDomainCreationException)

Please help,

Thank you!

Here is the web.config:

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MyWebSite-20120820105950;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-MyWebSite-20120820105950.mdf" providerName="System.Data.SqlClient" />
    <add name="MyWebSiteDbContext" providerName="System.Data.SqlClient" connectionString="server=.;database=MyWebSiteDB;Integrated Security=True;" />
  </connectionStrings>
  <appSettings>
    <add key="webpages:Version" value="2.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" timeout="2880" />
    </authentication>
    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Optimization" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages" />
      </namespaces>
    </pages>
    <profile>
      <providers>
        <clear/>
      </providers>
    </profile>
    <roleManager defaultProvider="MyWebSiteRoleProvider" enabled="true">
      <providers>
        <clear/>     
        <add name="MyWebSiteRoleProvider" type="MyWebSite.Security.MyWebSiteRoleProvider"/>
      </providers>
    </roleManager>
    <membership defaultProvider="MyWebSiteMembershipProvider">
      <providers>
        <clear />
        <add name="MyWebSiteMembershipProvider" type="MyWebSite.Security.MyWebSiteMembershipProvider"  />
      </providers>
    </membership>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
  </system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
  </entityFramework>
</configuration>

This is the relevant custom membership (I have simplified it, but even though the problem is still there) code, only ValidateUser is really overridden:

using System;
using System.Linq;
using System.Web.Security;
using DAL.MyWebSite;

namespace MyWebSite.Security
{
    public class MyWebSiteMembershipProvider : MembershipProvider
    {




        /// <summary>
        /// Verifies that the specified user name and password exist in the data source.
        /// </summary>
        /// <returns>
        /// true if the specified username and password are valid; otherwise, false.
        /// </returns>
        /// <param name="username">The name of the user to validate. </param><param name="password">The password for the specified user. </param>
        public override bool ValidateUser(string username, string password)
        {
            // simplified
            return true;
        }



    }
}

This is the (simplified) RoleProvider:

using System;
using System.Linq;
using System.Web.Security;
using DAL.MyWebSite;

namespace MyWebSite.Security
{
    public class MyWebSiteRoleProvider : RoleProvider
    {


        //readonly MyWebSiteDbContext _context = new MyWebSiteDbContext();
        /// <summary>
        /// Gets a value indicating whether the specified user is in the specified role for the configured applicationName.
        /// </summary>
        /// <returns>
        /// true if the specified user is in the specified role for the configured applicationName; otherwise, false.
        /// </returns>
        /// <param name="username">The user name to search for.</param><param name="roleName">The role to search in.</param>
        public override bool IsUserInRole(string username, string roleName)
        {
            return true;
            //return GetRolesForUser(username).Contains(roleName);
        }

        /// <summary>
        /// Gets a list of the roles that a specified user is in for the configured applicationName.
        /// </summary>
        /// <returns>
        /// A string array containing the names of all the roles that the specified user is in for the configured applicationName.
        /// </returns>
        /// <param name="username">The user to return a list of roles for.</param>
        public override string[] GetRolesForUser(string username)
        {
            return new string[] {"one", "two"};

            //var sm = _context.SalesManagers.Include("PermissionLevel").FirstOrDefault(manager => manager.UserName == username);

            //if (sm != null)
            //{
            //    if (sm.PermissionLevel.Name == "Sales Manager")
            //    {
            //        return new[] { "SalesManagers" };
            //    }

            //    if (sm.PermissionLevel.Name == "Administrator")
            //    {
            //        return new[] { "SalesManagers", "Administrators" };
            //    }

            //} 
            //return null;

        }

    }
}
berke762
  • 429
  • 1
  • 4
  • 7
  • for the sake of simplicity and message length limitations, i have pasted here a shortened version of my providers, and assured that the configuration problem is still there. – berke762 Aug 21 '12 at 08:14
  • 1
    I think the configuration code is more important. – Amiram Korach Aug 21 '12 at 08:16

4 Answers4

71

I use my own custom Membership and Role providers in Since MVC2 and ran into this issue when I migrated from MVC3 to 4.

I created a new project in MVC4 / .net4.5 EF5 and had the miss-fortune to encounter this error.

I managed to fix it by doing the following:

Add this to your webconfig appsettings:

  <appSettings>
    <add key="enableSimpleMembership" value="false"/>
    <add key="autoFormsAuthentication" value="false"/>
  </appSettings>

Add your connection string to your memberships and roles providers if not already set:

<membership defaultProvider="MyMembershipProvider">
  <providers>
    <add name="MyMembershipProvider" type="AMS.WebUI.Infrastructure.CustomMembershipProvider" connectionStringName="EFDbContext" />
  </providers>
</membership>
<roleManager defaultProvider="MyRoleprovider">
  <providers>
    <add name="MyRoleprovider" type="AMS.WebUI.Infrastructure.CustomRoleProvider" connectionStringName="EFDbContext" />
  </providers>
</roleManager>

This resolved the issue for me and I hope it can help you.

Ben Pretorius
  • 4,079
  • 4
  • 34
  • 28
  • Ben, Thanks for the answer. In fact I saw the answer in other forums, before I posted my question here. I did not want to try it because it changes settings in my web project, and I wanted it to work just the same it worked in VS2010 SP1. Also, as I wrote in my question, my project was initially written in MVC 4 (RC), so IMHO this answer does not seem to fit the situation i was facing. Please see the Answer I just posted... – berke762 Aug 30 '12 at 11:19
  • Thanks for the feedback. I will try this out as it does seem like a better appeoach than updating the config! – Ben Pretorius Aug 30 '12 at 21:46
  • 1
    i have played with this solution. please see my findings in a separate answer (was too long to paste it here) – Gaspar Nagy Aug 31 '12 at 08:43
  • If you are using MVC4 add the enabled prop to your web config in order for it to use the RolePrincipal. If you do not add this then it will default to GenericPrincipal inside your auth attributes and never hit your custom role provider. – Ben Pretorius Jan 30 '13 at 12:02
24

OK let's see if I can explain this right. ASP.NET 4 introduced a new assembly-level attribute: PreApplicationStartMethodAttribute, that is typically used in the Properties/AssemblyInfo.cs file.

ASP.NET MVC 4 standard template comes with a reference to the WebMatrix.WebData assembly. Looking at its code, the AssemblyInfo does have this:

[assembly: PreApplicationStartMethod(typeof(PreApplicationStartCode), "Start")]

So basically, before App_Start is even invoked, the framework will call WebMatrix.WebData.PreApplicationStartCode.Start() which, among other things, does this

// Initialize membership provider
WebSecurity.PreAppStartInit();

And sure enough, like @Ben Pretorius said, that method starts like this

internal static void PreAppStartInit()
{
    // Allow use of <add key="EnableSimpleMembershipKey" value="false" /> to disable registration of membership/role providers as default.
    if (ConfigUtil.SimpleMembershipEnabled)
    {
        ...

So there you have how/why this "automatically" fails. It's really too bad that Microsoft didn't include <add key="EnableSimpleMembershipKey" value="true" /> in the standard Web.config to make it more obvious that "hey there's this SimpleProvider stuff that's setup here".

Oli
  • 1,031
  • 8
  • 20
  • Thanks for the answer. It sheds some light on this mystery. – berke762 Sep 07 '12 at 07:06
  • 3
    @berke762 it does, doesn't it? Why don't you accept it as the answer then? Both Oli and Ben Pretorius provided answers that work, I just tried it out (had the same issue with custom provider). – mare Oct 11 '12 at 11:15
  • I think this has been changed to use `InitializeSimpleMembershipAttribute` in recent updates to the packages. Also [Jon Galloways article](http://weblogs.asp.net/jgalloway/archive/2012/08/29/simplemembership-membership-providers-universal-providers-and-the-new-asp-net-4-5-web-forms-and-asp-net-mvc-4-templates.aspx) is very informative on the subject. – jl. Nov 27 '12 at 15:18
  • Instead of setting the key I prefer to just get rid of the assemblies that aren't being used. Why have them in your project if you don't use them? You can remove the `WebMatrix` assemblies manually or just remove the `Microsoft.AspNet.WebpPages.WebData` and `Microsoft.AspNet.WebpPages.Data` packages. – Justin Helgerson Jan 31 '14 at 17:21
8

I was also playing around this a bit and looking into the WebMatrix code and finally I think that Ben's solution seems to be the working one.

(I have also tried to play with the assembly references without success. I think with removing the references you just achieved the same as the config settings does - the WebMatrix security was not properly initialized. But I think this is quite a dangerous way. But I'm just guessing here.)

So for me what worked:

<appSettings>
  <add key="enableSimpleMembership" value="false"/>
</appSettings>

<membership defaultProvider="MyMembershipProvider">
  <providers>
    <clear />
    <add name="MyMembershipProvider" type="MyNamespace.MyMembershipProvider" />
  </providers>
</membership>
<!-- this role configuration below uses the SimpleRoleProvider, because I just 
     wanted to replace the membership provider. If you need to replace that one 
     too, just use your own class instead. -->
<roleManager enabled="true" defaultProvider="AspNetSqlRoleProvider">
  <providers>
    <remove name="AspNetSqlRoleProvider" />
    <add name="AspNetSqlRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <!-- note: WebMatrix registers SimpleRoleProvider with name
         'AspNetSqlRoleProvider'. I don't know why but i kept it. -->
  </providers>
</roleManager>

I have checked the WebMatrix code, and it seems that setting 'enableSimpleMembership' to false is quite harmless. WebMatrix only uses it to initialize the membership/role providers (that can be substituted by the config above) and it enables the forms authentication allowing an alternative configuration way (through app settings) - this is also not necessary, if you have the standard forms authentication properly configured (mainly the 'loginUrl' is the only important player here).

I have also tried to check what the 'autoFormsAuthentication' setting does - but I haven't found anything, so just skipped it. Looks fine still.

Gaspar Nagy
  • 4,422
  • 30
  • 42
2

I already solved the problem. I found that when you create MVC 4 project in VS2012, the template adds references to some assemblies, among them: webmatrix and Oauth... I just removed those references, and the problem disappeared. I do not know to answer how this solved the problem, but I saw that the Exception comes from WebMatrix Assembly, so for that reason I tried to remove it. Sorry, but i have no time to invest in investigation....

berke762
  • 429
  • 1
  • 4
  • 7
  • 6
    of course it comes from WebMatrix assembly, that's where the Simple Membership is implemented and it was brought into MVC4, that's why disabling the Simple Membership in web.config works. Just accept one of the answers above. – mare Oct 11 '12 at 11:20
  • adamsın berke çok sağol – MustafaP Nov 04 '13 at 09:13