9

Ich habe eine grundlegende ASP.NET MVC 4-Website, die ich auf Azure-Websites hosten. Die Authentifizierung ist die Formularauthentifizierung und wurde nicht von der Standardvorlage angepasst. Jedes Mal, wenn ich meine Site erneut öffne, hängt sie einfach mit einem sehr langen Timeout (vielleicht ein paar Minuten), bis sie mir endlich eine Fehlermeldung anzeigt. Ich kann wiederherstellen, indem ich die Cookies für die Seite in meinem Browser lösche und neu lade.Authentifizierungsprobleme nach der Veröffentlichung von MVC 4 App auf Azure

Zunächst wurde das Problem nur versucht, Seiten zuzugreifen, die eine Authentifizierung erforderlich, aber dann habe ich das meinem _Layout.cshtml geteilt:

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

, die überhaupt bedeutet nun keine Seiten zugänglich sind, nachdem eine neue veröffentlichen, und so Ich kann nicht einmal auf den Logout-Link klicken, was eine andere Möglichkeit war, das Problem zu beheben.

Habe ich etwas falsch konfiguriert? Obwohl ich eine Problemumgehung habe, die ich selbst verwenden kann, wird dies keine gute Erfahrung für Benutzer der Website sein, nachdem ich ein Update veröffentlicht habe.

BEARBEITEN: von den ELMAH-Protokollen scheint es, als ob Formularauthentifizierung versucht, eine SQL Express-Datenbank zu erstellen, wenn ich IsInRole aufruft. Ich kann nicht sehen, warum dies so wäre, da meine Formularauthentifizierung für die Verwendung meiner SQL Azure-Datenbank eingerichtet ist.

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) 
+0

Mark Ich habe diese Frage in meinem eigenen ähnlichen hier verwiesen ... http://stackoverflow.com/questions/24149044/two-chrome-sessions-on-the-same-machine-one-will-connect -zu-unserer-azurblauen-Website und wenn Ihre Antwort unten funktioniert, müssen Sie wirklich das Kopfgeld auf Ihre Weise drücken ... – hawbsl

Antwort

9

Nach Dutzenden von verschiedenen Vorschlägen von verschiedenen Blog-Posts versucht, ich habe eine Lösung gefunden zu haben. Das Hinzufügen des Attributs InitialiseSimpleMembership zu meinem Heim-Controller behebt das Problem.

Nach dieser Änderung habe ich mehrere erfolgreich veröffentlicht ohne Probleme verwaltet. Ich vermute, dass der Grund dafür ist, dass die folgende Codezeile in dem InitializeSimpleMembershipAttribute Konstruktor ausgeführt werden muss, bevor irgendwelche Anrufe User.IsInRole gemacht werden:

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

ich das Beste, was raten zu tun wäre InitializeSimpleMembership in Application_Start läuft.

+2

Wenn das funktioniert, müssen Sie möglicherweise die Provider explizit in der Datei web.config verdrahten. Siehe http://blog.longle.net/2012/09/25/seeding-user-and-roles-with-mvc4-simplemembershipprovider-simpleleprovider-ef5-codefirst-and-custom-user-properties/ – RickAndMSFT

+0

danke @RickAndMSFT, Ich habe an einer Stelle untersucht, wie ich die Mitgliedschafts- und Rollenanbieter in meiner Web.Config-Datei bearbeiten konnte, aber Informationen darüber, was die Syntax sein sollte, waren extrem schwer zu bekommen. –

+0

Ich habe ein ähnliches Problem festgestellt - die Fehlermeldung war Ein netzwerkbezogener oder instanzspezifischer Fehler ist beim Herstellen einer Verbindung zu SQL Server aufgetreten. Der Server wurde nicht gefunden oder war nicht erreichbar. Stellen Sie sicher, dass der Instanzname korrekt ist und dass SQL Server so konfiguriert ist, dass Remoteverbindungen zugelassen werden. (Anbieter: SQL Network Interfaces, Fehler: 26 - Fehler beim Auffinden des Servers/der Instanz angegeben). – Will

4

Hier ist ein tutorial, dass Sie eine SQL-Datenbank-Setup folgen könnte, wenn die Anwendung auf Windows Azure bereitstellen. Sie müssen die richtige Verbindungszeichenfolge in Ihrer Datei web.config festlegen, die standardmäßig auf eine lokale SQL Express-Datenbank verweist, wenn Sie eine neue ASP.NET MVC 4-Anwendung mithilfe der Internetvorlage erstellen.

Ihre SQL Azure-Verbindungszeichenfolge wird wie folgt aussehen:

<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> 
+2

Danke, aber es gibt kein Problem mit meiner Verbindungszeichenfolge. Sobald ich die Cookies in meinem Browser lösche, funktioniert die Website einwandfrei. Alle Daten sind da, alle Benutzer-Logins und Rollen sind da. Es ist nur der erste Zugriff nach einer Veröffentlichung, der schief geht. –

+0

Nun, ich muss mir wirklich vorstellen, was das Problem sein könnte. Versuchen Sie, dem Lernprogramm zu folgen, mit dem ich verlinkt habe. –

+0

Ja, ich habe das versucht und es hat mich dazu gebracht, dies zu installieren: http://nuget.org/packages/Microsoft.AspNet.Providers.LocalDB, aber das hat alles kaputt gemacht, was den Fehler in diesem Thread http: // social diskutiert. msdn.microsoft.com/forums/pl-pl/windowsazuredevelopment/thread/d352bb1b-577c-42b7-8872-5ed59cd65f32 –

1

Obwohl diese Frage beantwortet wurde, dachte ich, ich könnte meine Erfahrungen mit dem gleichen Problem schnell teilen. Mein Fall ist etwas anders als bei Mark.

Also, ich hatte die InitializeSimpleMembership Attribut auf alle meine Controller (eigentlich hatte ich es auf meinem Basis-Controller, von dem alle meine Controller erben), aber ich hatte immer noch das gleiche Problem. Jetzt habe ich in meinem Basis-Controller auch die Initialize-Methode überschrieben, um einige Kontextinformationen für unsere Anwendung einzurichten. Ein Teil dieses Kontext-Setups besteht darin, zu überprüfen, ob der aktuelle Benutzer in einer bestimmten Rolle ist. Daher wurde die Methode IsUserInRole innerhalb dieser Initialize-Methode aufgerufen, bevor eine Aktionsmethode aufgerufen wird.

Nun, wenn man einen Blick auf die InitializeSimpleMembership Klasse nimmt man merkt, dass die Initialisierung tatsächlich in der OnActionExecuting Methode (dh InitializeSimpleMembership erbt von ActionFilterAttribute) durchgeführt wird:

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

In meinem Fall diese Mitgliedschaft Initialisierung geschieht zu spät ... das heißt, ich brauche die Einfache Mitgliedschaft, die initialisiert werden muss, bevor ich meinen Anruf an IsUserInRole in meinem Basiscontroller Initialize Methode übersteuert.

Die Lösung war für mich relativ einfach: Ich entfernte die InitializeSimpleMembership ganz Attribut und seine Logik direkt in meine Basis-Controller setzen, so dass ich es von meiner Initialize Methode nennen könnte, so etwas wie diese:

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' 
    } 
} 

Jetzt Ich stelle mir vor, dass man das wohl umgestalten sollte und es in die Application_Start() Methode schreiben sollte, wie Mark vorgeschlagen hat, aber man bekommt die Idee :). Ich wollte nur meine Erfahrung erklären, nur für den Fall, dass irgendjemand etwas Ähnliches in der vom Controller überschriebenen Initialize Methode tut.