Ch.
Ch.

Reputation: 111

How to include a return statement when executing a Polly policy?

Below is my code in C# Windows application, where connection with Oracle, FTP and Null Reference Exception are handled:

public Result Execute()
{
    Result result = null;
    string errorMessage = string.Empty;

    var retryTimes = 1000;
    var retryableErrorCodes = new[] { "ORA-03113", "ORA-03114", "ORA-12543",
        "ORA-12170", "ORA-12154", "ORA-12541", "ORA-12560", "ORA-03135",
        "Connection request timed out" };
    var retryExceptionError = new[] { "Object reference not set to an instance of an object" };

    RetryPolicy retryPolicyFTP = Policy
        .Handle<Xceed.Ftp.FtpInvalidStateException>().Or<Xceed.Ftp.FtpIOException>()
        .WaitAndRetry(retryTimes, _ => TimeSpan.FromSeconds(1));

    RetryPolicy retryPolicyOracle = Policy
        .Handle<OracleException>(ex => retryableErrorCodes.Any(errorCode => ex.Message.ToString().Contains(errorCode)))
        .WaitAndRetry(retryTimes, _ => TimeSpan.FromSeconds(1));

    RetryPolicy retryException = Policy
        .Handle<Exception>(ex => retryExceptionError.Any(errorCode => ex.Message.ToString().Contains(errorCode)))
        .WaitAndRetry(retryTimes, _ => TimeSpan.FromSeconds(1));

    Policy.Wrap(retryPolicyFTP, retryPolicyOracle, retryException).Execute(() =>
    {
        //few lines of C# Code like fetching details from Database
        if(some condition)
        {
            //Some Operations
            return new Result(ResultType.Failure, "This function has Failed");
        }
        if(some other condition)
        {
            //Some Operations
            return new Result(ResultType.Success, "This function is Successful");
        }
        //Some more lines of C# Code
    });
    return Result.Successful;
}

With this code, I cannot use return keyword in the middle of the function, because Polly framework does not allow it to do so.

Could you please suggest what is the better way of handling return keyword in the middle of the function?

Upvotes: 2

Views: 4162

Answers (1)

Peter Csala
Peter Csala

Reputation: 22819

In Polly you can define decorators for methods and for functions.

In case of a method the retry policy should be defined like this:

RetryPolicy retryPolicyFTP = Policy
    .Handle<Xceed.Ftp.FtpInvalidStateException>().Or<Xceed.Ftp.FtpIOException>()
    .WaitAndRetry(retryTimes, _ => TimeSpan.FromSeconds(1));

In case of a function the retry policy should be defined like this:

RetryPolicy<Result> retryPolicyFTP = Policy<Result>
    .Handle<Xceed.Ftp.FtpInvalidStateException>().Or<Xceed.Ftp.FtpIOException>()
    .WaitAndRetry(retryTimes, _ => TimeSpan.FromSeconds(1));

You should spot here the <Result> parts on lhs and rhs as well.


With this knowledge your method could be rewritten like this:

public Result Execute()
{
    Result result = null;
    string errorMessage = string.Empty;

    var retryTimes = 1000;
    var retryableErrorCodes = new[] { "ORA-03113", "ORA-03114", "ORA-12543", "ORA-12170", "ORA-12154", "ORA-12541", "ORA-12560", "ORA-03135", "Connection request timed out" };
    var retryExceptionError = new[] { "Object reference not set to an instance of an object" };

    RetryPolicy<Result> retryPolicyFTP = Policy<Result>
        .Handle<Xceed.Ftp.FtpInvalidStateException>().Or<Xceed.Ftp.FtpIOException>()
        .WaitAndRetry(retryTimes, _ => TimeSpan.FromSeconds(1));

    RetryPolicy<Result> retryPolicyOracle = Policy<Result>
        .Handle<OracleException>(ex => retryableErrorCodes.Any(errorCode => ex.Message.ToString().Contains(errorCode)))
        .WaitAndRetry(retryTimes, _ => TimeSpan.FromSeconds(1));

    RetryPolicy<Result> retryException = Policy<Result>
        .Handle<Exception>(ex => retryExceptionError.Any(errorCode => ex.Message.ToString().Contains(errorCode)))
        .WaitAndRetry(retryTimes, _ => TimeSpan.FromSeconds(1));

    Result res = Policy.Wrap(retryPolicyFTP, retryPolicyOracle, retryException).Execute(() =>
    {
        if (some condition)
        {
            return new Result(ResultType.Failure, "This function has Failed");
        }
        if (some other condition)
        {
            return new Result(ResultType.Success, "This function is Successful");
        }
        return Result.Successful;
    });
    return res;
}

Because your Execute has to return a Result that's why the Result.Successful could be moved inside the Execute block.


I would also suggest to separate strategy declaration and execution like this:

public Result Execute()
{
    ...

    var strategy = Policy.Wrap(retryPolicyFTP, retryPolicyOracle, retryException)
    return strategy.Execute(() =>
    {
        if (some condition)
        {
            return new Result(ResultType.Failure, "This function has Failed");
        }
        if (some other condition)
        {
            return new Result(ResultType.Success, "This function is Successful");
        }
        return Result.Successful;
    });
}

Upvotes: 6

Related Questions