2012-10-18 10 views
9

Ho un sito ASP.NET MVC 4 di base che sto ospitando su siti Web di Azure. L'autenticazione è l'autenticazione dei moduli e non è stata personalizzata dal modello predefinito. Ogni volta che pubblico, quando rivisto il mio sito, si blocca solo con un timeout molto lungo (un paio di minuti forse) prima di mostrarmi finalmente un messaggio di errore. Posso recuperare cancellando i cookie per il sito nel mio browser e ricaricando.Problemi di autenticazione dopo aver pubblicato l'app MVC 4 in azzurro

Inizialmente il problema stava solo cercando di accedere alle pagine che ha richiesto l'autenticazione, ma poi ho aggiunto questo al mio condivisa _Layout.cshtml:

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

che ora significa nessuna pagina a tutti sono accessibili dopo una nuova pubblicare, e così Non riesco nemmeno a fare clic sul link di disconnessione, che era un altro modo in cui ero in grado di risolvere il problema.

Ho configurato qualcosa di sbagliato? Anche se ho una soluzione che posso usare, questa non sarà una buona esperienza per gli utenti del sito dopo aver pubblicato un aggiornamento.

MODIFICA: dai log ELMAH, sembra che l'autenticazione delle form sta tentando di creare un database SQL Express quando chiamo IsInRole. Non riesco a capire perché lo farebbe, poiché l'autenticazione dei miei moduli è impostata per utilizzare il mio database 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) 
+0

Mark Ho fatto riferimento a questa domanda nel mio simile qui ... http://stackoverflow.com/questions/24149044/two-chrome-sessions-on-the-same-machine-one-will-connect -to-our-azure-website e se la tua risposta di seguito funziona, devi davvero spingere il bounty a modo tuo ... – hawbsl

risposta

9

Dopo aver provato diverse decine di suggerimenti da vari post del blog, ho trovato una soluzione. L'aggiunta dell'attributo InitialiseSimpleMembership al mio controller domestico risolve il problema.

[InitializeSimpleMembership] 
public class HomeController : Controller 

Dopo aver apportato questa modifica, sono riuscito a pubblicare numerosi e di successo senza problemi. Ho il sospetto che il motivo è che la seguente riga di codice nel costruttore InitializeSimpleMembershipAttribute deve essere eseguito prima delle chiamate al User.IsInRole sono fatte:

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

Credo che la cosa migliore da fare sarebbe quella di eseguire InitializeSimpleMembership in Application_Start.

+2

Se funziona, è necessario collegare esplicitamente i provider nel file web.config. Vedi http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/ – RickAndMSFT

+0

grazie a @RickAndMSFT, A un certo punto ho cercato di modificare i provider di appartenenze e ruoli nel mio file Web.Config, ma le informazioni su ciò che la sintassi dovrebbe essere era estremamente difficile da ottenere. –

+0

Ho riscontrato un problema simile: il messaggio di errore era Si è verificato un errore relativo alla rete o specifico dell'istanza mentre si stabiliva una connessione a SQL Server. Il server non è stato trovato o non era accessibile. Verificare che il nome dell'istanza sia corretto e che SQL Server sia configurato per consentire le connessioni remote. (fornitore: Interfacce di rete SQL, errore: 26 - Individuazione errore server/istanza specificata). – Will

4

Ecco un tutorial che si potrebbe seguire per l'installazione di un database SQL quando si distribuisce l'applicazione su Windows Azure. È necessario impostare la stringa di connessione corretta nel proprio file web.config che, per impostazione predefinita, punta a un database SQL Express locale quando si crea una nuova applicazione ASP.NET MVC 4 utilizzando il modello Internet.

la stringa di connessione SQL Azure sarà simile a questo:

<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

Grazie, ma non c'è nessun problema con la mia stringa di connessione. Una volta eliminati i cookie nel mio browser, il sito funziona correttamente. Tutti i dati sono lì, ci sono tutti gli accessi e i ruoli degli utenti. È solo il primo accesso dopo una pubblicazione che va storto. –

+0

Bene, allora ho davvero idea di quale potrebbe essere il problema. Prova a seguire il tutorial a cui mi sono collegato. –

+0

sì, l'ho provato e mi ha portato a installare questo: http://nuget.org/packages/Microsoft.AspNet.Providers.LocalDB, ma questo ha rotto tutto, causando l'errore discusso in questo thread http: // social. msdn.microsoft.com/forums/pl-pl/windowsazuredevelopment/thread/d352bb1b-577c-42b7-8872-5ed59cd65f32 –

1

Anche se questa domanda ha avuto risposta, ho pensato che avrei potuto condividere rapidamente le mie esperienze con lo stesso problema. Il mio caso è leggermente diverso da quello di Mark.

Quindi, ho avuto l'attributo InitializeSimpleMembership su tutti i miei controller (in realtà l'ho avuto sul mio controller di base da cui ereditano tutti i controller), tuttavia continuavo a riscontrare lo stesso problema. Ora, nel mio controller di base stavo anche scavalcando il metodo Initialize per configurare alcune informazioni di contesto per la nostra applicazione. Parte di questa impostazione del contesto è per verificare se l'utente corrente è in un ruolo particolare, quindi il metodo IsUserInRole veniva chiamato da questo metodo Initialize, prima che venga chiamato qualsiasi metodo di azione.

Ora, se si prende uno sguardo alla classe InitializeSimpleMembership ci si accorge che l'inizializzazione è in realtà fatto nel metodo OnActionExecuting (cioè InitializeSimpleMembership eredita da 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); 
} 

Nel mio caso, questa inizializzazione appartenenza stava accadendo troppo tardi ... cioè, ho bisogno di inizializzare la Simple Membership prima di chiamare al numero IsUserInRole nel metodo Initialize sovrascritto dal controller di base.

La soluzione per me è stato relativamente semplice: ho rimosso la InitializeSimpleMembership attributo del tutto e mettere la sua logica direttamente nel mio controller di base in modo che potessi chiamare da mio metodo Initialize, qualcosa di simile:

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

Ora Immagino che si dovrebbe probabilmente refactoring questo e metterlo nel metodo Application_Start(), come suggerito Mark, ma si ottiene l'idea :). Volevo solo spiegare la mia esperienza nel caso in cui qualcuno stia facendo qualcosa di simile nel metodo Initialize sostituito dal loro controller.

Problemi correlati