24

我认为我的理解SimpleMembershipProvider几乎是 60%,剩下的就是了解它是如何在内部工作的。

[InitializeSimpleMembership]仅在 AccountController(默认模板)中使用过滤器时,您可以很快发现一些问题。我认为在您使用 Memberhsip API 的任何地方WebMatrix.WebSecurity,您都需要确保首先调用此过滤器。

以后,如果你User.IsInRole在我的_Layout.cshtml. 您需要将过滤器应用于所有控制器,然后开始在全局中注册它。

但是,我只是意识到LazyInitializer.EnsureInitialized每个应用程序启动只执行一次初始化。

那么为什么SimpleMembershipInitializer(在过滤器中)不直接在 Application_Start 中?有什么理由使用过滤器吗?

4

7 回答 7

21

我相信该模板使用了一个属性进行数据库初始化,因此如果初始化失败,该站点的未经过身份验证的部分仍然可以工作。

对于大多数实际目的,最好只在 App_Start 中完成此操作。

于 2012-11-02T01:36:36.993 回答
18

如果您要将 合并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);
                }
            }
        }
    }
}
于 2013-02-21T18:23:19.917 回答
5

如果计划确保在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 方法之前执行。(如果其他 ActionFilter 正在检查安全性并允许派生自定义 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 ...
    }
}
于 2013-07-20T20:08:18.420 回答
3

受 Aaron 回答的启发,我实现了一个解决方案,它可以保持 Global.asax 的清洁并重用模板附带的代码。

  1. 在 RegisterApp_Satrt/FilterConfig.cs 中的 RegisterGlobalFilters 方法中添加一行

    filters.Add(new InitializeSimpleMembershipAttribute());
  2. 将默认构造函数添加到 Filters 文件夹中的 InitializeMembershipAttribute 类。此构造函数的内容将与 OnActionExecuting 方法的覆盖中的内容相同。(这是构造函数的样子)

    公共 InitializeSimpleMembershipAttribute()
        {
            // 确保每次应用启动时 ASP.NET 简单成员仅初始化一次
            LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
        }
  3. 注释掉(或删除) OnActionExecuting 方法的覆盖。

就是这样。这个解决方案给了我两个主要好处:

  1. FilterConfig.RegisterGlobalFilters(GlbalFilters.Filters)在 global.asax 上执行 line后立即检查与成员资格和角色相关的事情的灵活性。

  2. 确保 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);
            }
        }
    }
}
于 2014-08-17T14:27:22.007 回答
2

我在这个问题上花了很多时间。但我最终得到了这个改变:

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:line 5

这恰好是我的 _Layout.cshtml 中的以下行:

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

因此,为了测试我的简单解决方案,我在 EnsureInitialized 调用的 InitializeSmpleMembershipAttribute 类中放置了一个断点,在 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 中放置了一个断点(我将 User 的测试放在代码部分中,这样我就可以添加断点。

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

放入断点后,我所做的是注释掉 filters.Add(new InitializSimpleMembershipAttribute() 然后在 Visual Studio 中启动应用程序。我可以看到我在任何其他断点之前命中了 _Layout.cshtml 中的断点。然后我取消注释该行并再次运行应用程序。这次我看到 InitializeSimpleMembershipAttribute 类中的断点发生在 _Layout.cshtml 中的断点之前。为了确保它正常工作,我登录了我的网站,然后看到InitializeSimpleMembershipAttribute 类(EnsureInitialized)中的第一个断点,但不是第二个断点 - 这是我所期望的。

所以这一切似乎都奏效了。

感谢所有发现这一点的人!

于 2013-12-23T17:22:10.610 回答
2

我在墙上敲了一天的头,试图弄清楚为什么我的 Role.GetRoleForUser 失败了。这是因为 LazyInitializer 没有被调用。

所以,就像马特说的那样,把它放在 App_Start 中以确保你没有问题。

于 2013-01-24T14:56:39.487 回答
0

InitializeSimpleMembership 过滤器及其过于复杂的代码的原因是开发人员可能决定不使用表单身份验证,然后模板生成的代码仍然可以正常工作。如果您将始终使用表单身份验证,您可以在 Global.asax 的 Application_Start 方法中初始化 SimpleMembership。此处有有关如何执行此操作的详细说明

于 2013-02-21T18:41:39.730 回答