Polly icon indicating copy to clipboard operation
Polly copied to clipboard

Using class with generic parameter in RetryPolicy<T>

Open neonVoice opened this issue 4 years ago • 0 comments

Summary: What are you wanting to achieve?

I am trying to use RetryPolicy with generic parameter that can contain class with another generic parameter, for example, because they share same logic

RetryPolicy<ApiTaskResult<DraftsBuilderBuildResult>>
RetryPolicy<ApiTaskResult<CheckResult>>
RetryPolicy<ApiTaskResult<PrepareResult>>
RetryPolicy<ApiTaskResult<Docflow>>

What code or approach do you have so far?

private readonly RetryPolicy<ApiTaskResult<DraftsBuilderBuildResult>> _taskCheckDraftsBuilderBuildResultQueryingRetryPolicy = Policy
    .Handle<Exception>()
    .OrResult<ApiTaskResult<DraftsBuilderBuildResult>>(r => r.TaskState == TaskState.Running)
    .WaitAndRetry(
        50,
        rc => TimeSpan.FromSeconds(5 * rc),
        (driven, span) =>
        {
            Debug.WriteLine(driven.Exception is null
                ? $"Task {driven.Result.Id} is still running, waiting {span.TotalSeconds} seconds."
                : $"{driven.Exception}, waiting {span.TotalSeconds} seconds.");
        });

private readonly RetryPolicy<ApiTaskResult<CheckResult>> _taskCheckDraftsCheckResultQueryingRetryPolicy = Policy
    .Handle<Exception>()
    .OrResult<ApiTaskResult<CheckResult>>(r => r.TaskState == TaskState.Running)
    .WaitAndRetry(
        50,
        rc => TimeSpan.FromSeconds(5 * rc),
        (driven, span) =>
        {
            Debug.WriteLine(driven.Exception is null
                ? $"Task {driven.Result.Id} is still running, waiting {span.TotalSeconds} seconds."
                : $"{driven.Exception}, waiting {span.TotalSeconds} seconds.");
        });

private readonly RetryPolicy<ApiTaskResult<PrepareResult>> _taskCheckDraftsPrepareResultQueryingRetryPolicy = Policy
    .Handle<Exception>()
    .OrResult<ApiTaskResult<PrepareResult>>(r => r.TaskState == TaskState.Running)
    .WaitAndRetry(
        50,
        rc => TimeSpan.FromSeconds(5 * rc),
        (driven, span) =>
        {
            Debug.WriteLine(driven.Exception is null
                ? $"Task {driven.Result.Id} is still running, waiting {span.TotalSeconds} seconds."
                : $"{driven.Exception}, waiting {span.TotalSeconds} seconds.");
        });

private readonly RetryPolicy<ApiTaskResult<Docflow>> _taskCheckDraftsSendDraftResultQueryingRetryPolicy = Policy
    .Handle<Exception>()
    .OrResult<ApiTaskResult<Docflow>>(r => r.TaskState == TaskState.Running)
    .WaitAndRetry(
        50,
        rc => TimeSpan.FromSeconds(5 * rc),
        (driven, span) =>
        {
            Debug.WriteLine(driven.Exception is null
                ? $"Task {driven.Result.Id} is still running, waiting {span.TotalSeconds} seconds."
                : $"{driven.Exception}, waiting {span.TotalSeconds} seconds.");
        });

From definitions you can see that i am using OrResult<T> with predicate r => r.TaskState == TaskState.Running.

Here is definition of ApiTaskResult:

public class ApiTaskResult<TTaskResultClass>
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("task-state")]
    public TaskState TaskState { get; set; }

    [JsonProperty("task-type")]
    public string TaskType { get; set; }

    [JsonProperty("task-result")]
    public TTaskResultClass TaskResult { get; set; }

    [JsonProperty("error")]
    public ExternApiError Error { get; set; }

    [JsonProperty("progress")]
    public string Progress { get; set; }
}

TaskResult is deserialized into concrete class that i usually pass as generic parameter and json deserializer create concrete class based on TaskType.

I tried adding constraint for TTaskResultClass that it should be an interface of ITaskResult from which DraftsBuilderBuildResult, CheckResult, PrepareResult and Docflow derived and creating policy with interface as generic parameter, so i can reuse the same logic and i considered this because i am already seeing 4 places with the same code, knowing that it will probably increase in future.

private readonly RetryPolicy<ApiTaskResult<ITaskResult>> _taskCheckDraftsResultQueryingRetryPolicy = Policy
    .Handle<Exception>()
    .OrResult<ApiTaskResult<ITaskResult>>(r => r.TaskState == TaskState.Running)
    .WaitAndRetry(
        50,
        rc => TimeSpan.FromSeconds(5 * rc),
        (driven, span) =>
        {
            Debug.WriteLine(driven.Exception is null
                ? $"Task {driven.Result.Id} is still running, waiting {span.TotalSeconds} seconds."
                : $"{driven.Exception}, waiting {span.TotalSeconds} seconds.");
        });

I am using it in code like this:

apiSendDraftTaskResult = _taskCheckDraftsSendDraftResultQueryingRetryPolicy.Execute(() => draftsService.GetDraftsTaskResult<Docflow>(getDraftsSendTaskResultUrlSegments));

however if i try to use _taskCheckDraftsResultQueryingRetryPolicy with interface and pass concrete class as generic parameter i get compilation error: изображение

Severity	Code	Description	Project	File	Line	Suppression State
Error	CS1662	Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type	Omega.KonturExtern.Tests	D:\TFS\1C\Omega.KonturExtern\Omega.KonturExtern.Tests\Chains\ChainsTests.cs	917	Active
Error	CS0029	Cannot implicitly convert type 'Omega.KonturExtern.Models.DraftsBuilder.ApiTaskResult<Omega.KonturExtern.Models.Docflow>' to 'Omega.KonturExtern.Models.DraftsBuilder.ApiTaskResult<Omega.KonturExtern.Models.ITaskResult>'	Omega.KonturExtern.Tests	D:\TFS\1C\Omega.KonturExtern\Omega.KonturExtern.Tests\Chains\ChainsTests.cs	917	Active

Is there a way to overcome this problem or should i consider making some kind of factory ?

neonVoice avatar May 13 '20 14:05 neonVoice