Odporność połączeń wzorca ASP.NET Web Forms i przejmowanie poleceń
Autor: Erik Reitan
W tym samouczku zmodyfikujesz przykładową aplikację Wingtip Toys, aby obsługiwała odporność połączenia i przechwytywanie poleceń. Po włączeniu odporności połączenia przykładowa aplikacja Wingtip Toys automatycznie ponawia próby wywołań danych w przypadku wystąpienia przejściowych błędów typowych dla środowiska chmury. Ponadto przez zaimplementowanie przechwytywania poleceń przykładowa aplikacja Wingtip Toys przechwytuje wszystkie zapytania SQL wysyłane do bazy danych, aby je rejestrować lub zmieniać.
Uwaga
Ten samouczek dotyczący formularzy internetowych został oparty na następującym samouczku MVC firmy Tom Dykstra:
Odporność połączenia i przechwytywanie poleceń za pomocą platformy Entity Framework w aplikacji MVC ASP.NET
Zawartość:
- Jak zapewnić odporność połączenia.
- Jak zaimplementować przechwytywanie poleceń.
Wymagania wstępne
Przed rozpoczęciem upewnij się, że na komputerze jest zainstalowane następujące oprogramowanie:
Microsoft Visual Studio 2013 lub Microsoft Visual Studio Express 2013 for Web. Program .NET Framework jest instalowany automatycznie.
Przykładowy projekt Wingtip Toys, dzięki czemu można zaimplementować funkcje wymienione w tym samouczku w projekcie Wingtip Toys. Poniższy link zawiera szczegóły pobierania:
Przed ukończeniem tego samouczka rozważ przejrzenie powiązanej serii samouczków Wprowadzenie do ASP.NET 4.5 Web Forms i Visual Studio 2013. Seria samouczków pomoże Ci zapoznać się z projektem i kodem WingtipToys .
Odporność połączenia
Jeśli rozważasz wdrożenie aplikacji na platformie Windows Azure, jedną z opcji do rozważenia jest wdrożenie bazy danych w usłudze Bazy danych Azure SQL Database w systemie Windows Azure, czyli usłudze bazy danych w chmurze. Przejściowe błędy połączeń są zwykle częstsze podczas nawiązywania połączenia z usługą bazy danych w chmurze niż wtedy, gdy serwer internetowy i serwer bazy danych są połączone bezpośrednio w tym samym centrum danych. Nawet jeśli serwer internetowy w chmurze i usługa bazy danych w chmurze są hostowane w tym samym centrum danych, istnieje więcej połączeń sieciowych między nimi, które mogą mieć problemy, takie jak moduły równoważenia obciążenia.
Ponadto usługa w chmurze jest zwykle udostępniana przez innych użytkowników, co oznacza, że może to mieć wpływ na jego czas odpowiedzi. Dostęp do bazy danych może podlegać ograniczaniu przepustowości. Ograniczanie oznacza, że usługa bazy danych zgłasza wyjątki, gdy próbujesz uzyskać do niej dostęp częściej niż jest to dozwolone w umowie dotyczącej poziomu usług (SLA).
Wiele lub większość problemów z połączeniem występujących podczas uzyskiwania dostępu do usługi w chmurze jest przejściowych, czyli rozwiązuje się w krótkim czasie. Dlatego podczas próby wykonania operacji bazy danych i otrzymaniu typu błędu, który jest zwykle przejściowy, możesz spróbować wykonać operację ponownie po krótkim oczekiwaniu, a operacja może zakończyć się pomyślnie. Możesz zapewnić użytkownikom znacznie lepsze środowisko obsługi, jeśli będziesz obsługiwać błędy przejściowe, automatycznie próbując ponownie, dzięki czemu większość z nich jest niewidoczna dla klienta. Funkcja odporności połączenia w programie Entity Framework 6 automatyzuje ten proces ponawiania nieudanych zapytań SQL.
Funkcja odporności połączenia musi być odpowiednio skonfigurowana dla określonej usługi bazy danych:
- Musi wiedzieć, które wyjątki mogą być przejściowe. Chcesz ponowić próbę błędów spowodowanych tymczasową utratą łączności sieciowej, a nie błędami spowodowanymi na przykład usterkami programu.
- Musi odczekać odpowiedni czas między ponowną próbą operacji, która zakończyła się niepowodzeniem. Możesz poczekać dłużej między ponowną próbą dla procesu wsadowego niż w przypadku strony internetowej online, na której użytkownik czeka na odpowiedź.
- Musi ponowić próbę odpowiedniej liczby razy, zanim się podniesie. Możesz chcieć ponowić próbę więcej razy w procesie wsadowym, który można wykonać w aplikacji online.
Te ustawienia można skonfigurować ręcznie dla dowolnej bazy danych wsparcie środowiska dostawcy programu Entity Framework.
Wystarczy włączyć odporność połączenia, aby utworzyć klasę w zestawie pochodzącym z DbConfiguration
klasy, a w tej klasie ustawić strategię wykonywania usługi SQL Database, która w programie Entity Framework jest kolejnym terminem dla zasad ponawiania prób.
Implementowanie odporności połączenia
Pobierz i otwórz przykładową aplikację Web Forms WingtipToys w programie Visual Studio.
W folderze Logic aplikacji WingtipToys dodaj plik klasy o nazwie WingtipToysConfiguration.cs.
Zastąp istniejący kod poniższym kodem:
using System.Data.Entity; using System.Data.Entity.SqlServer; namespace WingtipToys.Logic { public class WingtipToysConfiguration : DbConfiguration { public WingtipToysConfiguration() { SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy()); } } }
Program Entity Framework automatycznie uruchamia kod, który znajduje w klasie pochodzącej z klasy DbConfiguration
. Za pomocą DbConfiguration
klasy można wykonywać zadania konfiguracyjne w kodzie, który w przeciwnym razie można wykonać w pliku Web.config . Aby uzyskać więcej informacji, zobacz EntityFramework Code-Based Configuration (Konfiguracja oparta na kodzie elementu EntityFramework).
W folderze Logika otwórz plik AddProducts.cs.
Dodaj instrukcję
using
dla elementuSystem.Data.Entity.Infrastructure
, jak pokazano na żółtym:using System; using System.Collections.Generic; using System.Linq; using System.Web; using WingtipToys.Models; using System.Data.Entity.Infrastructure;
catch
Dodaj blok doAddProduct
metody , abyRetryLimitExceededException
element był rejestrowany jako wyróżniony w kolorze żółtym: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; }
RetryLimitExceededException
Dodając wyjątek, możesz zapewnić lepsze rejestrowanie lub wyświetlić użytkownikowi komunikat o błędzie, w którym można spróbować ponownie wykonać proces. Przechwycąc RetryLimitExceededException
wyjątek, jedyne błędy, które prawdopodobnie będą przejściowe, zostały już wypróbowane i nie powiodły się kilka razy. Rzeczywisty zwrócony wyjątek zostanie opakowany w RetryLimitExceededException
wyjątek. Ponadto dodano również ogólny blok catch. Aby uzyskać więcej informacji na temat wyjątku RetryLimitExceededException
, zobacz Entity Framework Connection Resiliency / Retry Logic (Odporność połączenia programu Entity Framework/ Logika ponawiania prób).
Przechwytywanie poleceń
Po włączeniu zasad ponawiania prób możesz sprawdzić, czy działa ona zgodnie z oczekiwaniami? Nie jest tak łatwo wymusić wystąpienie błędu przejściowego, zwłaszcza w przypadku uruchamiania lokalnego i byłoby szczególnie trudne do zintegrowania rzeczywistych błędów przejściowych z automatycznym testem jednostkowym. Aby przetestować funkcję odporności połączenia, potrzebny jest sposób przechwytywania zapytań wysyłanych przez program Entity Framework do programu SQL Server i zastępowania odpowiedzi programu SQL Server typem wyjątku, który jest zwykle przejściowy.
Możesz również użyć przechwytywania zapytań w celu zaimplementowania najlepszych rozwiązań dla aplikacji w chmurze: rejestrowania opóźnienia i powodzenia lub niepowodzenia wszystkich wywołań usług zewnętrznych, takich jak usługi bazy danych.
W tej sekcji samouczka użyjesz funkcji przechwytywania programu Entity Framework zarówno do rejestrowania, jak i symulowania błędów przejściowych.
Tworzenie interfejsu rejestrowania i klasy
Najlepszym rozwiązaniem w przypadku rejestrowania jest wykonanie tej czynności przy użyciu wywołań interface
zamiast kodowania na stałe lub System.Diagnostics.Trace
klasy rejestrowania. Ułatwia to zmianę mechanizmu rejestrowania później, jeśli kiedykolwiek będzie to konieczne. W tej sekcji utworzysz interfejs rejestrowania i klasę, aby ją zaimplementować.
Na podstawie powyższej procedury pobrano i otwarto przykładową aplikację WingtipToys w programie Visual Studio.
Utwórz folder w projekcie WingtipToys i nadaj mu nazwę Rejestrowanie.
W folderze Logging utwórz plik klasy o nazwie ILogger.cs i zastąp domyślny kod następującym kodem:
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); } }
Interfejs udostępnia trzy poziomy śledzenia, aby wskazać względną ważność dzienników i jeden zaprojektowany w celu zapewnienia informacji o opóźnieniach dla wywołań usług zewnętrznych, takich jak zapytania bazy danych. Metody rejestrowania mają przeciążenia, które umożliwiają przekazanie wyjątku. Dzięki temu informacje o wyjątkach, w tym ślad stosu i wyjątki wewnętrzne, są niezawodnie rejestrowane przez klasę, która implementuje interfejs, zamiast polegać na tym, że odbywa się w każdym wywołaniu metody rejestrowania w całej aplikacji.
Metody
TraceApi
umożliwiają śledzenie opóźnienia każdego wywołania do usługi zewnętrznej, takiej jak SQL Database.W folderze Logging utwórz plik klasy o nazwie Logger.cs i zastąp domyślny kod następującym kodem:
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(); } } }
Implementacja używa System.Diagnostics
metody do śledzenia. Jest to wbudowana funkcja platformy .NET, która ułatwia generowanie i używanie informacji śledzenia. Istnieje wiele "odbiorników", których można używać ze śledzeniem System.Diagnostics
, aby zapisywać dzienniki w plikach, na przykład lub zapisywać je w magazynie obiektów blob na platformie Windows Azure. Zobacz niektóre opcje i linki do innych zasobów, aby uzyskać więcej informacji w temacie Rozwiązywanie problemów z witrynami sieci Web platformy Windows Azure w programie Visual Studio. W tym samouczku zapoznasz się tylko z dziennikami w oknie Dane wyjściowe programu Visual Studio.
W aplikacji produkcyjnej warto rozważyć użycie platform śledzenia innych niż System.Diagnostics
, a ILogger
interfejs sprawia, że stosunkowo łatwo przełączać się do innego mechanizmu śledzenia, jeśli zdecydujesz się to zrobić.
Tworzenie klas przechwytywania
Następnie utworzysz klasy, do których program Entity Framework będzie wywoływany za każdym razem, gdy będzie wysyłać zapytanie do bazy danych, jeden do symulowania błędów przejściowych i jeden do rejestrowania. Te klasy przechwytywania muszą pochodzić z DbCommandInterceptor
klasy . W nich piszesz przesłonięcia metod, które są automatycznie wywoływane, gdy zapytanie ma zostać wykonane. W tych metodach można zbadać lub zarejestrować zapytanie wysyłane do bazy danych, a zapytanie można zmienić przed wysłaniem go do bazy danych lub samodzielnie zwrócić coś do programu Entity Framework bez nawet przekazania zapytania do bazy danych.
Aby utworzyć klasę przechwytywania, która będzie rejestrować każde zapytanie SQL przed wysłaniem do bazy danych, utwórz plik klasy o nazwie InterceptorLogging.cs w folderze logiki i zastąp domyślny kod następującym kodem:
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); } } }
W przypadku pomyślnych zapytań lub poleceń ten kod zapisuje dziennik informacji z informacjami o opóźnieniu. W przypadku wyjątków tworzy on dziennik błędów.
Aby utworzyć klasę przechwytywania, która będzie generować fikcyjne błędy przejściowe po wprowadzeniu ciągu "Throw" w polu tekstowym Nazwa na stronie o nazwie AdminPage.aspx, utwórz plik klasy o nazwie InterceptorTransientErrors.cs w folderze logiki i zastąp domyślny kod następującym kodem:
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; } } }
Ten kod zastępuje tylko metodę
ReaderExecuting
, która jest wywoływana w przypadku zapytań, które mogą zwracać wiele wierszy danych. Jeśli chcesz sprawdzić odporność połączenia dla innych typów zapytań, możesz również zastąpićNonQueryExecuting
metody iScalarExecuting
, tak jak przechwytnik rejestrowania.Później zalogujesz się jako "Administrator" i wybierz link Administrator na górnym pasku nawigacyjnym. Następnie na stronie AdminPage.aspx dodasz produkt o nazwie "Throw". Kod tworzy fikcyjny wyjątek usługi SQL Database dla błędu numer 20, typ znany zazwyczaj jako przejściowy. Inne numery błędów, które są obecnie rozpoznawane jako przejściowe, to 64, 233, 10053, 10054, 10060, 10928, 10929, 40197, 40501 i 40613, ale mogą ulec zmianie w nowych wersjach usługi SQL Database. Nazwa produktu zostanie zmieniona na "TransientErrorExample", którą można wykonać w kodzie pliku InterceptorTransientErrors.cs .
Kod zwraca wyjątek do programu Entity Framework zamiast uruchamiać zapytanie i przekazywać wyniki wstecz. Wyjątek przejściowy jest zwracany cztery razy, a następnie kod wraca do normalnej procedury przekazywania zapytania do bazy danych.
Ponieważ wszystko jest rejestrowane, będzie można zobaczyć, że program Entity Framework próbuje wykonać zapytanie cztery razy przed powodzeniem, a jedyną różnicą w aplikacji jest to, że renderowanie strony z wynikami zapytania trwa dłużej.
Liczba ponownych prób programu Entity Framework jest możliwa do skonfigurowania; kod określa cztery razy, ponieważ jest to wartość domyślna zasad wykonywania usługi SQL Database. Jeśli zmienisz zasady wykonywania, należy również zmienić kod, który określa, ile razy są generowane błędy przejściowe. Możesz również zmienić kod, aby wygenerować więcej wyjątków, aby program Entity Framework zgłosił
RetryLimitExceededException
wyjątek.W pliku Global.asax dodaj następujące instrukcje using:
using System.Data.Entity.Infrastructure.Interception;
Następnie dodaj wyróżnione wiersze do
Application_Start
metody :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()); }
Te wiersze kodu są przyczyną uruchomienia kodu przechwytywania, gdy program Entity Framework wysyła zapytania do bazy danych. Zwróć uwagę, że ponieważ utworzono oddzielne klasy przechwytywania na potrzeby symulacji błędów przejściowych i rejestrowania, można je niezależnie włączać i wyłączać.
Można dodawać przechwytniki przy użyciu DbInterception.Add
metody w dowolnym miejscu w kodzie; nie musi być w metodzie Application_Start
. Inną opcją, jeśli nie dodano przechwytów w Application_Start
metodzie, byłoby zaktualizowanie lub dodanie klasy o nazwie WingtipToysConfiguration.cs i umieszczenie powyższego kodu na końcu konstruktora WingtipToysConfiguration
klasy.
Wszędzie tam, gdzie umieścisz ten kod, należy zachować ostrożność, aby nie wykonywać DbInterception.Add
tego samego przechwytywania więcej niż raz, lub uzyskasz dodatkowe wystąpienia przechwytywania. Na przykład w przypadku dwukrotnego dodania przechwytywania rejestrowania zobaczysz dwa dzienniki dla każdego zapytania SQL.
Przechwytniki są wykonywane w kolejności rejestracji (kolejności, w której DbInterception.Add
wywoływana jest metoda). Kolejność może mieć znaczenie w zależności od tego, co robisz w przechwytywaniu. Na przykład przechwytujący może zmienić polecenie SQL, które pobiera we CommandText
właściwości . Jeśli zmieni polecenie SQL, następny przechwytujący otrzyma zmienione polecenie SQL, a nie oryginalne polecenie SQL.
Kod symulacji błędu przejściowego został napisany w sposób umożliwiający wywołanie błędów przejściowych przez wprowadzenie innej wartości w interfejsie użytkownika. Alternatywnie można napisać kod przechwytywania, aby zawsze generować sekwencję wyjątków przejściowych bez sprawdzania określonej wartości parametru. Następnie można dodać przechwytywanie tylko wtedy, gdy chcesz wygenerować błędy przejściowe. Jeśli jednak to zrobisz, nie należy dodawać przechwytywania do momentu ukończenia inicjowania bazy danych. Innymi słowy, przed rozpoczęciem generowania błędów przejściowych wykonaj co najmniej jedną operację bazy danych, taką jak zapytanie w jednym z zestawów jednostek. Program Entity Framework wykonuje kilka zapytań podczas inicjowania bazy danych i nie są one wykonywane w transakcji, więc błędy podczas inicjowania mogą spowodować, że kontekst staje się niespójny.
Testowanie rejestrowania i odporności połączenia
W programie Visual Studio naciśnij F5 , aby uruchomić aplikację w trybie debugowania, a następnie zaloguj się jako "Administrator" przy użyciu hasła "Pa$$word".
Wybierz pozycję Administrator na pasku nawigacyjnym u góry.
Wprowadź nowy produkt o nazwie "Throw" z odpowiednim opisem, ceną i plikiem obrazu.
Naciśnij przycisk Dodaj produkt.
Zauważysz, że przeglądarka wydaje się zawieszać się przez kilka sekund, podczas gdy program Entity Framework ponawia próbę zapytania kilka razy. Pierwsze ponawianie jest wykonywane bardzo szybko, a następnie zwiększa się oczekiwanie przed każdym dodatkowym ponawianie próby. Ten proces oczekiwania dłużej przed każdym ponowieniem jest nazywany wycofywaniem wykładniczym .Poczekaj, aż strona nie będzie już próbowała załadować.
Zatrzymaj projekt i przyjrzyj się oknie Dane wyjściowe programu Visual Studio, aby wyświetlić dane wyjściowe śledzenia. Okno Dane wyjściowe można znaleźć, wybierając pozycję Debuguj ->Windows ->Output. Może być konieczne przewinięcie kilku innych dzienników zapisanych przez rejestrator.
Zwróć uwagę, że rzeczywiste zapytania SQL wysyłane do bazy danych są widoczne. Zobaczysz kilka początkowych zapytań i poleceń, które program Entity Framework wykonuje, aby rozpocząć pracę, sprawdzając tabelę wersji bazy danych i historii migracji.
Należy pamiętać, że nie można powtórzyć tego testu, chyba że zatrzymasz aplikację i uruchom ją ponownie. Jeśli chcesz mieć możliwość wielokrotnego testowania odporności połączenia w jednym uruchomieniu aplikacji, możesz napisać kod, aby zresetować licznik błędów w plikuInterceptorTransientErrors
.Aby zobaczyć różnicę w strategii wykonywania (zasady ponawiania), oznacz jako komentarz
SetExecutionStrategy
wiersz w pliku WingtipToysConfiguration.cs w folderze logiki, ponownie uruchom stronę Administratora w trybie debugowania i dodaj produkt o nazwie "Throw".Tym razem debuger zatrzymuje się od pierwszego wygenerowanego wyjątku natychmiast, gdy próbuje wykonać zapytanie po raz pierwszy.
Usuń komentarz z
SetExecutionStrategy
wiersza w pliku WingtipToysConfiguration.cs .
Podsumowanie
W tym samouczku pokazano, jak zmodyfikować przykładową aplikację web forms w celu obsługi odporności połączenia i przechwytywania poleceń.
Następne kroki
Po przejrzeniu odporności połączenia i przechwycenia poleceń w ASP.NET Web Forms zapoznaj się z tematem ASP.NET Web Forms Asynchronous Methods in ASP.NET 4.5 (Metody asynchroniczne w ASP.NET 4.5). W tym temacie przedstawiono podstawy tworzenia asynchronicznej aplikacji ASP.NET Web Forms przy użyciu programu Visual Studio.