.net Polly在GraphServiceClient上使用DI的默认实现是什么?

js4nwp54  于 2023-04-22  发布在  .NET
关注(0)|答案(1)|浏览(118)

我试图找到一种简单的方法来实现Polly在我的GraphServiceClient使用DI。
我有一个GraphApiBuilder类,我用它来与任何需要GraphServiceClient的类进行接口,因为直接将它注入到一个具有硬逻辑的类中会使它实际上无法测试:

public class GraphApiBuilder : IGraphApiBuilder, ISingletonDependency
{
    private readonly GraphServiceClient _graphServiceClient;

    public GraphApiBuilder(GraphServiceClient graphServiceClient)
    {
        _graphServiceClient = graphServiceClient;
    }

    public GraphServiceClient Create()
    {
        return _graphServiceClient;
    }
}

在我的ConfigureServices方法中,我这样做:

context.Services.AddSingleton(_ =>
{
    var graphApiOptions = new GraphApiOptions();
    context.Services.GetConfiguration().Bind(GraphApiOptions.SectionName, graphApiOptions);
    var clientSecretCredential = new ClientSecretCredential(graphApiOptions.TenantId, graphApiOptions.ClientId, graphApiOptions.ClientSecret);
    return new GraphServiceClient(clientSecretCredential);
});

您可以看到,自从Microsoft在this doc中推荐它以来,所有内容都是单例的
现在我希望当GraphServiceClient返回一个错误时,它使用Polly重试。我不想重复这段代码。

5vf7fwbs

5vf7fwbs1#

在阅读了Peter Csala对我的原始帖子的评论后,我发现微软已经在GraphServiceClient上默认包含了RetryHandler
然而,我想用自己的机制覆盖重试机制,我发现this post显示了一个自定义实现,这是一个很好的起点。
我最后的好东西看起来像这样:

private const string RetryAfterHeaderKey = "Retry-After";
    private const int MaxRetry = 3;

   [...]

    context.Services.AddSingleton(_ =>
        {
            var graphApiOptions = new GraphApiOptions();
            context.Services.GetConfiguration().Bind(GraphApiOptions.SectionName, graphApiOptions);
            var clientSecretCredential = new ClientSecretCredential(graphApiOptions.TenantId, graphApiOptions.ClientId, graphApiOptions.ClientSecret);
            var handlers = GraphClientFactory.CreateDefaultHandlers(new TokenCredentialAuthProvider(clientSecretCredential));
            // Find the index of the existing RetryHandler, if any
            var retryHandlerIndex = handlers.FindIndex(handler => handler is RetryHandler);

            // Remove the existing RetryHandler, if found
            if (retryHandlerIndex >= 0)
            {
                handlers.RemoveAt(retryHandlerIndex);
                handlers.Insert(retryHandlerIndex, new RetryHandler(new RetryHandlerOption()
                {
                    MaxRetry = MaxRetry,
                    ShouldRetry = (delay, attempt, httpResponse) =>
                    {
                        if (httpResponse.IsSuccessStatusCode)
                        {
                            return false;
                        }

                        Log.Error($"{httpResponse.RequestMessage?.RequestUri} request returned status code {httpResponse.StatusCode}");
                        // Add more status codes here or change your if statement...
                        if (httpResponse?.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden or HttpStatusCode.NotFound)
                        {
                            return false;
                        }

                        var delayInSeconds = CalculateDelay(httpResponse, attempt, delay);
                        switch (attempt)
                        {
                            case 0:
                                Log.Error($"Request failed, let's retry after a delay of {delayInSeconds} seconds");
                                break;
                            case MaxRetry:
                                Log.Error($"This was the last retry attempt {attempt}");
                                break;
                            default:
                                Log.Error($"This was retry attempt {attempt}, let's retry after a delay of {delayInSeconds} seconds");
                                break;
                        }

                        return true;
                    }
                }));
            }
            
            // Now we have an extra step of creating a HTTPClient passing in the customized pipeline
            var httpClient = GraphClientFactory.Create(handlers);

            // Then we construct the Graph Service Client using the HTTPClient
            return new GraphServiceClient(httpClient);
        });

internal static double CalculateDelay(HttpResponseMessage response, int retryCount, int delay)
    {
        var headers = response.Headers;
        double delayInSeconds = delay;
        if (headers.TryGetValues(RetryAfterHeaderKey, out var values))
        {
            var retryAfter = values.First();
            if (int.TryParse(retryAfter, out var delaySeconds))
            {
                delayInSeconds = delaySeconds;
            }
        }
        else
        {
            delayInSeconds = retryCount * delay;
        }
        const int maxDelay = 9;
        delayInSeconds = Math.Min(delayInSeconds, maxDelay);
        return delayInSeconds;
    }

相关问题