Quantcast
Channel: Brian Pedersen's Sitecore and .NET Blog
Viewing all articles
Browse latest Browse all 285

C# Inject ILogger into Dapper Polly Retry Policy

$
0
0

There are many articles describing how to make a retry policy when using Dapper. I especially like this extension method implementation. But you cannot inject any class into extension methods because extension methods reside in a static class. Therefore you don’t have a constructor where the injection can happen.

Instead, another easy solution is to create a base class for your Dapper repository classes.

STEP 1: THE PREREQUISITES

You need Dapper and Polly of course:

Then you need a reference to a class called “SqlServerTransientExceptionDetector“. You can either grab the code and paste in into your class, or you can reference Microsoft.EntityFrameworkCore.SqlServer NuGet:

STEP 2: THE BASE CLASS

I’m assuming you use standard dependency injection, and that you have an ILogger to be used.

using Microsoft.Extensions.Logging;
using Polly.Retry;
using Polly;
using System.ComponentModel;
using System.Data.SqlClient;
using System.Data;
using Dapper;

namespace MyCode
{
  public class DapperRetryPolicyBaseClass<T>
  {
    private readonly ILogger<T> _logger;
    private static readonly IEnumerable<TimeSpan> _retryTimes = new[]
    {
        TimeSpan.FromSeconds(2),
        TimeSpan.FromSeconds(4),
        TimeSpan.FromSeconds(8)
    };

    private readonly AsyncRetryPolicy _asyncRetryPolicy;

    public DapperRetryPolicyBaseClass(ILogger<T> logger)
    {
      _logger = logger;

      _asyncRetryPolicy = Policy
        .Handle<SqlException>(SqlServerTransientExceptionDetector.ShouldRetryOn)
        .Or<TimeoutException>()
        .OrInner<Win32Exception>(SqlServerTransientExceptionDetector.ShouldRetryOn)
        .WaitAndRetryAsync(_retryTimes,
                        (exception, timeSpan, retryCount, context) =>
                        {
                          _logger.LogWarning(exception, "{InstanceType} SQL Exception. retry #{RetryCount}. Exception {Exception}", _logger.GetType(), retryCount, exception);
                        });
    }

    protected async Task<int> ExecuteAsyncWithRetry(IDbConnection connection, 
                                                    string sql, 
                                                    object param = null,
                                                    IDbTransaction transaction = null, 
                                                    int? commandTimeout = null,
                                                    CommandType? commandType = null
                                                   ) =>
        await _asyncRetryPolicy.ExecuteAsync(async () => await connection.ExecuteAsync(sql, param, transaction, commandTimeout, commandType));

    protected async Task<IEnumerable<T>> QueryAsyncWithRetry<T>(IDbConnection connection, 
                                                                string sql, 
                                                                object param = null,
                                                                IDbTransaction transaction = null, 
                                                                int? commandTimeout = null,
                                                                CommandType? commandType = null
                                                               ) =>
        await _asyncRetryPolicy.ExecuteAsync(async () => await connection.QueryAsync<T>(sql, param, transaction, commandTimeout, commandType));
  }
}

Being a base class, we suddenly have a constructor where the injection can happen. Also, the Polly retry policy can be defined in the constructor where the ILogger is available.

STEP 3: HOW TO USE THE BASE CLASS

using System.Data.SqlClient;
using Dapper;
using Microsoft.Extensions.Logging;

namespace MyCode
{
  public class MyRepository : DapperRetryPolicyBaseClass<MyRepository>
  {

    private readonly ILogger<JsonEventRepository> _logger;

    public JsonEventRepository(ILogger<MyRepository> logger) : base(logger)
    {
      _logger = logger;
    }

    public async Task Execute(string someDateToBeInsertedIntoATable)
    {
      using var sqlConnection = new SqlConnection("some-connection-string");
      await base.ExecuteWithRetryAsync(sqlConnection, "insert into some_table (field) values (@field)", 
        new
        {
            Field = someDateToBeInsertedIntoATable,
        }
      );
    }
  }
}

Inherit from DapperRetryPolicyBaseClass and define the class type, and the ILogger of the base class will have the same type as the MyRepository.

Then use the base retry methods. On any retry, the base class will log the retry using the ILogger.

That’s it. You are now a Dapper expert. Happy coding.

MORE TO READ:


Viewing all articles
Browse latest Browse all 285

Trending Articles