I want to query data from a database via a webservice. The provided API allows to send batch requests, so that multiple requests are sent in a single HTTP request. I'd like to make benefit from this and am trying to build a C# interface to use this service using async code and Tasks.
My goal is to achieve something like the following:
// Service class that abstracts the server interface.
class Service
{
// Return all StateA objects at the next server contact.
public Task<IEnumerable<StateA>> GetStatesA() { ... }
// Return all StateB objects at the next server contact.
public Task<IEnumerable<StateB>> GetStatesB() { ... }
// Send all queued requests to the server.
public Task SendRequests() { ... }
}
Usage of the API:
// Query all StateA and StateB objects from the database and
// process them as soon as they arrive.
service.GetStatesA().ContinueWith((t) => DoSomething(t.Result));
service.GetStatesB().ContinueWith((t) => DoSomething(t.Result));
// Some other code
// Actually access the API such that the data is queried and the Tasks completed.
service.SendRequests();
From my knowledge, Tasks in C# can only wrap synchronous functions and return when they finish. Is there any way to complete a Task from the outside?
I'm looking for something similar to e.g. the Completer class in Dart (see https://api.dartlang.org/1.13.0/dart-async/Completer-class.html).
What you're looking for is a TaskCompletionSource<T>
:
public class Foo
{
private readonly TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
public Task GetStateA()
{
// Do stuff;
return tcs.Task;
}
public Task GetStateB()
{
//Do stuff
return tcs.Task;
}
public async Task QueryApiAsync()
{
// Query the API
tcs.SetResult(true);
}
}
Although possible to use, it looks to me like the API you're exposing isn't really convenient. I wouldn't want GetStateA()
to return a Task
which only completes once the batch query executes. I would rather have a some sort of batch method which is provided with an IEnumerable<States>
and returns to the caller once the batch completes. If you're not really planning on allowing a single state to query the API.
So, I would either turn the API methods that has an Enqueue
method which invokes a callback:
public interface Foo
{
void Enqueue<T>(T state, Action callback) where T : State;
}
Or actually expose the functionality of making a singular call to the API, as well as a Batch operation:
public interface Foo
{
Task<List<StateA>> GetStatesAAsync();
Task<List<StateB>> GetStatesBAsync();
Task<List<IState>> GetBatchStatesAsync(IEnumerable<IState> states);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With