web-dev-qa-db-fra.com

Utilisation de SimpleMembership avec le modèle EF en premier

Est-ce que SimpleMembership peut être utilisé avec EF model-first ? Lorsque j'essaie, je reçois le message "Impossible de trouver le fournisseur de données .NET Framework demandé" lorsque j'appelle WebSecurity.InitializeDatabaseConnection.

En d'autres termes: l'appel de WebSecurity.InitializeDatabaseConnection ne peut pas fonctionner lorsque la chaîne de connexion utilise le fournisseur System.Data.EntityClient (comme c'est le cas lorsque vous utilisez le modèle model-first paradigm).

Pour reproduire le problème, créez une application MVC 4 et remplacez la classe d'entité code-first UserProfile (fournie gratuitement avec le modèle MVC 4) par la classe model-first User que vous avez. créé dans le concepteur d'entité:

  1. Créez une application MVC 4 dans VS 2012 et ajoutez un nouveau modèle d'entité vierge Modèle.
  2. Ajoutez une nouvelle entité nommée User au modèle, avec des champs pour Id,UserName, and FullName. Ainsi, à ce stade, l'entité de données User est mappée Sur une table Users et est accessible via une connexion funky Chaîne qui utilise le fournisseur System.Data.EntityClient.
  3. Vérifiez que leEFpeut accéder à l’entité User. Un moyen facile de faire Consiste à échafauder un contrôleur Utilisateurs en fonction de la table User Et de son DbContext associé.
  4. Editez le fichier AccountModels.cs pour supprimer la classe UserProfile et Sa classe UsersContext associée. Remplacez les références aux classes (Maintenant manquantes) UserProfile et UsersContext par des référencesà votre nouvelle classe User et à sa classe DbContext associée.
  5. Déplacez l'appel vers InitializeDatabaseConnection de la classe de filtre InitializeSimpleMembershipAttribute vers la méthode Application_Start dans Global.asax.cs. Pendant que vous y êtes, Modifiez les arguments pour utiliser la connexion Chaîne, nom de table et nom de colonne UserId de votre nouvelle entité utilisateur.
  6. Supprimez la (plus utilisée) InitializeSimpleMembershipAttribute Class et ses références.

Lorsque vous exécutez la repro, il obtiendra un Exception à l’appel de InitializeDatabaseConnection.

Bob

24

SimpleMembership peut d'abord travailler avec le modèle. Voici la solution.

1 .InitializeSimpleMembershipAttribute.cs de l’application Internet MVC 4 Internet devrait ressembler à ceci: 

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.Supprimez les classes CodeFirst de AcountModel.cs

3. Corrigez AccountCotroler.cs pour travailler avec votre méthode DbContext (méthode ExternalLoginConfirmation(RegisterExternalLoginModel model, string returnUrl)) Model-first

4.Définissez votre chaîne de connexion "ConnStringForWebSecurity" qui n’est pas identique à cette chaîne de connexion géniale pour l’accès à la base de données premier modèle. Notez que nous utilisons le fournisseur System.Data.SqlClient et non le 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
Mario Zderic

C'est un bug dans MVC 4. Il y a une solution de contournement dans cet article de blog .

En tant que filtre d'action, InitializeSimpleMembershipAttribute s'accroche dans OnActionExecuting pour effectuer le travail d'initialisation différée, mais cela peut être trop tard dans le cycle de vie. L'attribut Autoriser nécessitera que les fournisseurs soient prêts plus tôt s'il doit effectuer des contrôles d'accès basés sur les rôles (pendant OnAuthorization). En d'autres termes, si la première demande adressée à un site déclenche une action du contrôleur telle que celle-ci:

[Authorize(Roles="Sales")]

.. alors vous aurez une exception car le filtre vérifie le rôle de l’utilisateur mais les fournisseurs ne sont pas initialisés.

Ma recommandation est de supprimer ISMA du projet et d'initialiser WebSecurity lors de l'événement de démarrage de l'application.

12
Craig Stuntz

1 - Vous devez activer les migrations, de préférence avec EntityFramework 5

2 - Déplacez votre 

WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "EmailAddress", autoCreateTables: true); 

