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

Примечание

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

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

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

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

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

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

Примечание

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

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

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

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

Этот код указывает EF использовать SqlAzureExecutionStrategy при соединении с SQL Server.

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

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

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

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

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

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

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

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

По умолчанию EF6 и более поздние версии будут помещает результаты запроса в буфер, а не потоки. если требуется потоковая передача результатов, можно использовать метод асстреаминг для изменения 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.

Решение. Стратегия выполнения вызова вручную

Решение состоит в том, чтобы вручную использовать стратегию выполнения и дать ей весь набор логики для выполнения, чтобы он мог повторить все, если одна из операций завершается ошибкой. При выполнении стратегии выполнения, производной от Дбексекутионстратеги, она приостанавливает неявную стратегию выполнения, используемую в 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();
            }
        }
    });