Resilienza della connessione e intercettazione dei comandi di Web Forms ASP.NET
di Erik Reitan
In questa esercitazione si modificherà l'applicazione di esempio Wingtip Toys per supportare la resilienza della connessione e l'intercettazione dei comandi. Abilitando la resilienza della connessione, l'applicazione di esempio Wingtip Toys ritenta automaticamente le chiamate ai dati quando si verificano errori temporanei tipici di un ambiente cloud. Implementando anche l'intercettazione dei comandi, l'applicazione di esempio Wingtip Toys intercetta tutte le query SQL inviate al database per registrarle o modificarle.
Nota
Questa esercitazione sui Web Form è basata sull'esercitazione MVC seguente di Tom Dykstra:
Resilienza della connessione e intercettazione dei comandi con Entity Framework in un'applicazione MVC ASP.NET
Contenuto dell'esercitazione:
- Come fornire la resilienza della connessione.
- Come implementare l'intercettazione dei comandi.
Prerequisiti
Prima di iniziare, assicurarsi di avere installato il software seguente nel computer:
Microsoft Visual Studio 2013 o Microsoft Visual Studio Express 2013 for Web. .NET Framework viene installato automaticamente.
Il progetto di esempio Wingtip Toys, in modo da poter implementare la funzionalità descritta in questa esercitazione all'interno del progetto Wingtip Toys. Il collegamento seguente fornisce i dettagli del download:
Prima di completare questa esercitazione, è consigliabile esaminare la serie di esercitazioni correlate, Introduzione a ASP.NET Web Form 4.5 e Visual Studio 2013. La serie di esercitazioni consentirà di acquisire familiarità con il progetto e il codice WingtipToys .
Resilienza della connessione
Quando si valuta la possibilità di distribuire un'applicazione in Windows Azure, è consigliabile distribuire il database in Windows database SQL di Azure, un servizio di database cloud. Gli errori di connessione temporanei sono in genere più frequenti quando ci si connette a un servizio di database cloud rispetto a quando il server Web e il server di database sono connessi direttamente nello stesso data center. Anche se un server Web cloud e un servizio di database cloud sono ospitati nello stesso data center, esistono più connessioni di rete tra di esse che possono avere problemi, ad esempio i servizi di bilanciamento del carico.
Inoltre, un servizio cloud viene in genere condiviso da altri utenti, il che significa che la velocità di risposta può essere influenzata da essi. E l'accesso al database potrebbe essere soggetto a limitazioni. La limitazione indica che il servizio di database genera eccezioni quando si tenta di accedervi più frequentemente di quanto sia consentito nel contratto di servizio.
Molti o la maggior parte dei problemi di connessione che si verificano quando si accede a un servizio cloud sono temporanei, ovvero si risolvono in un breve periodo di tempo. Pertanto, quando si tenta un'operazione di database e si ottiene un tipo di errore in genere temporaneo, è possibile ritentare l'operazione dopo un breve attesa e l'operazione potrebbe avere esito positivo. È possibile offrire agli utenti un'esperienza molto migliore se si gestiscono gli errori temporanei riprovando automaticamente, rendendo la maggior parte invisibili al cliente. La funzionalità di resilienza della connessione in Entity Framework 6 automatizza il processo di ripetizione dei tentativi di query SQL non riuscite.
La funzionalità di resilienza della connessione deve essere configurata in modo appropriato per un servizio di database specifico:
- Deve sapere quali eccezioni sono probabilmente temporanee. Si desidera ripetere gli errori causati da una perdita temporanea nella connettività di rete, non da errori causati da bug del programma, ad esempio.
- Deve attendere un intervallo di tempo appropriato tra i tentativi di un'operazione non riuscita. È possibile attendere più tempo tra i tentativi per un processo batch rispetto a una pagina Web online in cui un utente è in attesa di una risposta.
- È necessario riprovare un numero appropriato di volte prima di rinunciare. È possibile riprovare più volte in un processo batch che si farebbe in un'applicazione online.You might want to retry more times in a batch process that you would in an online application.
È possibile configurare queste impostazioni manualmente per qualsiasi database supporto ambientale ed da un provider Entity Framework.
Tutto ciò che occorre fare per abilitare la resilienza della connessione è creare una classe nell'assembly che deriva dalla DbConfiguration
classe e in tale classe impostare la strategia di esecuzione database SQL, che in Entity Framework è un altro termine per i criteri di ripetizione dei tentativi.
Implementazione della resilienza della connessione
Scaricare e aprire l'applicazione Web Form di esempio WingtipToys in Visual Studio.
Nella cartella Logic dell'applicazione WingtipToys aggiungere un file di classe denominato WingtipToysConfiguration.cs.
Sostituire il codice esistente con quello riportato di seguito:
using System.Data.Entity; using System.Data.Entity.SqlServer; namespace WingtipToys.Logic { public class WingtipToysConfiguration : DbConfiguration { public WingtipToysConfiguration() { SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy()); } } }
Entity Framework esegue automaticamente il codice trovato in una classe che deriva da DbConfiguration
. È possibile usare la DbConfiguration
classe per eseguire attività di configurazione nel codice che altrimenti si farebbe nel file Web.config . Per altre informazioni, vedere Configurazione basata su codice EntityFramework.
Nella cartella Logica aprire il file AddProducts.cs.
Aggiungere un'istruzione
using
per comeSystem.Data.Entity.Infrastructure
illustrato in giallo:using System; using System.Collections.Generic; using System.Linq; using System.Web; using WingtipToys.Models; using System.Data.Entity.Infrastructure;
Aggiungere un
catch
blocco alAddProduct
metodo in modo che vengaRetryLimitExceededException
registrato come evidenziato in giallo:public bool AddProduct(string ProductName, string ProductDesc, string ProductPrice, string ProductCategory, string ProductImagePath) { var myProduct = new Product(); myProduct.ProductName = ProductName; myProduct.Description = ProductDesc; myProduct.UnitPrice = Convert.ToDouble(ProductPrice); myProduct.ImagePath = ProductImagePath; myProduct.CategoryID = Convert.ToInt32(ProductCategory); using (ProductContext _db = new ProductContext()) { // Add product to DB. _db.Products.Add(myProduct); try { _db.SaveChanges(); } catch (RetryLimitExceededException ex) { // Log the RetryLimitExceededException. WingtipToys.Logic.ExceptionUtility.LogException(ex, "Error: RetryLimitExceededException -> RemoveProductButton_Click in AdminPage.aspx.cs"); } } // Success. return true; }
Aggiungendo l'eccezione RetryLimitExceededException
, è possibile fornire una registrazione migliore o visualizzare un messaggio di errore all'utente in cui è possibile scegliere di ritentare il processo. Intercettando l'eccezione RetryLimitExceededException
, gli unici errori probabilmente saranno già stati provati e non riusciti più volte. L'eccezione effettiva restituita verrà sottoposta a wrapping nell'eccezione RetryLimitExceededException
. È stato inoltre aggiunto un blocco catch generale. Per altre informazioni sull'eccezione, vedere Entity Framework Connection Resiliency/Retry Logic.For more information about the RetryLimitExceededException
exception, see Entity Framework Connection Resiliency/Retry Logic.
Intercettazione dei comandi
Ora che è stato attivato un criterio di ripetizione dei tentativi, come si esegue il test per verificare che funzioni come previsto? Non è così facile forzare l'esecuzione di un errore temporaneo, soprattutto quando si esegue localmente e sarebbe particolarmente difficile integrare errori temporanei effettivi in uno unit test automatizzato. Per testare la funzionalità di resilienza della connessione, è necessario un modo per intercettare le query inviate da Entity Framework a SQL Server e sostituire la risposta di SQL Server con un tipo di eccezione in genere temporaneo.
È anche possibile usare l'intercettazione di query per implementare una procedura consigliata per le applicazioni cloud: registrare la latenza e l'esito positivo o negativo di tutte le chiamate a servizi esterni, ad esempio i servizi di database.
In questa sezione dell'esercitazione si userà la funzionalità di intercettazione di Entity Framework sia per la registrazione che per la simulazione di errori temporanei.
Creare un'interfaccia di registrazione e una classe
Una procedura consigliata per la registrazione consiste nell'usare una interface
classe di registrazione anziché una chiamata hardcoded a System.Diagnostics.Trace
o a una classe di registrazione. In questo modo è più semplice modificare il meccanismo di registrazione in un secondo momento, se necessario. In questa sezione si creeranno quindi l'interfaccia di registrazione e una classe per implementarla.
In base alla procedura precedente, è stata scaricata e aperta l'applicazione di esempio WingtipToys in Visual Studio.
Creare una cartella nel progetto WingtipToys e denominarla Logging.
Nella cartella Logging creare un file di classe denominato ILogger.cs e sostituire il codice predefinito con il codice seguente:
using System; namespace WingtipToys.Logging { public interface ILogger { void Information(string message); void Information(string fmt, params object[] vars); void Information(Exception exception, string fmt, params object[] vars); void Warning(string message); void Warning(string fmt, params object[] vars); void Warning(Exception exception, string fmt, params object[] vars); void Error(string message); void Error(string fmt, params object[] vars); void Error(Exception exception, string fmt, params object[] vars); void TraceApi(string componentName, string method, TimeSpan timespan); void TraceApi(string componentName, string method, TimeSpan timespan, string properties); void TraceApi(string componentName, string method, TimeSpan timespan, string fmt, params object[] vars); } }
L'interfaccia fornisce tre livelli di traccia per indicare l'importanza relativa dei log e uno progettato per fornire informazioni sulla latenza per le chiamate al servizio esterno, ad esempio le query di database. I metodi di registrazione hanno overload che consentono di passare un'eccezione. In questo modo, le informazioni sulle eccezioni, incluse l'analisi dello stack e le eccezioni interne, vengono registrate in modo affidabile dalla classe che implementa l'interfaccia, anziché basarsi su tale operazione eseguita in ogni chiamata al metodo di registrazione in tutta l'applicazione.
I
TraceApi
metodi consentono di tenere traccia della latenza di ogni chiamata a un servizio esterno, ad esempio database SQL.Nella cartella Logging creare un file di classe denominato Logger.cs e sostituire il codice predefinito con il codice seguente:
using System; using System.Diagnostics; using System.Text; namespace WingtipToys.Logging { public class Logger : ILogger { public void Information(string message) { Trace.TraceInformation(message); } public void Information(string fmt, params object[] vars) { Trace.TraceInformation(fmt, vars); } public void Information(Exception exception, string fmt, params object[] vars) { Trace.TraceInformation(FormatExceptionMessage(exception, fmt, vars)); } public void Warning(string message) { Trace.TraceWarning(message); } public void Warning(string fmt, params object[] vars) { Trace.TraceWarning(fmt, vars); } public void Warning(Exception exception, string fmt, params object[] vars) { Trace.TraceWarning(FormatExceptionMessage(exception, fmt, vars)); } public void Error(string message) { Trace.TraceError(message); } public void Error(string fmt, params object[] vars) { Trace.TraceError(fmt, vars); } public void Error(Exception exception, string fmt, params object[] vars) { Trace.TraceError(FormatExceptionMessage(exception, fmt, vars)); } public void TraceApi(string componentName, string method, TimeSpan timespan) { TraceApi(componentName, method, timespan, ""); } public void TraceApi(string componentName, string method, TimeSpan timespan, string fmt, params object[] vars) { TraceApi(componentName, method, timespan, string.Format(fmt, vars)); } public void TraceApi(string componentName, string method, TimeSpan timespan, string properties) { string message = String.Concat("Component:", componentName, ";Method:", method, ";Timespan:", timespan.ToString(), ";Properties:", properties); Trace.TraceInformation(message); } private static string FormatExceptionMessage(Exception exception, string fmt, object[] vars) { var sb = new StringBuilder(); sb.Append(string.Format(fmt, vars)); sb.Append(" Exception: "); sb.Append(exception.ToString()); return sb.ToString(); } } }
L'implementazione usa System.Diagnostics
per eseguire la traccia. Si tratta di una funzionalità predefinita di .NET che semplifica la generazione e l'uso delle informazioni di traccia. Esistono molti "listener" che è possibile usare con System.Diagnostics
la traccia, per scrivere log nei file, ad esempio o per scriverli nell'archivio BLOB in Windows Azure. Per altre informazioni, vedere alcune delle opzioni e collegamenti ad altre risorse in Risoluzione dei problemi relativi ai siti Web di Windows Azure in Visual Studio. Per questa esercitazione verranno esaminati solo i log nella finestra Output di Visual Studio.
In un'applicazione di produzione è consigliabile prendere in considerazione l'uso di framework di traccia diversi da System.Diagnostics
e l'interfaccia ILogger
rende relativamente semplice passare a un meccanismo di traccia diverso se si decide di farlo.
Creare classi di intercettori
Successivamente, si creeranno le classi in cui Entity Framework chiamerà ogni volta che invierà una query al database, una per simulare gli errori temporanei e una per eseguire la registrazione. Queste classi intercettore devono derivare dalla DbCommandInterceptor
classe . In essi, si scrivono override del metodo che vengono chiamati automaticamente quando la query sta per essere eseguita. In questi metodi è possibile esaminare o registrare la query inviata al database ed è possibile modificare la query prima che venga inviata al database o restituire un elemento a Entity Framework senza passare la query al database.
Per creare la classe dell'intercettore che registra ogni query SQL prima dell'invio al database, creare un file di classe denominato InterceptorLogging.cs nella cartella Logica e sostituire il codice predefinito con il codice seguente:
using System; using System.Data.Common; using System.Data.Entity; using System.Data.Entity.Infrastructure.Interception; using System.Data.Entity.SqlServer; using System.Data.SqlClient; using System.Diagnostics; using System.Reflection; using System.Linq; using WingtipToys.Logging; namespace WingtipToys.Logic { public class InterceptorLogging : DbCommandInterceptor { private ILogger _logger = new Logger(); private readonly Stopwatch _stopwatch = new Stopwatch(); public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { base.ScalarExecuting(command, interceptionContext); _stopwatch.Restart(); } public override void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { _stopwatch.Stop(); if (interceptionContext.Exception != null) { _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText); } else { _logger.TraceApi("SQL Database", "Interceptor.ScalarExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText); } base.ScalarExecuted(command, interceptionContext); } public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) { base.NonQueryExecuting(command, interceptionContext); _stopwatch.Restart(); } public override void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) { _stopwatch.Stop(); if (interceptionContext.Exception != null) { _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText); } else { _logger.TraceApi("SQL Database", "Interceptor.NonQueryExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText); } base.NonQueryExecuted(command, interceptionContext); } public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { base.ReaderExecuting(command, interceptionContext); _stopwatch.Restart(); } public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { _stopwatch.Stop(); if (interceptionContext.Exception != null) { _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText); } else { _logger.TraceApi("SQL Database", "Interceptor.ReaderExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText); } base.ReaderExecuted(command, interceptionContext); } } }
Per le query o i comandi riusciti, questo codice scrive un log delle informazioni con informazioni sulla latenza. Per le eccezioni, viene creato un log degli errori.
Per creare la classe intercettore che genererà errori temporanei fittizi quando si immette "Throw" nella casella di testo Name (Genera ) nella pagina denominata AdminPage.aspx, creare un file di classe denominato InterceptorTransientErrors.cs nella cartella Logic e sostituire il codice predefinito con il codice seguente:
using System; using System.Data.Common; using System.Data.Entity; using System.Data.Entity.Infrastructure.Interception; using System.Data.Entity.SqlServer; using System.Data.SqlClient; using System.Diagnostics; using System.Reflection; using System.Linq; using WingtipToys.Logging; namespace WingtipToys.Logic { public class InterceptorTransientErrors : DbCommandInterceptor { private int _counter = 0; private ILogger _logger = new Logger(); public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { bool throwTransientErrors = false; if (command.Parameters.Count > 0 && command.Parameters[0].Value.ToString() == "Throw") { throwTransientErrors = true; command.Parameters[0].Value = "TransientErrorExample"; command.Parameters[1].Value = "TransientErrorExample"; } if (throwTransientErrors && _counter < 4) { _logger.Information("Returning transient error for command: {0}", command.CommandText); _counter++; interceptionContext.Exception = CreateDummySqlException(); } } private SqlException CreateDummySqlException() { // The instance of SQL Server you attempted to connect to does not support encryption var sqlErrorNumber = 20; var sqlErrorCtor = typeof(SqlError).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Where(c => c.GetParameters().Count() == 7).Single(); var sqlError = sqlErrorCtor.Invoke(new object[] { sqlErrorNumber, (byte)0, (byte)0, "", "", "", 1 }); var errorCollection = Activator.CreateInstance(typeof(SqlErrorCollection), true); var addMethod = typeof(SqlErrorCollection).GetMethod("Add", BindingFlags.Instance | BindingFlags.NonPublic); addMethod.Invoke(errorCollection, new[] { sqlError }); var sqlExceptionCtor = typeof(SqlException).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Where(c => c.GetParameters().Count() == 4).Single(); var sqlException = (SqlException)sqlExceptionCtor.Invoke(new object[] { "Dummy", errorCollection, null, Guid.NewGuid() }); return sqlException; } } }
Questo codice esegue solo l'override del
ReaderExecuting
metodo , chiamato per le query che possono restituire più righe di dati. Se si vuole controllare la resilienza della connessione per altri tipi di query, è anche possibile eseguire l'override deiNonQueryExecuting
metodi eScalarExecuting
, come avviee con l'intercettore di registrazione.Successivamente, si accederà come "Amministratore" e si selezionerà il collegamento Amministratore nella barra di spostamento superiore. Quindi, nella pagina AdminPage.aspx si aggiungerà un prodotto denominato "Throw". Il codice crea un'eccezione fittizia database SQL per il numero di errore 20, un tipo noto per essere in genere temporaneo. Altri numeri di errore attualmente riconosciuti come temporanei sono 64, 233, 10053, 10054, 10060, 10928, 10929, 40197, 40501 e 40613, ma sono soggetti a modifiche nelle nuove versioni di database SQL. Il prodotto verrà rinominato in "TransientErrorExample", che è possibile seguire nel codice del file InterceptorTransientErrors.cs .
Il codice restituisce l'eccezione a Entity Framework anziché eseguire la query e passare i risultati. L'eccezione temporanea viene restituita quattro volte e il codice viene ripristinato alla normale procedura di passaggio della query al database.
Poiché tutti gli elementi vengono registrati, si noterà che Entity Framework tenta di eseguire la query quattro volte prima dell'esito positivo e l'unica differenza nell'applicazione è che il rendering di una pagina con risultati della query richiede più tempo.
Il numero di tentativi che Entity Framework ritenta è configurabile; il codice specifica quattro volte perché questo è il valore predefinito per il criterio di esecuzione database SQL. Se si modificano i criteri di esecuzione, è necessario modificare anche il codice qui che specifica il numero di volte in cui vengono generati errori temporanei. È anche possibile modificare il codice per generare più eccezioni in modo che Entity Framework generi l'eccezione
RetryLimitExceededException
.In Global.asax aggiungere le istruzioni using seguenti:
using System.Data.Entity.Infrastructure.Interception;
Aggiungere quindi le righe evidenziate al
Application_Start
metodo :void Application_Start(object sender, EventArgs e) { // Code that runs on application startup RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); // Initialize the product database. Database.SetInitializer(new ProductDatabaseInitializer()); // Create administrator role and user. RoleActions roleActions = new RoleActions(); roleActions.createAdmin(); // Add Routes. RegisterRoutes(RouteTable.Routes); // Logging. DbInterception.Add(new InterceptorTransientErrors()); DbInterception.Add(new InterceptorLogging()); }
Queste righe di codice determinano l'esecuzione del codice dell'intercettore quando Entity Framework invia query al database. Si noti che, poiché sono state create classi di intercettore separate per la simulazione e la registrazione degli errori temporanei, è possibile abilitarle e disabilitarle in modo indipendente.
È possibile aggiungere intercettori usando il DbInterception.Add
metodo in qualsiasi punto del codice. Non è necessario che si trovi nel Application_Start
metodo . Un'altra opzione, se non sono stati aggiunti intercettori nel Application_Start
metodo , consiste nell'aggiornare o aggiungere la classe denominata WingtipToysConfiguration.cs e inserire il codice precedente alla fine del costruttore della WingtipToysConfiguration
classe .
Ovunque si inserisca questo codice, prestare attenzione a non eseguire DbInterception.Add
per lo stesso intercettore più volte o si otterranno istanze aggiuntive dell'intercettore. Ad esempio, se si aggiunge due volte l'intercettore di registrazione, verranno visualizzati due log per ogni query SQL.
Gli intercettori vengono eseguiti nell'ordine di registrazione (l'ordine in cui viene chiamato il DbInterception.Add
metodo). L'ordine potrebbe dipendere da ciò che stai facendo nell'intercettatore. Ad esempio, un intercettore potrebbe modificare il comando SQL che ottiene nella CommandText
proprietà . Se cambia il comando SQL, l'intercettore successivo otterrà il comando SQL modificato, non il comando SQL originale.
Il codice di simulazione degli errori temporanei è stato scritto in modo da causare errori temporanei immettendo un valore diverso nell'interfaccia utente. In alternativa, è possibile scrivere il codice dell'intercettore per generare sempre la sequenza di eccezioni temporanee senza verificare la presenza di un determinato valore di parametro. È quindi possibile aggiungere l'intercettore solo quando si desidera generare errori temporanei. In questo caso, tuttavia, non aggiungere l'intercettore fino al completamento dell'inizializzazione del database. In altre parole, eseguire almeno un'operazione di database, ad esempio una query su uno dei set di entità, prima di iniziare a generare errori temporanei. Entity Framework esegue diverse query durante l'inizializzazione del database e non vengono eseguite in una transazione, pertanto gli errori durante l'inizializzazione potrebbero causare l'incoerenza del contesto.
Testare la registrazione e la resilienza delle connessioni
In Visual Studio premere F5 per eseguire l'applicazione in modalità di debug e quindi accedere come "Amministratore" usando "Pa$$word" come password.
Selezionare Admin (Amministratore ) nella barra di spostamento nella parte superiore.
Immettere un nuovo prodotto denominato "Throw" con la descrizione, il prezzo e il file di immagine appropriati.
Premere il pulsante Aggiungi prodotto .
Si noterà che il browser sembra bloccarsi per diversi secondi mentre Entity Framework sta ritentando la query più volte. Il primo tentativo si verifica molto rapidamente, quindi l'attesa aumenta prima di ogni nuovo tentativo aggiuntivo. Questo processo di attesa più lungo prima che ogni nuovo tentativo venga chiamato backoff esponenziale.Attendere che la pagina non tenti più di caricare.
Arrestare il progetto ed esaminare la finestra Output di Visual Studio per visualizzare l'output di traccia. È possibile trovare la finestra Output selezionando Debug ->Windows ->Output. Potrebbe essere necessario scorrere oltre diversi altri log scritti dal logger.
Si noti che è possibile visualizzare le query SQL effettive inviate al database. Vengono visualizzate alcune query e comandi iniziali che Entity Framework esegue per iniziare, controllando la tabella della cronologia della migrazione e della versione del database.
Si noti che non è possibile ripetere questo test a meno che l'applicazione non venga interrotta e riavviata. Se si vuole poter testare la resilienza della connessione più volte in una singola esecuzione dell'applicazione, è possibile scrivere codice per reimpostare il contatore degli errori inInterceptorTransientErrors
.Per vedere la differenza tra la strategia di esecuzione (criteri di ripetizione dei tentativi), impostare come commento la
SetExecutionStrategy
riga nel file WingtipToysConfiguration.cs nella cartella Logica , eseguire di nuovo la pagina Admin in modalità di debug e aggiungere di nuovo il prodotto denominato "Throw".Questa volta il debugger si arresta immediatamente alla prima eccezione generata quando tenta di eseguire la query la prima volta.
Rimuovere il commento dalla
SetExecutionStrategy
riga nel file di WingtipToysConfiguration.cs .
Riepilogo
In questa esercitazione è stato illustrato come modificare un'applicazione di esempio Web Form per supportare la resilienza della connessione e l'intercettazione dei comandi.
Passaggi successivi
Dopo aver esaminato la resilienza della connessione e l'intercettazione dei comandi in Web Forms ASP.NET, esaminare l'argomento Web Forms ASP.NET Metodi asincroni in ASP.NET 4.5. L'argomento illustra le nozioni di base per la creazione di un'applicazione Web Forms ASP.NET asincrona con Visual Studio.