tag route net mvc español asp asp.net-mvc asp.net-mvc-4 simplemembership

asp.net-mvc - route - asp.net mvc 7



Por qué InitializeSimpleMembershipAttribute en la aplicación MVC 4 (7)

Creo que mi comprensión sobre SimpleMembershipProvider es casi del 60% y el resto es conocer cómo funciona internamente.

Puede encontrar rápidamente algún problema al usar el filtro [InitializeSimpleMembership] solo en AccountController (la plantilla predeterminada). Creo que en cualquier lugar que use Memberhsip API o WebMatrix.WebSecurity , debe asegurarse de que este filtro se llame primero.

Más tarde, si usa User.IsInRole en mi _Layout.cshtml . Debe aplicar el filtro a todos los controladores, luego comienza a registrarlo globalmente.

Sin embargo, me acabo de dar cuenta de que hay LazyInitializer.EnsureInitialized que hace que la inicialización se realice solo una vez por cada inicio de la aplicación.

Entonces, ¿por qué SimpleMembershipInitializer (en el filtro) no está directamente en Application_Start? ¿Hay alguna razón para usar el filtro?


Creo que la plantilla usó un atributo para la inicialización de la base de datos, por lo que las partes no autenticadas del sitio seguirían funcionando si la inicialización fallara.

Para la mayoría de los propósitos prácticos, lo mejor es hacer esto en App_Start.


Inspirada en la respuesta de Aaron, he implementado una solución que mantiene a Global.asax limpio y reutiliza el código que viene con la plantilla.

  1. Agregue una línea al método RegisterGlobalFilters en RegisterApp_Satrt / FilterConfig.cs

    filters.Add(new InitializeSimpleMembershipAttribute());

  2. Agregue un constructor predeterminado a la clase InitializeMembershipAttribute que se encuentra en la carpeta Filtros. El contenido de este constructor va a ser la misma línea que está en la anulación del método OnActionExecuting. (Así es como se ve el constructor)

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

  3. Comente (o elimine) la anulación del método OnActionExecuting.

Y eso es. Esta solución me está dando dos beneficios principales:

  1. La flexibilidad para verificar las cosas relacionadas con la pertenencia y los roles inmediatamente después de la línea FilterConfig.RegisterGlobalFilters(GlbalFilters.Filters) se ejecuta en global.asax.

  2. Garantiza que la inicialización de la base de datos de WebSecurity se ejecute solo una vez.

EDITAR : InitializeSimpleMembershipAttribute que estoy usando.

[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 InitializeSimpleMembershipAttribute() { // Ensure ASP.NET Simple Membership is initialized only once per app start LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock); } //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() { 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("Database_Connection_String_Name", "Users", "UserId", "UserName", autoCreateTables: true); } 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); } } } }


La razón para el filtro InitializeSimpleMembership y su código demasiado complejo es para el caso en que un desarrollador puede decidir no usar la autenticación de formularios, entonces el código generado por la plantilla seguirá funcionando correctamente. Si siempre usará autenticación de formularios, puede inicializar SimpleMembership en el método Application_Start de Global.asax. Hay instrucciones detalladas sobre cómo hacer esto aquí .


Me golpeé la cabeza contra las paredes por un día tratando de descubrir por qué mi Role.GetRoleForUser falló. Fue debido a que LazyInitializer no fue llamado.

Entonces, como dijo Matt, simplemente póngalo en App_Start para asegurarse de que no tenga problemas.


Pasé muchas horas en este mismo problema. pero terminé con solo este cambio:

public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); filters.Add(new InitializeSimpleMembershipAttribute()); } }

He estado viendo al azar el siguiente error

System.Web.HttpException (0x80004005): no se puede conectar a la base de datos de SQL Server. ---> System.Data.SqlClient.SqlException (0x80131904): Se ha producido un error relacionado con la red o específico de la instancia al establecer una conexión con SQL Server. El servidor no se encontró o no estaba accesible. Verifique que el nombre de la instancia sea correcto y que SQL Server esté configurado para permitir conexiones remotas. (proveedor: Interfaces de red SQL, error: 26 - Error al localizar el servidor / Instancia especificada)

