Odporność Połączenie i logika ponawiania prób

Uwaga

Tylko rozwiązanie EF6 i nowsze wersje — Funkcje, interfejsy API itp. omówione na tej stronie zostały wprowadzone w rozwiązaniu Entity Framework 6. Jeśli korzystasz ze starszej wersji, niektóre lub wszystkie podane informacje nie mają zastosowania.

Aplikacje łączące się z serwerem bazy danych zawsze były narażone na przerwy połączeń z powodu awarii zaplecza i niestabilności sieci. Jednak w środowisku opartym na sieci LAN działającym na dedykowanych serwerach baz danych te błędy są na tyle rzadkie, że dodatkowa logika do obsługi tych błędów nie jest często wymagana. Wraz z powstaniem serwerów baz danych opartych na chmurze, takich jak usługa Windows Azure SQL Database i połączenia w mniej niezawodnych sieciach, jest teraz bardziej powszechne w przypadku wystąpienia przerw w połączeniach. Może to być spowodowane technikami defensywnymi używanymi przez bazy danych w chmurze w celu zapewnienia sprawiedliwości usługi, takiej jak ograniczanie połączenia, lub niestabilności sieci powodującej sporadyczne przekroczenia limitu czasu i inne przejściowe błędy.

Połączenie odporność odnosi się do możliwości automatycznego ponawiania prób wykonania wszystkich poleceń, które kończą się niepowodzeniem z powodu tych przerw w połączeniach.

Strategie wykonywania

Połączenie ponawianie próby jest wykonywane przez implementację interfejsu IDbExecutionStrategy. Implementacje IDbExecutionStrategy będą odpowiedzialne za akceptowanie operacji i, jeśli wystąpi wyjątek, określenie, czy ponawianie próby jest odpowiednie i ponawianie próby, jeśli tak jest. Istnieją cztery strategie wykonywania dostarczane z programem EF:

  1. DefaultExecutionStrategy: ta strategia wykonywania nie ponawia próby wykonania żadnych operacji, jest to wartość domyślna dla baz danych innych niż sql server.
  2. DefaultSqlExecutionStrategy: jest to strategia wykonywania wewnętrznego używana domyślnie. Ta strategia w ogóle nie ponawia próby, jednak opakowuje wszelkie wyjątki, które mogą być przejściowe, aby poinformować użytkowników, że mogą chcieć włączyć odporność połączenia.
  3. DbExecutionStrategy: ta klasa jest odpowiednia jako klasa bazowa dla innych strategii wykonywania, w tym własnych niestandardowych. Implementuje ona zasady ponawiania wykładniczego, gdzie początkowe ponawianie ma miejsce z zerowym opóźnieniem, a opóźnienie zwiększa wykładniczo do momentu trafienia maksymalnej liczby ponownych prób. Ta klasa ma abstrakcyjną metodę ShouldRetryOn, którą można zaimplementować w strategiach wykonywania pochodnego w celu kontrolowania, które wyjątki należy ponowić.
  4. SqlAzureExecutionStrategy: ta strategia wykonywania dziedziczy z bazy danych DbExecutionStrategy i ponowi próbę wyjątków, które są prawdopodobnie przejściowe podczas pracy z usługą Azure SQL Database.

Uwaga

Strategie wykonywania 2 i 4 są zawarte w dostawcy programu Sql Server dostarczanym z programem EF, który znajduje się w zestawie EntityFramework.SqlServer i są przeznaczone do pracy z programem SQL Server.

Włączanie strategii wykonywania

Najprostszym sposobem, aby program EF używał strategii wykonywania, jest użycie metody SetExecutionStrategy klasy DbConfiguration :

public class MyConfiguration : DbConfiguration
{
    public MyConfiguration()
    {
        SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
    }
}

Ten kod informuje ef o użyciu programu SqlAzureExecutionStrategy podczas nawiązywania połączenia z programem SQL Server.

Konfigurowanie strategii wykonywania

Konstruktor sqlAzureExecutionStrategy może akceptować dwa parametry: MaxRetryCount i MaxDelay. Maksymalna liczba ponownych prób w strategii to maksymalna liczba ponownych prób. Element MaxDelay to przedział czasu reprezentujący maksymalne opóźnienie między ponownymi próbami, które będą używane przez strategię wykonywania.

Aby ustawić maksymalną liczbę ponownych prób na 1 i maksymalne opóźnienie do 30 sekund, należy wykonać następujące czynności:

public class MyConfiguration : DbConfiguration
{
    public MyConfiguration()
    {
        SetExecutionStrategy(
            "System.Data.SqlClient",
            () => new SqlAzureExecutionStrategy(1, TimeSpan.FromSeconds(30)));
    }
}

SqlAzureExecutionStrategy ponawia próbę natychmiast po raz pierwszy wystąpi błąd przejściowy, ale opóźni się między każdym ponawianiem próby, dopóki limit maksymalnej liczby ponownych prób nie zostanie przekroczony lub łączny czas osiągnie maksymalne opóźnienie.

