ASP.NET MVC 4, La “WebSecurity.InitializeDatabaseConnection” méthode ne peut être appelée qu'une seule fois
Je suis en train d'élaborer un code de la première application web dans Visual Studio 2012 Express.
- Je utiliser cette chaîne de connexion dans le web.config:
<add name="myContext" connectionString="Data Source=.;Integrated Security=True;Initial Catalog=cityKingMVC4" providerName="System.Data.SqlClient" />
Je suis en utilisant SimpleMembership.
Je suis en train de semences 1 administrateur de Filtres/InitializeSimpleMembershipAttribute.cs:
...
WebSecurity.InitializeDatabaseConnection("myContext", "Users", "UserId", "Email", autoCreateTables: true);
//A: Create Admin user
if (!WebSecurity.ConfirmAccount("[email protected]"))
{
WebSecurity.CreateUserAndAccount("[email protected]", "password");
}
//B: Create admin role if not exist
if (!Roles.RoleExists("Administrator"))
{
Roles.CreateRole("Administrator");
Roles.AddUserToRole("[email protected]", "Administrator");
}
Si j'ai un commentaire A & B, il ne tombe pas en panne. Si je n'ai pas-je obtenir ceci:
Le "WebSecurity.InitializeDatabaseConnection" méthode ne peut être appelée qu'une seule fois.
Si je débogage et de mettre un point d'arrêt sur " WebSecurity.InitializeDatabaseConnection' - il n'appelle qu'une fois et il n'y a pas d'autre code appelant WebSecurity.InitializeDatabaseConnection n'importe où.
Si je debug - il se bloque sur une ligne plus haut dans le fichier (standard SimpleAuthentication fichier):
LazyInitializer.EnsureInitialized(réf _initializer, ref _isInitialized, ref _initializerLock);
avec cette erreur:
Une Exception a été levée par la cible d'un appel.
Trace De La Pile:
[InvalidOperationException: The "WebSecurity.InitializeDatabaseConnection" method can be called only once.]
WebMatrix.WebData.WebSecurity.InitializeMembershipProvider(SimpleMembershipProvider simpleMembership, DatabaseConnectionInfo connect, String userTableName, String userIdColumn, String userNameColumn, Boolean createTables) +87978
WebMatrix.WebData.WebSecurity.InitializeProviders(DatabaseConnectionInfo connect, String userTableName, String userIdColumn, String userNameColumn, Boolean autoCreateTables) +86
myapPMVC4.Filters.SimpleMembershipInitializer..ctor() in c:\Users\name\Documents\Visual Studio 2012\Projects\myapPMVC4\myapPMVC4\Filters\InitializeSimpleMembershipAttribute.cs:43
[InvalidOperationException: The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588]
myapPMVC4.Filters.SimpleMembershipInitializer..ctor() in c:\Users\name\Documents\Visual Studio 2012\Projects\myapPMVC4\myapPMVC4\Filters\InitializeSimpleMembershipAttribute.cs:88
[TargetInvocationException: Exception has been thrown by the target of an invocation.]
System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) +159
System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) +256
System.Activator.CreateInstance(Type type, Boolean nonPublic) +127
System.Activator.CreateInstance(Type type) +11
System.Threading.LazyHelpers`1.ActivatorFactorySelector() +72
System.Threading.LazyInitializer.EnsureInitializedCore(T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory) +241
System.Threading.LazyInitializer.EnsureInitialized(T& target, Boolean& initialized, Object& syncLock) +139
myapPMVC4.Filters.InitializeSimpleMembershipAttribute.OnActionExecuting(ActionExecutingContext filterContext) in c:\Users\name\Documents\Visual Studio 2012\Projects\myapPMVC4\myapPMVC4\Filters\InitializeSimpleMembershipAttribute.cs:22
System.Web.Mvc.Async.AsyncControllerActionInvoker.InvokeActionMethodFilterAsynchronously(IActionFilter filter, ActionExecutingContext preContext, Func`1 nextInChain) +145
System.Web.Mvc.Async.AsyncControllerActionInvoker.InvokeActionMethodFilterAsynchronously(IActionFilter filter, ActionExecutingContext preContext, Func`1 nextInChain) +840201
System.Web.Mvc.Async.<>c__DisplayClass37.<BeginInvokeActionMethodWithFilters>b__31(AsyncCallback asyncCallback, Object asyncState) +266
System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +146
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +202
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag) +112
System.Web.Mvc.Async.<>c__DisplayClass25.<BeginInvokeAction>b__1e(AsyncCallback asyncCallback, Object asyncState) +839055
System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +146
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +166
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag) +27
System.Web.Mvc.<>c__DisplayClass1d.<BeginExecuteCore>b__17(AsyncCallback asyncCallback, Object asyncState) +50
System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +146
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +166
System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state) +826145
System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +146
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +166
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, Object tag) +27
System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +401
System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__2(AsyncCallback asyncCallback, Object asyncState) +786250
System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +146
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +166
System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, Object tag) +27
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +343
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +12550291
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +288
Ce qu'il se passe?
Thx
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Threading;
using System.Web.Mvc;
using WebMatrix.WebData;
using System.Web.Security;
using myapPMVC4.Models;
namespace myapPMVC4.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)
{
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
Database.SetInitializer<UsersContext>(null);
try
{
using (var context = new UsersContext())
{
if (!context.Database.Exists())
{
//Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
//WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
if (!WebSecurity.Initialized)
{
WebSecurity.InitializeDatabaseConnection("myapPMVC4DBContext", "Users", "UserId", "Email", autoCreateTables: true);
}
//Create Admin user
if (!WebSecurity.ConfirmAccount("[email protected]"))
{
//WebSecurity.CreateUserAndAccount("admin", "pass", new { email = "[email protected]" });
WebSecurity.CreateUserAndAccount("[email protected]", "pass");
}
//Create admin role if not exist
if (!Roles.RoleExists("Administrator"))
{
Roles.CreateRole("Administrator");
Roles.AddUserToRole("[email protected]", "Administrator");
}
}
catch (Exception ex)
{
throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
}
}
}
}
}
- Montre-nous ton code. Quelle est la trace de la pile?
- double possible de WebSecurity.InitializeDatabaseConnection méthode ne peut être appelée qu'une seule fois
- pas de double - vu, n'est-ce pas m'aider
- ajouté trace de la pile au-dessus de
Vous devez vous connecter pour publier un commentaire.
Le problème ...
est que
InitializeDatabaseConnection
appelsWebSecurity.InitializeProviders
en interne, et cette méthode n'est pas thread-safe, puis combinez cela avec le fait que WebSecurity généralement des besoins de l'initialisation à partir de différents endroits. Ceci a des implications que les applications web sont intrinsèquement multi-thread environnements ... etWebSecurity.Initialized
etWebSecurity.InitializeDatabaseConnection
ne sont pas thread-safe, lorsqu'utilisés ensemble - ils créer un typique condition de course.De semis (pour les migrations) signifie que votre
WebSecurity
peut être initialisé plus d'une fois que vous pouvez aussi avoir besoin de l'initialiser dans le Mondial.asax.cs pour les déploiements à l'ensemencement éteint, et votreInitializeSimpleMembershipAttribute
peut potentiellement être appelée plusieurs fois simultaneusly par des requêtes http en direct déploiements etc.De mettre le code d'initialisation dans plusieurs endroits casse également votre SECness
La solution ...
Assurez-vous que votre init appels sont thread-safe, et qu'une seule fois par exemple d'un domaine d'application. L'utilisation d'un thread-safe classe singleton pour ce faire; et de réduire la duplication de code.
Appeler le singleton
EnsureInitialize
méthode de tout/tous les éléments suivants, en fonction de votre application:Application_Start
méthode avant toute autre chose)Seed
méthode (avant la création de l'utilisateur)SimpleMembershipInitializer
constructeur après le contexte est initialisé)Voici un exemple simple de singleton:
Une fois que je l'avais fait, mon cas de l'erreur que vous mentionnez disparu, de ne pas être vu à nouveau.
Notes de bas de page
La classe singleton conserve également votre code SEC, ce qui est particulièrement utile pendant la phase précoce du développement d'une application si vous avez besoin de modifier la configuration de votre
WebSecurity.InitializeDatabaseConnection
comme il va seulement être dans un endroit (end edit)Je garde aussi le
SimpleMembershipInitializer
propre, et au lieu de graines de mes utilisateurs avec les communes de semis dans les Migrations\Configuration.csSeed
méthode. Cette aide à la testabilité de semis mon migrations en gardant tout dans un seul endroit. J'utilise les tests unitaires pour s'assurer que nous pouvons toujours monter et descendre les migrations de l'arbre, donc cela rend plus facile de le faire.Toutefois, le lieu de vos semis code n'a pas d'importance, il est plus juste pour s'assurer que, globalement, vous avez initialisé WebSecurity qu'une seule fois dans votre domaine d'application, et que tout appel à
InitializeDatabaseConnection
est thread-safe.WebSecurity
(voir Place de les mettre en Base de données.SetInitializer et fusil de chasse de chirurgie). Tout à fait séparément-je utiliser monEnsureInitialize
dans ces deux endroits, sans "si WebSec...Init.." tests pour permettre à divers scénarios de déploiement.Ajouter ce code à
Global.asax.cs
. Cela permet de s'assurer que votre base de données est toujours Initialisée avant toutes les autres exécutions. Assurez-vous également de sa première inscription dansApplication_Start()
Se débarrasser de
Filters/InitializeSimpleMembershipAttribute.cs
ou simplement en commentaire le code à l'intérieur, dans le cas où vous voudriez revenir à elle.Supprimer
[InitializeSimpleMembership]
en haut deAccountController.cs
Aussi, si vous ne l'avez pas déjà activé migrations, je voudrais vous encourager à le faire. De cette façon, vous pouvez faire vos graines dans
Configuration.cs
créé à l'intérieur de laMigration folder
lorsque vous exécutezEnable-Migrations
Si il est déjà initialisé alors assurez-vous que votre premier appel:
De cette façon, vous serez sûr que vous ne répétez pas le processus d'initialisation.
Disposez-vous également de la InitializeSimpleMembership attribut sur votre AccountController classe? (c'est la valeur par défaut). Si oui, vous sont en cours d'initialisation deux fois.
<add key="enableSimpleMembership" value="true" />
dans votre site web.config?À assurez-vous que le WebSecurity.InitializeDatabaseConnection n'est pas appelé à deux reprises suffit d'utiliser la WebSecurity.Initialisé pour vérifier si ça a déjà été appelée. Cette blog fournit des instructions détaillées sur l'ensemencement et la personnalisation de SimpleMembership. Il y a une série dans ce blog sur l'utilisation de SimpleMembership et je recommande aussi de regarder le découplage SimpleMembership à partir de votre ASP.NET Application MVC. Vous pouvez obtenir le le code source complet pour ces exemples ici.