Noté que cada vez que veía el error también veía:

en ASP._Page_Views_Shared__Layout_cshtml.Execute () en h: / root / home / btournoux-001 / www / site7 / Views / Shared_Layout.cshtml: línea 5

Esta es la siguiente línea en mi _Layout.cshtml:

if (User != null && User.Identity != null && (User.IsInRole("publisher") || User.IsInRole("admin")))

Entonces, para probar mi solución simple, puse un punto de interrupción en mi clase InitializeSmpleMembershipAttribute en la llamada EnsureInitialized y otra en la primera línea en SimpleMembershipInitializer

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() { Database.SetInitializer<DataContext>(null);

Además de esos 2 puntos de interrupción también puse un punto de interrupción en mi _Layout.cshtml (puse la prueba para el usuario en una sección de código para poder agregar el punto de interrupción.

@{ var maintenanceAccess = false; if (User != null && User.Identity != null && (User.IsInRole("publisher") || User.IsInRole("admin"))) { maintenanceAccess = true; } }

Después de poner en los puntos de interrupción lo que hice fue comentar los filtros.Añadir (nuevo InitializSimpleMembershipAttribute () y luego iniciar la aplicación en Visual Studio. Pude ver que llegué al punto de interrupción en el _Layout.cshtml antes de cualquier otro punto de interrupción. Descomenté esa línea y volví a ejecutar la aplicación. Esta vez vi los puntos de interrupción dentro de la clase InitializeSimpleMembershipAttribute antes del punto de interrupción en _Layout.cshtml. Y para estar seguro de que funcionaba correctamente, inicié sesión en mi sitio web y luego vi el primer punto de interrupción en la clase InitializeSimpleMembershipAttribute (EnsureInitialized) pero no el segundo, que era lo que esperaba.

Entonces todo parece funcionar.

¡Gracias a todos los que descubrieron esto!


Si combinara InitializeSimpleMembershipAttribute en Global.asax.cs Application_Start para que SimpleMembershipProvider se inicialice sin que se haya AccountController ninguna ruta AccountController ...

... podría verse más o menos así: http://aaron-hoffman.blogspot.com/2013/02/aspnet-mvc-4-membership-users-passwords.html

// The using below is needed for "UsersContext" - it will be relative to your project namespace using MvcApplication1.Models; using System; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Threading; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using WebMatrix.WebData; namespace MvcApplication1 { // Note: For instructions on enabling IIS6 or IIS7 classic mode, // visit http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); AuthConfig.RegisterAuth(); // Ensure ASP.NET Simple Membership is initialized only once per app start LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock); } private static SimpleMembershipInitializer _initializer; private static object _initializerLock = new object(); private static bool _isInitialized; 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); } 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); } } } } }


Si planea asegurarse de que InitializeSimpleMembershipAttribute ejecute globalmente, sería una buena práctica usar MVC 4 en el App_Start/FilterConfig.cs ;

public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); filters.Add(new InitializeMembershipAttribute()); } }

Mantiene limpia la Global.asax.cs del código que probablemente debería encapsularse de la misma forma que MVC 4 en versiones anteriores. Deja una buena limpieza:

public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); AuthConfig.RegisterAuth(); } }

También recomiendo cambiar el tipo a AuthorizeAttribute (que es realmente lo que hace) porque los métodos AuthorizeAttribute se ejecutan antes de los métodos de ActionFilterAttribute. (Esto debería producir menos problemas si otros ActionFilters están comprobando la seguridad, y permite derivadas Custom AuthorizeAttributes).

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class InitializeMembershipAttribute : AuthorizeAttribute { private static SimpleMembershipInitializer _initializer; private static object _initializerLock = new object(); private static bool _isInitialized; public override void OnAuthorization(AuthorizationContext filterContext) { // Ensure ASP.NET Simple Membership is initialized only once per app start LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock); base.OnAuthorization(filterContext); } private class SimpleMembershipInitializer ... } }