Laurence
Laurence

Reputation: 1020

Passing asynchronous task result from one library (NPoco) to another (Polly) - which approach to use?

Background

In my C# project (.NET 5.0), I am using NPoco (an ORM) to run SQL commands. I am then looking to execute the calls via a retry policy using Polly (e.g. if certain transient SQL errors occur, then retry up to N times).

For synchronous calls, this is straightforward. However, for asynchronous calls, I can see at least 3 ways to do it. Essentially, the difference between my 3 ways is in whether or not they use Polly's and NPoco's own synchronous method or asynchronous methods. Note that I cannot see the internals of these methods.

The question

All the below methods build OK, and appear to work. My question is - is there any difference (functionally, reliability or performance-wise) between these approaches?

Code samples

Note that a Polly policy has a synchronous method: T Execute(Func<T> action) and an asynchronous method: Task<T> ExecuteAsync(Func<Task<T>> action)

Here is my synchronous method (synchronous Polly and NPoco methods):

public T GetScalar<T>(string sql, params object[] args)
{
    return _policy.Execute(() => _npoco.ExecuteScalar<T>(sql, args));
}

Here is my asynchronous method #1 (asynchronous Polly and NPoco methods):

public async Task<T> GetScalarAsync1<T>(string sql, params object[] args)
{
    return await _policy.ExecuteAsync(() => _npoco.ExecuteScalarAsync<T>(sql, args));
}

Here is my asynchronous method #2 (asynchronous Polly and synchronous NPoco methods):

public async Task<T> GetScalarAsync2<T>(string sql, params object[] args)
{
    return await _policy.ExecuteAsync(() => Task.Run(() => _npoco.ExecuteScalar<T>(sql, args)));
}

Here is my asynchronous method #3 (synchronous Polly and NPoco methods, run as task):

public async Task<T> GetScalarAsync3<T>(string sql, params object[] args)
{
    return await Task.Run(() => _policy.Execute(() => _npoco.ExecuteScalar<T>(sql, args)));
}

Upvotes: 1

Views: 231

Answers (1)

Peter Csala
Peter Csala

Reputation: 22829

You should use this version since ExecuteScalarAsync most probably can take advantage of non-blocking async I/O:

await _policy.ExecuteAsync(() => _npoco.ExecuteScalarAsync<T>(sql, args));

or you could use async-await inside the ExecuteAsync as well

await _policy.ExecuteAsync(async () => await _npoco.ExecuteScalarAsync<T>(sql, args));

By definition Task.Run:

Queues the specified work to run on the ThreadPool and returns a task or Task<TResult> handle for that work

In other words it is not designed for async I/O rather to kick off a CPU intensive computation in the background.

Upvotes: 1

Related Questions