7
votes

ASP.NET MVC 4, la méthode «Websecurity.InitializedAbaseconnection» peut être appelée qu'une seule fois

Je développe une application Web de code de code dans Visual Studio 2012 Express.

J'utilise cette chaîne de connexion dans le web.config: p> xxx pré>

J'utilise SimpleMembership . P>

J'essaie de semer 1 administrateur de filtres / initialisiseimplememberthipattribute.cs: ... p>

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("admin@myapp.com"))
                    {
                       //WebSecurity.CreateUserAndAccount("admin", "pass", new { email = "a@b.com" });
                       WebSecurity.CreateUserAndAccount("admin@myapp.com", "pass");
                    }

                    // Create admin role if not exist
                    if (!Roles.RoleExists("Administrator"))
                    {
                       Roles.CreateRole("Administrator");
                       Roles.AddUserToRole("admin@myapp.com", "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);
                }
            }
        }
    }
}


4 commentaires

Montrez-nous votre code. Quelle est la trace de la pile?


Dupliqué possible de La méthode WebSecurity.InitializedAbaseconnection ne peut être appelée qu'une fois


pas dupliquer - vu ça, ne m'a pas aidé


Ajout de la trace de la pile ci-dessus


5 Réponses :


2
votes

S'il est déjà initialisé, assurez-vous que votre premier appel: xxx

De cette façon, vous serez sûr de ne pas répéter le processus d'initialisation.


2 commentaires

Cela ne semble pas aider - voir ci-dessus. FYI, ma méthode de semences SampleData.cs dérive de 'DropcreateDatabaseways): Classe publique SampleData: DropcreateDatabaseways


J'ai essayé d'envelopper LAZYYLOader (voir AccountController.cs ci-dessus) dans ceci - N'a pas aidé.



0
votes

Avez-vous également l'attribut d'initialisationImplemEnplemEship sur votre classe de comptyController? (Ceci est la valeur par défaut). Si tel est le cas, vous initialisez deux fois.


10 commentaires

"Vous devez appeler la méthode" WebSecurity.InitializedAbaseconnection "avant d'appeler toute autre méthode de la classe" Websecurity ". Cet appel doit être placé dans un fichier _Appstart.cshtml à la racine de votre site."


@ user2254951 - Vous êtes absolument sûr que c'est le seul endroit où est la méthode initaliséatabaseconnection? Vous êtes sûr de ne pas avoir l'attribut dans plus d'un endroit?


Non, comme je dis ci-dessus - si je supprimais [Intialiszeimplememembervership] dans AccountController.cs - I Obtenir l'erreur ci-dessus (ce qui l'implique qu'il n'a pas été initialisé du tout


@ user2254951 - non? Tu n'es pas sûr?


@ user2254951 - Vous devez fournir tout le code source de votre version d'initialisationImplememberShipAttribute, en offrant uniquement une petite partie, nous ne pouvons pas voir ce que vous pourriez faire d'autre.


@ user2254951 - Il y a quelque chose qui se passe ici que vous ne nous montrez pas .. Vous devez l'appeler deux fois en quelque sorte.


@ user2254951 - Vous n'avez pas de fichier _Appstart.cshtml. Est-ce que cela a un appel d'initialiséeAbaseconnection? Avez-vous dans votre web.config?


Je n'ai pas de code dans _appstart.cshtml, seulement des informations de mise en page (un pendage de l'ancienne webmatrix, je pense?) - Et pas de référence web.config


Je pense que je l'ai réduit à la réduction - il semble fonctionner si je commencez ces lignes: [Code] (// Créer un utilisateur administrateur si (! Websecurity.confirmaccount ("admin@mydom.com")) {// Websecurity. CreateUserAndAccount ("admin", "Pass", nouveau {email = "a@b.com"}); Websecurity.createUserandAccount ("admin@mydom.com", "Pass");} // Créer un rôle d'administrateur sinon existent if (! roles.roleexists ("administrateur")) {roles.criptionaleole ("administrateur"); roles.addusertorole ("admin@mydom.com", "administrateur");} est quelque chose qui ne va pas avec eux? Si oui, quel est le meilleur moyen de semer les administrateurs?


Code formaté: Je pense que je l'ai réduit - il semble fonctionner si je commencez ces lignes: '// Créer un utilisateur administrateur si (! Websecurity.confirmaccount ("admin@mydom.com")) {// Websecurity. CreateUserandAccount ("admin", "Pass", nouveau {email = "a@b.com"}); Websecurity.createUserandAccount ("admin@mydom.com", "Pass"); } // Créer un rôle d'administration s'il n'existe pas si (! Roles.RoleExists ("Administrateur")) {Roles.Createrole ("Administrateur"); Roles.adduSertorole ("admin@mydom.com", "administrateur"); } '



