How to wrap a legacy synchronous I/O bound operation with an asynchronous method?

I'm still learning async/await concepts so please bear with me. Suppose there is this legacy code that defines a contract for getting data from a data source (local database in the real case):

public interface IRepository<T> {
    IList<T> GetAll();
}

In new code I have the following interface:

public interface IMyObjectRepository {
    Task<IEnumerable<MyObject>> GetAllAsync();
}

The MyObjectRepository class depends on IRepository<T> like this:

public class MyObjectRepository : IMyObjectRepository
{
    private readonly IRepository<Some.Other.Namespace.MyObject> repo_;

    private readonly IDataMapper<Some.Other.Namespace.MyObject, MyObject> mapper_;

    public BannerRepository(IRepository<Some.Other.Namespace.MyObject> repo, IDataMapper<Some.Other.Namespace.MyObject, MyObject> mapper)
    {
        repo_ = repo;
        mapper_ = mapper;
    }
}

How do I implement the IMyObjectRepository.GetAllAsync() method in such a way that it makes sense in the context of asynchronous programming? Getting data from some local data source is an I/O bound operation, so the way I did it is:

public async Task<IEnumerable<MyObject>> GetAllAsync()
{
    var tcs = new TaskCompletionSource<IEnumerable<MyObject>>();

    try
    {
        GetAll(objects =>
        {
            var result = new List<MyObject>();

            foreach (var o in objects)
            {
                result.Add(mapper_.Map(o));
            }

            tcs.SetResult(result);
        });
    }
    catch (Exception e)
    {
        tcs.SetException(e);
    }

    return await tcs.Task;
}

private void GetAll(Action<IEnumerable<Some.Other.Namespace.MyObject>> handle)
{
    IEnumerable<Some.Other.Namespace.MyObject> objects = repo_.GetAll<Some.Other.Namespace.MyObject>();

    if (objects is null)
    {
        objects = Enumerable.Empty<Some.Other.Namespace.MyObject>();
    }

    handle(objects);
}

Does this make any sense? I did not want to use Task.Run() because, the way I understand it, that wastes a thread for nothing in the case of an I/O bound operation which makes sense.



Read more here: https://stackoverflow.com/questions/68016543/how-to-wrap-a-legacy-synchronous-i-o-bound-operation-with-an-asynchronous-method

Content Attribution

This content was originally published by vladek at Recent Questions - Stack Overflow, and is syndicated here via their RSS feed. You can read the original post over there.

%d bloggers like this: