Подтвердить что ты не робот

Почему InitializeSimpleMembershipAttribute в приложении MVC 4

Я думаю, что мое понимание на SimpleMembershipProvider составляет почти 60%, а остальные узнают, как он работает внутри.

Вы можете быстро найти проблему при использовании фильтра [InitializeSimpleMembership] только в AccountController (шаблон по умолчанию). Я думаю, что везде, где вы используете API-интерфейс Memberhsip или WebMatrix.WebSecurity, вам нужно сначала убедиться, что этот фильтр нужно вызвать.

Позже, если вы используете User.IsInRole в моем _Layout.cshtml. Вам необходимо применить фильтр ко всем контроллерам, после чего вы начнете его регистрировать в глобальном масштабе.

Однако я просто понимаю, что есть LazyInitializer.EnsureInitialized, которые делают инициализацию выполняемой только один раз за начало приложения.

Итак, почему SimpleMembershipInitializer (в фильтре) не находится непосредственно в Application_Start? Есть ли причина использовать фильтр?

4b9b3361

Ответ 1

Я считаю, что шаблон использовал атрибут для инициализации базы данных, чтобы не аутентифицированные части сайта все равно работали, если инициализация завершилась неудачно.

Для большинства практических целей лучше всего это сделать в App_Start.

Ответ 2

Если вы должны были объединить InitializeSimpleMembershipAttribute в Global.asax.cs Application_Start, чтобы SimpleMembershipProvider был инициализирован без вызова маршрутов AccountController...

... он может выглядеть примерно так: 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);
                }
            }
        }
    }
}

Ответ 3

Если вы планируете, чтобы InitializeSimpleMembershipAttribute выполнялся глобально, было бы лучше использовать способ MVC 4 в App_Start\FilterConfig.cs;

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

Удерживает Global.asax.cs в чистоте от кода, который, вероятно, должен быть инкапсулирован, как MVC 4 по сравнению с предыдущими версиями. Отличается чистотой:

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

Я также рекомендую изменить тип на AuthorizeAttribute (это действительно то, что он делает), потому что методы AuthorizeAttribute выполняются до методов ActionFilterAttribute. (Это должно приводить к меньшим проблемам, если другие ActionFilters проверяют безопасность и позволяют создавать пользовательские атрибуты 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 ...
    }
}

Ответ 4

Вдохновленный ответом Аарона, я реализовал решение, которое сохраняет Global.asax в чистоте и повторяет код, который поставляется с шаблоном.

  • Добавьте одну строку в метод RegisterGlobalFilters в RegisterApp_Satrt/FilterConfig.cs

    filters.Add(new InitializeSimpleMembershipAttribute());
  • Добавьте конструктор по умолчанию в класс InitializeMembershipAttribute, который находится в папке Filters. Содержимое этого конструктора будет такой же, что и в переопределении метода OnActionExecuting. (Вот как выглядит конструктор)

    public InitializeSimpleMembershipAttribute()
        {
            // Ensure ASP.NET Simple Membership is initialized only once per app start
            LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
        }
  • Комментарий (или удаление) переопределения метода OnActionExecuting.

И что это. Это решение дает мне два основных преимущества:

  • Гибкость проверки элементов, связанных с членством и ролями сразу после строки FilterConfig.RegisterGlobalFilters(GlbalFilters.Filters), выполняется на global.asax.

  • Обеспечивает, что инициализация базы данных WebSecurity выполняется только один раз.


РЕДАКТИРОВАТЬ: InitializeSimpleMembershipAttribute, который я использую.

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

Ответ 5

Я постучал головой о стены на день, пытаясь понять, почему моя роль. GetRoleForUser не удалась. Это произошло из-за того, что LazyInitializer не получил вызов.

Итак, как сказал Мэтт, просто поставьте его в App_Start, чтобы убедиться, что у вас нет проблем.

Ответ 6

Я потратил много часов на эту самую проблему. но я закончил с этим изменением:

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

    }
}

Я случайно обнаружил следующую ошибку:

System.Web.HttpException(0x80004005): невозможно подключиться к базе данных SQL Server. --- > System.Data.SqlClient.SqlException(0x80131904): При установлении соединения с SQL Server возникла связанная с сетью или конкретная ошибка экземпляра. Сервер не найден или не был доступен. Проверьте правильность имени экземпляра и настройте SQL Server для удаленного подключения. (поставщик: сетевые интерфейсы SQL, ошибка: 26 - ошибка определения местонахождения сервера/экземпляра)

Я заметил, что всякий раз, когда я вижу ошибку, я также вижу:

в ASP._Page_Views_Shared__Layout_cshtml.Execute() в h:\root\home\btournoux-001\www\site7\Views\Shared_Layout.cshtml: строка 5

В моей _Layout.cshtml это следующая строка:

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

Итак, чтобы проверить мое простое решение, я помещаю точку останова в свой класс InitializeSmpleMembershipAttribute на вызов EnsureInitialized, а другой - в первой строке в 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);

В дополнение к этим двум точкам останова я также поставил точку останова в моем _Layout.cshtml(я поставил тест для пользователя в разделе кода, чтобы добавить точку останова.

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

После того, как я установил точки останова, я должен был закомментировать фильтры .Add(new InitializSimpleMembershipAttribute(), а затем запустил приложение в Visual Studio. Я видел, что я ударил точку останова в _Layout.cshtml перед любым другим точка останова. Затем я раскоментировал эту строку и снова запустил приложение. На этот раз я увидел точки останова внутри класса InitializeSimpleMembershipAttribute до точки останова в _Layout.cshtml. И, чтобы быть уверенным, что он работает правильно, я вошел в систему на своем веб-сайте и затем увидел первую точку останова в классе InitializeSimpleMembershipAttribute (EnsureInitialized), но не второй - это то, что я ожидал.

Итак, все работает.

Спасибо всем, кто это открыл!

Ответ 7

Причиной для фильтра InitializeSimpleMembership и его чрезмерно сложного кода является тот случай, когда разработчик может решить не использовать проверку подлинности на основе форм, тогда сгенерированный шаблон будет по-прежнему работать правильно. Если вы всегда будете использовать проверку подлинности форм, вы можете инициализировать SimpleMembership в методе Application_Start для Global.asax. подробные инструкции о том, как это сделать здесь.