14
votes

Le problème ...

est que initialisatabaseconnection code> appels Websecurity.initializeProviders code> interne et cette méthode n'est pas thread-coffre-fort fort>, Ensuite, combinez cela avec le fait que WebSecurity a généralement besoin d'initialiser à partir de divers endroits. Cela a des implications comme des applications Web sont intrinsèquement des environnements multi-filetés ... et Websecurity.Initialisé code> et WebSecurity.initializedAbaseconnection code> ne sont pas thread-sûrs lorsqu'ils sont utilisés ensemble EM> - Ils créent une condition de course typique. P>

Semencage (pour les migrations) signifie que votre Websecurity code> peut être initialisé plus d'une fois, car vous devrez peut-être également l'initialiser à Global. ASAX.cs Pour les déploiements avec semis éteint, et votre initialisiseimplemembeShipAttribute code> peut potentiellement être appelé plusieurs fois simultanée par les demandes HTTP dans des déploiements en direct, etc. P>

Mettre le code d'initialisation dans plusieurs endroits également. Brise votre Dry NESS P>

La solution ... h3 >

Assurez-vous que vos appels d'initialisation sont à thread-coffre-fort et ne se produisent qu'une fois par exemple d'une appdomaine. Utilisez une classe Singleton Singleton sur le fil pour le faire; et réduire la duplication du code. P>

Appelez la méthode SINGLETON ''S> EURISITIALISE CODE> à partir de tout / Tout ce qui suit, selon le cas possible pour votre application: P>

  • global.asax.cs ( application_start code> méthode avant tout autre) li>
  • migrations \ configuration.cs graines code> (avant de créer les utilisateurs) li>
  • filtres \ initialisiseimplemembeShipAttribute.cs (dans le constructeur SimplemembershipInitializer code> après le contexte est initialisé) LI> ul>

    Voici un exemple simple Singleton: P>

    // 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);
                    }
                }
            }
        }
    }
    


8 commentaires

Merci - ça a l'air vraiment intéressant. Rapportera l'ASAP (hors ligne pendant une journée)


En mettant en œuvre ceci maintenant - pouvez-vous recommander des exemples d'applications que je peux télécharger intégrer les meilleures pratiques - comme celle-ci. Je suis nouveau à MVC et ce serait très utile. Mvcmusicstore ne va pas presque assez loin ...


@ user2254951. En tant que sa propre classe, cela peut aller n'importe où, tant qu'il est accessible par tous les points que j'ai mentionnés qui doivent l'appeler. Je ne suis pas sûr de l'échantillon d'applications - les modèles fournis par VS sont un bon début, mais je n'ai trouvé aucun bien, grand, des exemples de meilleures pratiques encore.


Essayer de mieux comprendre la situation. J'ai ceci dans la première ligne d'application_start (): 'if (! Websecurity.Initialisé) {System.Data.entity.database.setinity.database.setinitializer (nouveau myappmvc4.models.sampledata ()); } 'J'ai aussi cela, dans les initialesImsizeImplememberShipAttribute.cs: si (! Websecurity.initialisé) {Websecurity.InitializedAbaseconnection ("MyAppmVC4DBContext", "Utilisateurs", "Courriel", "email", autocréate: true); } Ce que vous dites, c'est qu'ils conflit et ont besoin de passer au même endroit dans le code qui est exécuté une seule fois?


Je pense qu'il y a un écart sur le marché - je paierais certainement 5 $? 20 $? ou plus pour une application complète d'échantillons clairs ....


@ user2254951. Votre première ligne a l'air étrange pour moi, je ne l'ai jamais fait comme ça, ou le lier à Websecurity (voir endroit pour mettre de la base de données.Setinitializer et Chirurgie de fusil de chasse ). J'utilise assez séparément mon S'assurez-existialiser dans les deux endroits sans aucun "si Websec ... init .." Tests pour permettre à divers scénarios de déploiement.


Merci - j'ai aussi trouvé ce message très utile: blog.longle.net/2012/09/25/...


Merci beaucoup pour cela. Je pourrais obtenir mon site pour courir et semer localement, mais je continue à courir dans la question lors de la publication d'Azure. J'ai toujours besoin de grokser complètement les concepts, mais samedi après-midi sauvé! :)



13
votes

Ajouter ce code à global.asax.cs code>. Cela veillera à ce que votre base de données soit toujours initialisée avant toute autre exécution. Assurez-vous également que c'est la première inscription dans Application_Start () CODE>

if (!WebSecurity.Initialized)
                WebSecurity.InitializeDatabaseConnection("DefaultConnection",
"UserProfile", "UserId", "UserName", autoCreateTables: true);


0 commentaires