à votre méthode Seed dans votre classe YourMvcApp/Migrations/Configuration.cs

    protected override void Seed(UsersContext context)
    {
        WebSecurity.InitializeDatabaseConnection(
            "DefaultConnection",
            "UserProfile",
            "UserId",
            "UserName", autoCreateTables: true);

        if (!Roles.RoleExists("Administrator"))
            Roles.CreateRole("Administrator");

        if (!WebSecurity.UserExists("lelong37"))
            WebSecurity.CreateUserAndAccount(
                "lelong37",
                "password",
                new {Mobile = "+19725000000", IsSmsVerified = false});

        if (!Roles.GetRolesForUser("lelong37").Contains("Administrator"))
            Roles.AddUsersToRoles(new[] {"lelong37"}, new[] {"Administrator"});
    }

Désormais, EF5 sera chargé de créer votre table UserProfile. Après cela, vous appellerez WebSecurity.InitializeDatabaseConnection pour n’enregistrer que SimpleMembershipProvider avec la table UserProfile déjà créée (Dans votre cas, vous pouvez remplacer la valeur du paramètre "UserProfile" par votre nom de table personnalisé), indiquant également à SimpleMembershipProvider quelle colonne est UserId et UserName. Je montre également un exemple de la manière dont vous pouvez ajouter des utilisateurs, des rôles et associer les deux dans votre méthode Seed à des propriétés/champs personnalisés UserProfile, par exemple. le numéro de téléphone portable d'un utilisateur.

3 - Désormais, lorsque vous exécuterez update-database à partir de la console Package Manager, EF5 configurera votre table avec toutes vos propriétés personnalisées.

Pour des références supplémentaires, veuillez vous référer à cet article avec le code source: http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider- ef5-codefirst-and-custom-user-properties/

10
LeLong37

ce problème causé par WebSecurity.InitializeDatabaseConnection ne peut pas utiliser une chaîne de connexion avec le nom de fournisseur System.Data.EntityClient.

fournir une chaîne de connexion double n'est pas bon, vous pouvez donc générer la chaîne de connexion pour le modèle EF en premier dans le constructeur de la classe partielle.

le code ressemble à ci-dessous

public partial class MyDataContext 
{
    private static string GenerateConnectionString(string connectionString)
    {
        var cs = System.Configuration.ConfigurationManager
                     .ConnectionStrings[connectionString];

        SqlConnectionStringBuilder sb = 
             new SqlConnectionStringBuilder(cs.ConnectionString);
        EntityConnectionStringBuilder builder = 
             new EntityConnectionStringBuilder();
        builder.Provider = cs.ProviderName;
        builder.ProviderConnectionString = sb.ConnectionString;
        builder.Metadata = "res://*/MyDataContext.csdl|" +
              "res://*/MyDataContext.ssdl|res://*/MyDataContext.msl";
        return builder.ToString();
    }

    public MyDataContext(string connectionName) : 
          base(GenerateConnectionString(connectionName)) { }
}

avec cette astuce, vous pouvez utiliser une chaîne de connexion unique sur votre configuration Web, mais un problème réside dans le fait que vous ne pouvez pas utiliser le constructeur par défaut sur votre contexte de données. Vous devez plutôt indiquer le nom de la chaîne de connexion partout lorsque vous instanciez le contexte de données. mais ce n'est pas un gros problème lorsque vous utilisez un modèle d'injection de dépendance.

1
ktutnik

Je ne suis pas capable de travailler avec EF et WebMatrix webSecurity class afin d'éviter ce problème et de continuer:

Changez d'abord mon modèle Ef en code d'abord.

Changez la chaîne de connexion pour utiliser providerName = "System.Data.SqlClient" (en supprimant toutes les informations de métadonnées) ou utilisez la connexion EF.

Dans mon cas, le modèle, les données et le Web sont des projets différents. Pour moi, il n’est donc pas un problème de supprimer ces informations de Web.config sur le Web.project.

De nos jours, websecuroty.initializedatabase ne s'exécute pas avec une chaîne de connexion EF.

Je souhaite que cela aide

0
jlsfernandez