I've implemented a generic repository and was wondering if there is a smart way to implement a retry logic in case of a deadlock exception?
The approach should be the same for all repository methods. So is there anyway I can avoid writing 'try/catch - call method again with retry-count', in every single method?
Any suggetsion are welcome.
A bit of my Repository code:
public class GenericRepository : IRepository
{
private ObjectContext _context;
public List<TEntity> ExecuteStoreQuery<TEntity>(string commandText, params object[] parameters) where TEntity : class
{
List<TEntity> myList = new List<TEntity>();
var groupData = _context.ExecuteStoreQuery<TEntity>(commandText, parameters);
return myList;
}
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
var entityName = GetEntityName<TEntity>();
return _context.CreateQuery<TEntity>(entityName);
}
public IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class
{
return GetQuery<TEntity>().AsEnumerable();
}
EDIT:
1.Solution:
Modified slightly from chris.house.00's solution
public static T DeadlockRetryHelper<T>(Func<T> repositoryMethod, int maxRetries)
{
var retryCount = 0;
while (retryCount < maxRetries)
{
try
{
return repositoryMethod();
}
catch (System.Data.SqlClient.SqlException ex)
{
if (ex.Number == 1205)// Deadlock
retryCount++;
else
throw;
}
}
return default(T);
}
And you call it like this:
public TEntity FirstOrDefault<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
{
return RetryUtility.DeadlockRetryHelper<TEntity>( () =>p_FirstOrDefault<TEntity>(predicate), 3);
}
protected TEntity p_FirstOrDefault<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
{
return GetQuery<TEntity>().FirstOrDefault<TEntity>(predicate);
}
I know this is an old post but wanted to share an updated answer.
EF 6 now has a built-in solution, you can set the execution strategy which would be a one time implementation. You create a class that inherits from DbExectutionStrategy and overrides the ShouldRetryOn virtual method. You can create a static class of the exceptions containing constant field valuess that are retry eligible codes and loop through each one to determine if the current sql exception being thrown matches the list of eligible retry codes...
Finally once, you've set up your custom execution strategy, you simply create another class that inherits from DbConfiguration with a public constructor that Sets the execution strategy:
Have you considered some form of policy injection? You could use Unity interception, just as an example, to capture all your repository calls. Then you just write the retry logic once, in the interceptor, rather than repeating it many times in each method.
The solution works though I prefer not to have to worry about the number of arguments to the
Action
orFunc
that will be retired. If you create a single retry method with a genericAction
, you can handle all of the variability of the method to be called in a lambda:Then use it like so:
How about something like this:
With the above code, your retry logic is all in this method and you can just pass your repository method in as a delegate.
EntityFramework 6
addExecutionStrategy
feature. All that is need is to setup up the strategy properly.My retry policy:
Tell EF to apply my strategy:
Sources:
The retry strategy will not work with user initiated transactions (transaction created with
TransactionScope
) as explained here. If used you will get the ErrorThe configured execution strategy does not support user initiated transactions