Strategie wykonywania będą ponawiać tylko ograniczoną liczbę wyjątków, które są zwykle przejściowe, nadal trzeba będzie obsługiwać inne błędy, a także przechwytywać wyjątek RetryLimitExceeded w przypadku, gdy błąd nie jest przejściowy lub trwa zbyt długo, aby rozwiązać problem.

Istnieją pewne znane ograniczenia dotyczące używania strategii ponawiania wykonywania:

Zapytania przesyłane strumieniowo nie są obsługiwane

Domyślnie program EF6 i nowsza wersja będą buforować wyniki zapytań, a nie przesyłać ich strumieniowo. Jeśli chcesz przesyłać strumieniowo wyniki, możesz użyć metody AsStreaming, aby zmienić zapytanie LINQ to Entities na przesyłanie strumieniowe.

using (var db = new BloggingContext())
{
    var query = (from b in db.Blogs
                orderby b.Url
                select b).AsStreaming();
    }
}

Przesyłanie strumieniowe nie jest obsługiwane w przypadku zarejestrowania strategii ponawiania wykonywania. To ograniczenie istnieje, ponieważ połączenie może spowodować usunięcie częściowego sposobu przez zwracane wyniki. W takim przypadku program EF musi ponownie uruchomić całe zapytanie, ale nie ma wiarygodnego sposobu znajomości, które wyniki zostały już zwrócone (dane mogły ulec zmianie od czasu wysłania początkowego zapytania, wyniki mogą wrócić w innej kolejności, wyniki mogą nie mieć unikatowego identyfikatora itp.).

Transakcje inicjowane przez użytkownika nie są obsługiwane

Po skonfigurowaniu strategii wykonywania, która powoduje ponawianie prób, istnieją pewne ograniczenia dotyczące używania transakcji.

Domyślnie program EF będzie wykonywać wszystkie aktualizacje bazy danych w ramach transakcji. Nie musisz nic robić, aby to włączyć, program EF zawsze wykonuje to automatycznie.

Na przykład w poniższym kodzie funkcja SaveChanges jest wykonywana automatycznie w ramach transakcji. Jeśli funkcja SaveChanges nie powiodła się po wstawieniu jednej z nowych lokacji, transakcja zostanie wycofana i nie zostaną zastosowane żadne zmiany w bazie danych. Kontekst jest również pozostawiony w stanie, który umożliwia wywołanie funkcji SaveChanges ponownie w celu ponawiania próby zastosowania zmian.

using (var db = new BloggingContext())
{
    db.Blogs.Add(new Site { Url = "http://msdn.com/data/ef" });
    db.Blogs.Add(new Site { Url = "http://blogs.msdn.com/adonet" });
    db.SaveChanges();
}

Jeśli nie używasz strategii ponawiania wykonywania, można opakowować wiele operacji w jednej transakcji. Na przykład poniższy kod opakowuje dwa wywołania SaveChanges w jednej transakcji. Jeśli którakolwiek część dowolnej operacji zakończy się niepowodzeniem, żadna ze zmian nie zostanie zastosowana.

using (var db = new BloggingContext())
{
    using (var trn = db.Database.BeginTransaction())
    {
        db.Blogs.Add(new Site { Url = "http://msdn.com/data/ef" });
        db.Blogs.Add(new Site { Url = "http://blogs.msdn.com/adonet" });
        db.SaveChanges();

        db.Blogs.Add(new Site { Url = "http://twitter.com/efmagicunicorns" });
        db.SaveChanges();

        trn.Commit();
    }
}

Nie jest to obsługiwane w przypadku korzystania ze strategii ponawiania próby wykonania, ponieważ program EF nie zna żadnych poprzednich operacji i sposobu ich ponawiania. Jeśli na przykład druga funkcja SaveChanges nie powiodła się, program EF nie ma już wymaganych informacji, aby ponowić próbę pierwszego wywołania SaveChanges.

Rozwiązanie: Ręczne wywoływanie strategii wykonywania

Rozwiązaniem jest ręczne użycie strategii wykonywania i nadanie jej całego zestawu logiki do uruchomienia, aby można było ponowić próbę wszystkiego, jeśli jedna z operacji zakończy się niepowodzeniem. Gdy jest uruchomiona strategia wykonywania pochodna dbExecutionStrategy, zawiesi niejawną strategię wykonywania używaną w programie SaveChanges.

Należy pamiętać, że wszystkie konteksty powinny być konstruowane w bloku kodu, który ma zostać ponowiony. Gwarantuje to, że zaczynamy od czystego stanu dla każdej ponawiania próby.

var executionStrategy = new SqlAzureExecutionStrategy();

executionStrategy.Execute(
    () =>
    {
        using (var db = new BloggingContext())
        {
            using (var trn = db.Database.BeginTransaction())
            {
                db.Blogs.Add(new Blog { Url = "http://msdn.com/data/ef" });
                db.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
                db.SaveChanges();

                db.Blogs.Add(new Blog { Url = "http://twitter.com/efmagicunicorns" });
                db.SaveChanges();

                trn.Commit();
            }
        }
    });