Sto lottando con EntityFramework 6 e un'architettura PaaS. Ho un progetto di repository che chiama un progetto DAL per eseguire alcune stored procedure importate da EF6. Fino a poco tempo fa stavamo per un'architettura IaaS, ma per qualche motivo siamo passati a PaaS. Questo stesso repository è stato utilizzato con successo da un servizio WCF. Questo servizio WCF è stato convertito in un ruolo Web e funziona come un fascino. Ora utilizzo lo stesso repository in Worker Role per de-accodare un bus di servizio e elaborare i dati (en-queued dal ruolo Web). Ma poi ho ricevuto l'errore durante l'utilizzo del repository durante la mia prima chiamata a una stored procedure tramite EF6 (una richiesta Get)
Schedule worker error with inner exception : 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: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server) The underlying provider failed on Open.
at System.Data.Entity.Core.EntityClient.EntityConnection.Open()
at System.Data.Entity.Core.Objects.ObjectContext.EnsureConnection()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass45`1.b__43()
at System.Data.Entity.Infrastructure.DbExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteFunction[TElement](String functionName, ExecutionOptions executionOptions, ObjectParameter[] parameters)
at XXX.DBContext.XXXEntities.GetTrades(Nullable`1 id, Nullable`1 entityBuy, Nullable`1 entitySell, Nullable`1 sessionId, Nullable`1 orderBuy, Nullable`1 orderSell)
at XXX.RepositoryServices.MarketPlaceService.GetTradeInstances(Nullable`1 EntityBuy, Nullable`1 EntitySell, Nullable`1 SessionId, Nullable`1 OrderBuyId, Nullable`1 OrderSellId)
at WorkerRole1.WorkerRole.Run()
(XXX e YYY sono spazi dei nomi ma per motivi di policy non riesco a visualizzarli) Ho provato a impostare un'eccezione firewall per l'azzurro sul Db (ospitato in IaaS) per IP da 0.0.0.0 a 0.0.0.4. Ho aggiunto una classe di configurazione che eredita da DbConfiguration che configura in questo modo in Ctor:
this.SetExecutionStrategy("System.Data.SqlClient", () => SuspendExecutionStrategy
? (IDbExecutionStrategy)new DefaultExecutionStrategy()
:new System.Data.Entity.SqlServer.SqlAzureExecutionStrategy(5,TimeSpan.FromMilliseconds(25)));
(usando SuspendExecutionStrategy = true) Mi sono assicurato che le DLL EntityFramework e EntityFramework.SqlServer della versione corretta venissero copiate in cspkg.
Anche le stringhe delle mie connessioni sono buone (con credenziali all'interno). Sono sicuro dell'ultima parte perché posso utilizzare correttamente le query sql di ADO.NET nel mio ruolo di lavoro, metodo Run e nelle classi che utilizzano questo repository. Ho provato con l'ultima versione di EF6 (cioè 6.1) e non funziona. Ho provato a mettere il mio lavoratore nella stessa sottorete dei ruoli web (non funziona). Ho provato a utilizzare l'indirizzo IP di SqlServer nella stringa di connessione ma non ha funzionato. La stessa stringa di connessione viene utilizzata per ADO.NET ed EF6.
<add name="XXXEntities" connectionString="metadata=res://*/XXXContext.csdl|res://*/XXXContext.ssdl|res://*/XXXConte xt.msl;provider=System.Data.SqlClient;provider connection string="data source=negobdd1.YYY.com;initial catalog=XXX;user id=[User];password= [Password];MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
(mi dispiace per tutta la burocrazia, ma è un requisito del client. anche tutti i XXX sono tutti esattamente la stessa stringa) Ho provato a connettermi dalla macchina virtuale azura che ospita il ruolo di lavoratore per connettersi al db con un file .udl usando il indirizzo "negobdd1.YYY.com" e l'utente e la password della stringa di connessione con successo. Posso eseguire il ping anche sulla macchina SqlServer.
MODIFICARE
il contesto è creato in questo modo
public partial class NegotiationsPlatformEntities : DbContext
{
public NegotiationsPlatformEntities()
: base("name=NegotiationsPlatformEntities")
{
}
// auto-generated methods here
}
con questa instanciazione
internal NegotiationsPlatform.DBContext.NegotiationsPlatformEntities db = new NegotiationsPlatform.DBContext.NegotiationsPlatformEntities();
Non imposto alcun parametro speciale tranne il nome della stringa di connessioni.
RE-EDIT
Dopo aver esaminato DbContext.Database.Connection.Datasource, ho scoperto che apparentemente EF6 sta prendendo di mira il server db di staging locale, non il SqlServer di Azure IaaS. Indagherò e post-back.
Qualsiasi aiuto sarebbe molto apprezzato.
Grazie.
Risulta che i ruoli dei lavoratori non hanno a che fare con il file di stringhe di connessione allo stesso modo di un ruolo web. Poiché esistono diversi ambienti di distribuzione, abbiamo più file denominati "connectionStrings" + [target] + ".config".
All'interno del metodo OnStart di un ruolo Web, utilizziamo un file .bat per eliminare qualsiasi file di configurazione non necessario e rinominare il file desiderato come "connectionStrings.config". In questo modo su una webrole distribuita su Azure, mantiene solo il file di configurazione PaaS e quindi lo usa. Ma a quanto pare non funziona allo stesso modo in un ruolo di lavoratore.
Il file ".bat" di pulizia viene eseguito e lascia solo un file di configurazione con il contenuto giusto, ma ciò che viene utilizzato è ciò che era nel file di configurazione predefinito. Quindi immagino che il lavoratore carica il file di configurazione prima che invochi il metodo OnStart e quindi qualsiasi modifica non avrà importanza. (Non ho provato ad eliminare il processo di lavoro per vedere se dopo un riavvio viene caricato con l'unico file valido rimanente dalla prima distribuzione)
Quindi ecco la mia soluzione: cancellare tutti i file di configurazione nel progetto worker tranne quelli PaaS e non fare affidamento su un file ".bat" utilizzato durante OnStart .
Grazie mille a Dean Ward per avermi messo sulla strada giusta :) Tutto sommato era "solo" questione di stringhe di connessione.