устойчивость Подключение и логика повторных попыток

Примечание.

Только в EF6 и более поздних версиях. Функции, API и другие возможности, описанные на этой странице, появились в Entity Framework 6. При использовании более ранней версии могут быть неприменимы некоторые или все сведения.

Приложения, подключающиеся к серверу базы данных, всегда были уязвимы для разрывов подключения из-за сбоев серверной части и нестабильности сети. Однако в среде на основе локальной сети, работающей с выделенными серверами баз данных, эти ошибки являются достаточно редкими, что дополнительная логика для обработки этих сбоев не часто требуется. С ростом облачных серверов баз данных, таких как Windows База данных SQL Azure и подключения через менее надежные сети, теперь чаще возникают разрывы подключений. Это может быть связано с оборонительными методами, которые используются облачными базами данных для обеспечения справедливости службы, таких как регулирование подключений, или нестабильности в сети, что приводит к временным ожиданиям и другим временным ошибкам.

Подключение устойчивость относится к возможности автоматического повтора всех команд, которые завершаются сбоем из-за этих разрывов подключений.

Стратегии выполнения

Подключение повторные попытки заботятся о реализации интерфейса IDbExecutionStrategy. Реализации IDbExecutionStrategy будут отвечать за принятие операции и, если возникает исключение, определить, подходит ли повторная попытка и повторите попытку, если она есть. Существует четыре стратегии выполнения, которые отправляются в EF:

  1. DefaultExecutionStrategy: эта стратегия выполнения не повторяет никаких операций, это значение по умолчанию для баз данных, отличных от SQL Server.
  2. DefaultSqlExecutionStrategy: это внутренняя стратегия выполнения, используемая по умолчанию. Эта стратегия не повторяется вообще, однако она будет упаковывать любые исключения, которые могут быть временными для информирования пользователей о том, что они могут включить устойчивость подключений.
  3. DbExecutionStrategy: этот класс подходит в качестве базового класса для других стратегий выполнения, включая собственные пользовательские. Она реализует экспоненциальную политику повторных попыток, где начальная повторная попытка происходит с нулевой задержкой, а задержка увеличивается экспоненциально до достижения максимального количества повторных попыток. Этот класс имеет абстрактный метод ShouldRetryOn, который можно реализовать в производных стратегиях выполнения, чтобы контролировать, какие исключения следует извлечь.
  4. SqlAzureExecutionStrategy: эта стратегия выполнения наследует от DbExecutionStrategy и повторит исключения, которые, как известно, являются временными при работе с База данных SQL Azure.

Примечание.

Стратегии выполнения 2 и 4 включены в поставщик SQL Server, который поставляется с EF, которая находится в сборке EntityFramework.SqlServer и предназначена для работы с SQL Server.

Включение стратегии выполнения

Самый простой способ сообщить EF использовать стратегию выполнения с помощью метода SetExecutionStrategy класса DbConfiguration :

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

Этот код сообщает EF использовать SqlAzureExecutionStrategy при подключении к SQL Server.

Настройка стратегии выполнения

Конструктор SqlAzureExecutionStrategy может принимать два параметра, MaxRetryCount и MaxDelay. Максимальное число повторных попыток — это максимальное количество раз, когда стратегия повторится. MaxDelay — это интервал TimeSpan, представляющий максимальную задержку между повторными попытками, которые будет использоваться стратегией выполнения.

Чтобы задать максимальное число повторных попыток в 1, а максимальное значение задержки — 30 секунд, выполните следующее:

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

SqlAzureExecutionStrategy будет повторяться мгновенно при первом возникновении временных сбоев, но будет задерживаться дольше между каждой попыткой до тех пор, пока максимальное ограничение повторных попыток не будет превышено или общее время достигнет максимальной задержки.

Стратегии выполнения будут повторять только ограниченное количество исключений, которые обычно являются временными, вам по-прежнему потребуется обрабатывать другие ошибки, а также перехватывать исключение RetryLimitExceeded для случая, когда ошибка не является временной или занимает слишком много времени, чтобы устранить себя.

При использовании стратегии повторного выполнения существуют некоторые известные ограничения:

Потоковые запросы не поддерживаются

По умолчанию EF6 и более поздняя версия буферизирует результаты запросов, а не потоковую передачу. Если требуется потоковая передача результатов, можно использовать метод AsStreaming для изменения запроса LINQ to Entities на потоковую передачу.

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

Потоковая передача не поддерживается при регистрации стратегии повторного выполнения. Это ограничение существует, так как подключение может отбросить часть через возвращаемые результаты. В этом случае EF должен повторно выполнить весь запрос, но не имеет надежного способа знать, какие результаты уже возвращены (данные могут быть изменены с момента отправки первоначального запроса, результаты могут вернуться в другом порядке, результаты могут не иметь уникального идентификатора и т. д.).

Транзакции, инициированные пользователем, не поддерживаются

Если вы настроили стратегию выполнения, которая приводит к повторным попыткам, существуют некоторые ограничения на использование транзакций.

По умолчанию EF будет выполнять любые обновления базы данных в рамках транзакции. Вам не нужно ничего делать, чтобы включить это, EF всегда делает это автоматически.

Например, в следующем коде SaveChanges автоматически выполняется в транзакции. Если SaveChanges завершится сбоем после вставки одного из новых сайтов, то транзакция будет откатена и никаких изменений, примененных к базе данных. Контекст также остается в состоянии, позволяющем снова вызывать SaveChanges, чтобы повторить применение изменений.

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();
}

Если не используется стратегия повтора выполнения, можно упаковать несколько операций в одну транзакцию. Например, следующий код упаковывает два вызова SaveChanges в одну транзакцию. Если любая часть любой операции завершается ошибкой, то никакие изменения не применяются.

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();
    }
}

Это не поддерживается при использовании стратегии повторного выполнения, так как EF не знает о предыдущих операциях и о том, как повторить их. Например, если второй сбой SaveChanges, EF больше не имеет необходимых сведений, чтобы повторить первый вызов SaveChanges.

Решение: стратегия выполнения вручную

Решение заключается в том, чтобы вручную использовать стратегию выполнения и предоставить ему весь набор логики, чтобы он смог повторить все, если одна из операций завершается сбоем. При выполнении стратегии выполнения, полученной из DbExecutionStrategy, она приостановит неявную стратегию выполнения, используемую в SaveChanges.

Обратите внимание, что все контексты должны быть созданы в блоке кода для получения. Это гарантирует, что мы начинаем с чистого состояния для каждой повторных попыток.

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();
            }
        }
    });