9

我有一个在 Azure 网站上托管的基本 ASP.NET MVC 4 站点。身份验证是表单身份验证,尚未从默认模板自定义。每次我发布时,当我重新访问我的网站时,它只会挂起很长的超时时间(可能是几分钟),然后才最终向我显示一条错误消息。我可以通过在浏览器中删除网站的 cookie 并重新加载来恢复。

最初的问题只是试图访问需要身份验证的页面,但后来我将其添加到我的共享中_Layout.cshtml

@if (User.IsInRole("Admin"))
{
    <li>@Html.ActionLink("Admin", "Index", "Admin")</li>
}

这意味着在新发布后根本无法访问任何页面,因此我什至无法单击注销链接,这是我过去能够解决问题的另一种方式。

我有什么配置错误吗?虽然我有一个我可以自己使用的解决方法,但在我发布更新后,这对于网站用户来说并不是一个好的体验。

编辑:从 ELMAH 日志中,当我调用 IsInRole 时,表单身份验证似乎正在尝试创建 SQL Express 数据库。我不明白为什么会这样做,因为我的表单身份验证都设置为使用我的 SQL Azure 数据库。

System.Web.HttpException (0x80004005): Unable to connect to SQL Server database. ---> System.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
   at System.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean withFailover)
   at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, SqlConnection owningObject, Boolean withFailover)
   at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, Boolean redirectedUserInstance, SqlConnection owningObject, SqlConnectionString connectionOptions, TimeoutTimer timeout)
   at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(SqlConnection owningObject, TimeoutTimer timeout, SqlConnectionString connectionOptions, String newPassword, Boolean redirectedUserInstance)
   at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, Object providerInfo, String newPassword, SqlConnection owningObject, Boolean redirectedUserInstance)
   at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection)
   at System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup)
   at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
   at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.SqlClient.SqlConnection.Open()
   at System.Web.Management.SqlServices.GetSqlConnection(String server, String user, String password, Boolean trusted, String connectionString)
ClientConnectionId:00000000-0000-0000-0000-000000000000
   at System.Web.Management.SqlServices.GetSqlConnection(String server, String user, String password, Boolean trusted, String connectionString)
   at System.Web.Management.SqlServices.SetupApplicationServices(String server, String user, String password, Boolean trusted, String connectionString, String database, String dbFileName, SqlFeatures features, Boolean install)
   at System.Web.Management.SqlServices.Install(String database, String dbFileName, String connectionString)
   at System.Web.DataAccess.SqlConnectionHelper.CreateMdfFile(String fullFileName, String dataDir, String connectionString)
   at System.Web.DataAccess.SqlConnectionHelper.EnsureSqlExpressDBFile(String connectionString)
   at System.Web.DataAccess.SqlConnectionHelper.GetConnection(String connectionString, Boolean revertImpersonation)
   at System.Web.Security.SqlRoleProvider.GetRolesForUser(String username)
   at WebMatrix.WebData.SimpleRoleProvider.GetRolesForUser(String username)
   at System.Web.Security.RolePrincipal.IsInRole(String role)
4

3 回答 3

9

在尝试了各种博客文章中的数十种不同建议后,我找到了解决方案。将InitialiseSimpleMembership属性添加到我的家庭控制器可以解决问题。

[InitializeSimpleMembership]
public class HomeController : Controller

进行此更改后,我成功地管理了几次发布,没有出现任何问题。我怀疑原因是InitializeSimpleMembershipAttribute构造函数中的以下代码行需要在进行任何调用之前运行User.IsInRole

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

我想最好的办法就是跑InitializeSimpleMembership进去Application_Start

于 2012-10-20T19:21:21.750 回答
4

tutorialWindows Azure 上部署应用程序时,您可以按照以下步骤设置 SQL 数据库。您必须在 web.config 中设置正确的连接字符串,当您使用 Internet 模板创建新的 ASP.NET MVC 4 应用程序时,默认情况下它指向本地 SQL Express 数据库。

您的 SQL Azure 连接字符串将如下所示:

<connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=tcp:#server#.database.windows.net,1433;Initial Catalog=#DBName#;User ID=UserName#@#server#;Password=#password#;MultipleActiveResultSets=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
于 2012-10-20T16:13:16.630 回答
1

虽然这个问题已经得到解答,但我想我可能会很快分享我遇到同样问题的经验。我的情况与马克的情况略有不同。

所以,我InitializeSimpleMembership在所有控制器上都有这个属性(实际上我在我的所有控制器继承的基本控制器上都有它),但是我仍然遇到同样的问题。现在,在我的基本控制器中,我还重写了该Initialize方法,以便为我们的应用程序设置一些上下文信息。此上下文设置的一部分是检查当前用户是否处于特定角色中,因此该IsUserInRole方法是在此Initialize方法中被调用的,所有这些都是在调用任何操作方法之前。

现在,如果看一下InitializeSimpleMembership类,就会注意到初始化实际上是在OnActionExecuting方法中完成的(即InitializeSimpleMembership继承自ActionFilterAttribute):

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

IsUserInRole就我而言,这种成员资格初始化发生得太晚了……也就是说,在我调用基本控制器的覆盖Initialize方法之前,我需要初始化简单成员资格。

对我来说解决方案相对简单:我InitializeSimpleMembership完全删除了该属性并将其逻辑直接放入我的基本控制器中,以便我可以从我的Initialize方法中调用它,如下所示:

public class BaseController : Controller
{
    private static SimpleMembershipInitializer _initializer;
    private static object _initializerLock = new object();
    private static bool _isInitialized;

    private class SimpleMembershipInitializer
    {
        public SimpleMembershipInitializer()
        {
            try
            {
                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);
            }
        }
    }

    ...

    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);
        LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
        SetupControllerContext(); // my function that calls 'IsUserInRole'
    }
}

现在我想应该Application_Start()按照 Mark 的建议重构它并将其放入方法中,但你明白了 :)。Initialize我只是想解释一下我的经验,以防万一有人在他们的控制器的覆盖方法中做类似的事情。

于 2013-02-10T08:17:26.387 回答