Connection resiliency is the ability to automatically retry certain transient errors when attempting to connect to the database.
In Entity Framework 6, we can easily deal with timeouts, deadlocks, and other transient SQL errors. In this article, we will discuss how to implement it.
The first step is to create a class that inherits from DbExecutionStrategy
and overrides ShouldRetryOn()
. Let's add a new class MyExecutionStrategy
to the DAL folder and add the following code.
using System;
using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;
using System.Linq;
namespace MvcWithEF6Demo.DAL
{
public class MyExecutionStrategy : DbExecutionStrategy
{
public MyExecutionStrategy()
{
}
public MyExecutionStrategy(int maxRetryCount, TimeSpan maxDelay) : base(maxRetryCount, maxDelay)
{
}
protected override bool ShouldRetryOn(Exception ex)
{
bool retry = false;
SqlException sqlException = ex as SqlException;
if (sqlException != null)
{
int[] errorsToRetry = { 1205, -2, 2601 };
if (sqlException.Errors.Cast<SqlError>().Any(x => errorsToRetry.Contains(x.Number)))
{
retry = true;
}
else
{
}
}
if (ex is TimeoutException)
{
retry = true;
}
return retry;
}
}
}
The ShouldRetryOn()
takes an Exception
as an argument, examines it, and returns a boolean indicating to the EF context whether it should be retried after a short wait.
To hook it into our Entity Framework, we need to create a new class MyDbConfiguration
that inherits from DbConfiguration
. The easiest way to tell EF to use an execution strategy is with the SetExecutionStrategy
method of the DbConfiguration
class.
using System.Data.Entity;
namespace MvcWithEF6Demo.DAL
{
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetExecutionStrategy("System.Data.SqlClient", () => new MyExecutionStrategy());
}
}
}
It tells EF to use the MyExecutionStrategy
when connecting to SQL Server. The context will automatically find this class on compilation when it is just available in the assembly.
In all the controllers change the catch
block that catches DataException
exceptions with RetryLimitExceededException
exceptions as shown below.
catch (RetryLimitExceededException)
{
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
DataException
was used to try and identify errors that might be transient to give a friendly "try again" message.RetryLimitExceededException
exception.