Make Async Operations prettier in .Net4.0 - c#

To preface, I'm 100% stuck using .Net 4.0.
I'm trying to consume a web service, and make the async calls a little less messy for the client to consume. I came up with:
protected void Get<T>(string uri, Action<T> callback)
{
var client = GetNewClient();
var request = new HttpRequestMessage(HttpMethod.Get, uri);
client.SendAsync(request)
.Completed(t =>
{
T resp = t.Result.Content.ReadAsAsync<T>().Result;
callback(resp);
})
.Errored(t =>
{
throw t.Exception;
});
}
Completed and Errored are just extension methods that wrap TaskContinuationOptions.OnlyOnRanToCompletion and OnlyOnFaulted, respectively.
This code works well when everything goes as expected. The problem is, if the task faults (i.e. web service down), the error doesn't make it back to the consumer. Also, the task is marked as Completed even if the response HttpStatusCode indicates an error (i.e. 404). I'd certainly like to handle those situations effectively, with this current implementation I have no way to do so (the response item is simply null).
Is there a way to raise an error back to the consumer here, or should I abandon this approach altogether?

Is there a way to raise an error back to the consumer here, or should I abandon this approach altogether?
Well, take a look at your signature:
protected void Get<T>(string uri, Action<T> callback);
When the client calls Get<T>, it starts the asynchronous operation and then returns. Obviously, if there is an exception later, there's no way to have the thread travel back in time and return from this method again.
Instead, you need to modify your callback. You can easily do this by adding a second callback delegate:
protected void Get<T>(string uri, Action<T> callback, Action<Exception> errorCallback);
Now one or the other of the callbacks will be called when the operation is complete. But what is this doing, really? Just re-introducing callbacks when you already have promises. So a superior solution would be (updated due to Servy's comment):
protected Task<T> Get<T>(string uri)
{
var client = GetNewClient();
var request = new HttpRequestMessage(HttpMethod.Get, uri);
return client.SendAsync(request)
.ContinueWith(t => t.Result.Content.ReadAsAsync<T>())
.Unwrap();
}
That said, I do think the best approach is to use Microsoft.Bcl.Async. Bundling KB2468871 into the installer is not too hard.

Decorate your method with async, call await on your get method:
protected async Task<T> Get<T>(string uri)
{
var client = GetNewClient();
var response = await client.GetAsync(uri);
// Throws an exception to the user if it was not a successful request.
response.EnsureSuccessStatusCode();
return await response.ReadAsAsync<T>();
}

Related

How do I use Polly for retries and transient fault handling of arbitrary "failure" conditions

I want to use Polly not to check for overt "failures" but rather for other conditions. Specifically, I want to make a single (async) call, for example httpClient.GetAsync(...) which for the purposes of this question I know will succeed - that is, after the execution of:
var response = await _myHttpRequestPolicy.ExecuteAsync(() => httpClient.GetAsync(uri));
response.IsSuccessStatusCode will be true.
Let's assume then I do the standard:
var content = await response.Content.ReadAsStringAsync();
and
content == { "Name":"Tom", "Age", 30", "ErrorCode":"12345" }
I want my policy logic to execute based on the contents (or absence or presence of) the ErrorCode portion of the response. So it's just a single call I'm making.
How can I do this with Polly?
You're configuring a policy to guard the HttpClient GetAsync method, which returns Task<HttpResponseMessage>. You want to configure a Policy<HttpResponseMessage> to work with this method, using async handlers.
Policy<T>.HandleResult(Func<T, bool> filter) allows you to look at the HttpResponseMessage and determine whether you want to handle that result.
A couple of options. One, you could figure out deserializing/reading the HttpResponseMessage's json payload within the HandleResult method. You only get a Func<HttpResponseMessage, bool> to work with. This would need to happen synchronously, as adding async/await changes the return type to Task.
Second, you could apply the policy at a higher level. Get the response as you are httpclient.GetAsync(uri), then deserialize the content. Maybe have one Policy<HttpResponseMessage> wrap the httpclient call, and one Policy<MyAbstractApiResponse> to look for the custom error code after deserializing?
As a note, an API error should really be picked up by the IsSuccessStatusCode property on the HttpResponseMessage. Your REST api (is it yours? that's an assumption) should be setting status codes appropriate to the error, not solely 200's and custom response properties.
Related further reading: Check string content of response before retrying with Polly
Update:
class Consumer
{
public void Test()
{
var apiResponse = Policy<IApiResponse>
.HandleResult(resp => !string.IsNullOrWhiteSpace(resp.ErrorCode))
// Setup Policy
.ExecuteAsync(() => otherClassInstance.MakeApiCall());
}
}
class OtherClass
{
HttpClient httpClient = ...;
public async Task<IApiResponse> MakeApiCall()
{
var response = Policy<HttpResponseMessage>
.HandleResult(message => !message.IsSuccessStatusCode)
// Setup Policy
.ExecuteAsync(() => httpClient.GetAsync(url));
var content = await response.ReadAsStringAsync();
return new ApiResponse(content);
}
}
I didn't look at real method names or syntax in putting that together so it might not compile. Hopefully you get the idea I'm trying to convey, one policy is called from within another.
As it was stated by mountain traveller the HandleResult was not designed to execute an async method in it. There are several workaround for this like:
using .GetAwaiter().GetResult() inside HandleResult
define two separate policies like it was in the linked github issue
or perform the async call inside the onRetryAsync and use a CancellationTokenSource to break the retry sequence
In this post let me focus on the last one
var noMoreRetrySignal = new CancellationTokenSource();
IAsyncPolicy<HttpResponseMessage> retryPolicy = Policy<HttpResponseMessage>
.HandleResult(r => r.StatusCode == System.Net.HttpStatusCode.OK)
.RetryAsync(3, onRetryAsync: async (dr, _, ctx) => {
var content = await dr.Result.Content.ReadAsStringAsync();
if (!content.Contains("ErrorCode")) //TODO: refine it based on your requirements
{
ctx["content"] = content;
noMoreRetrySignal.Cancel();
}
});
var result = await retryPolicy.ExecuteAndCaptureAsync(
async ct => await httpClient.GetAsync(address, ct),
noMoreRetrySignal.Token);
if(result.FinalException is OperationCanceledException)
{
Console.WriteLine(result.Context["content"]);
}
The noMoreRetrySignal is used to cancel the retry sequence
if there is no need for further retries
The retryPolicy checks whether the StatusCode is OK or not
The retryPolicy reads the response body asynchronously then performs some existence check
If it does not present then it stores the read string inside the Context and exits from the retry loop
If it presents then it continues the retry sequence
ExecuteAndCaptureAsync passes the noMoreRetrySignal's Token to the decorated method
The result of this call allows us to access the exception and the context
If the exception is caused by the CancellationTokenSource then we need to acces the context because the result.Result is null (that's why we had to use context to save the already read response body)

Async JSON deserialisation in ASP.NET

I have a small MVC 5 application that calls a web service, and receives a JSON response. I deserialise the response into my own type and it gets passed on to the view and data is displayed by razor.
The controller handler:
public async Task<ActionResult> Search(string q)
{
var vm = new SearchResultViewModel(await _searchService.GetDataAsync(q));
return View(vm);
}
The search service method:
public async Task<ISearchResult> GetDataAsync(string q)
{
var fullRequest = new UriBuilder(RequestUri) {Query = "q=" + q};
var result = await _client.GetAsync(fullRequest.ToString()).ConfigureAwait(false);
if (result.IsSuccessStatusCode)
{
var jsonResponse = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
// How should I call this?
return JsonConvert.DeserializeObject<SearchResult>(jsonResponse);
}
return new SearchResult
}
My question: How should I call JsonConvert.DeserializeObject? It's an inherently CPU bound operation, so is it ok to call synchronously (and block the thread) since I can't return until it's done anyway? If there's a problem with deserialisation, a cancellation token couldn't be used.
If I should call asynchronously, should I use Task.Factory.StartNew() as suggested by intellisense, as a replacement for the deprecated JsonConvert.DeserializeObjectAsync()? This Channel 9 video suggests (at 58mins) that this isn't such a good idea. Perhaps another option, such as Task.Run()? Possibly a bad idea since it might cause SyncContext issues?
Any pointers gratefully received!
Your code is good as is. DeserializeObject will run inside a thread-pool thread since you are using ConfigureAwait(false).
Your overall method (GetDataAsync) would still be asynchronous since it will return to the caller on the first await.

Async method runs fine, but does not change anything

I want to read a XML file from the Web with following method.
public static async void Load_WinPhone(string URL)
{
HttpClient client = new HttpClient();
var httpResponseMessage = await client.GetAsync(new Uri(URL));
if (httpResponseMessage.StatusCode == System.Net.HttpStatusCode.OK)
{
var xmlStream = await httpResponseMessage.Content.ReadAsStreamAsync();
XDocument Xdoc = XDocument.Load(xmlStream);
var query = from data in Xdoc.Descendants("article")
select new MyClass
{
Title = data.Element("title").Value
}
foreach (MyClass x in query)
{
AnotherClass.List.Add(x);
}
}
This Works, but after the method finished the AnotherClass.List is still empty.
I think it is because of the async, I tried this in the console without the async and it worked fine.
But now i want to to this on a Windows Phone 8.1 and the list stays empty.
Can someone explain me why or even have a workaround for this?
Yes, that's how await works - from the point of view of the caller, it's basically the same thing as a return. So when you call this method, it most likely returns on the first await - long before AnotherClass.List is modified.
The main problem you have is that your method is async void - you're throwing away all the information about the method's execution. Instead, you want to return Task - this allows you to await the method or bind a continuation to it.
Whenever you break the await chain, you also break the synchronicity of the code. Most of the time (especially in UI), you want to await all the way to the top - usually, the only thing that's async void is the event handlers, and even then it's only because event handlers must return void.
Overall, multi-threading and asynchronous code is a rather big topic - http://www.albahari.com/threading/ is a great start on understanding most of the fundamentals, as well as ways to handle it well in C#.

How to cancel a TaskCompletionSource using a timeout

I have the function that I call asynchronously using the await keyword:
public Task<StatePropertyEx> RequestStateForEntity(EntityKey entity, string propName)
{
var tcs = new TaskCompletionSource<StateInfo>();
try
{
var propInstance = BuildCacheKey(entity, propName);
StateCacheItem cacheItem;
if (_stateCache.TryGetValue(propInstance, out cacheItem))
{
tcs.SetResult( new StateInfo (cacheItem.State.Name, cacheItem.State.Value) );
return tcs.Task;
}
//state not found in local cache so save the tcs for later and request the state
var cacheKey = BuildCacheKey(entity, propName);
_stateRequestItemList.TryAdd(cacheKey, new StateRequestItem(entity, propName, tcs));
_evtClient.SubmitStateRequest(entity, propName);
return tcs.Task;
}
catch (Exception ex)
{
tcs.SetException(ex);
return tcs.Task;
}
}
The function has a look to see if it has the information it needs and if it does it returns it. If it doesn’t have the details it sends a request out which should eventually come in as an event. At that point my code (not shown) finds the stored TaskCompletionSource item, sets the result and returns it. This all works fine but I have now been asked to consider a situation where a reply may never be returned when I request state via the “_evtClient.SubmitStateRequest(entity, propName);” line. I need to implement some sort of timeout mechanism so I can cancel the TCS task so the function caller can fail gracefully. I’ve been looking on SO and the internet and can’t find anything that looks right. I’m now not sure if I need to restructure the above code in a different way. Can anyone advise or point me to a similar scenario?
The code that calls the above function can call it in a single hit like this:
var stateProperty = await RequestStateForEntity(key, stateName);
or in a batch, like this:
await
Task.WhenAll(
stateDefinitions.Select(stateDefinition => stateDefinition.Name)
.Select(
stateName =>
Task.Factory.StartNew(
async () => results.Add(await RequestStateForEntity(key, stateName)))
.Unwrap())
.ToArray());
First off, what you really want to enable is cancellation. The fact that the cancellation comes from a timeout is just a footnote.
.NET has some great built-in support for cancellation, and the Task-based Asynchronous Pattern prescribes how to use it.
Essentially, what you want to do is take a CancellationToken:
Task<StatePropertyEx> RequestStateForEntity(EntityKey entity, string propName,
CancellationToken cancellationToken);
Next, you want to respond when that token is signaled. Ideally, you would want to just pass the CancellationToken down to the _evtClient so that the request is truly cancelled:
_evtClient.SubmitStateRequest(entity, propName, cancellationToken);
This is the normal way of implementing cancellation, and it works great if SubmitStateRequest already understands cancellation. Often the event arguments have a flag indicating cancellation (e.g., AsyncCompletedEventArgs.Cancelled). If at all possible, use this approach (i.e., change _evtClient as necessary to support cancellation).
But sometimes this just isn't possible. In this case, you can choose to pretend to support cancellation. What you're actually doing is just ignoring the request if it completes after it was cancelled. This is not the most ideal situation but sometimes you have no choice.
Personally, I don't really like this kind of approach since it makes the API "lie": the method signature claims to support cancellation but it really is just faking it. So first, I recommend documenting this. Put in a code comment apology explaining that _evtClient doesn't support cancellation, and the "cancellation" is actually just pretend cancellation.
Then, you'll need to hook into the CancellationToken yourself, after the state request item is in the list but before the actual request is sent:
var item = new StateRequestItem(entity, propName, tcs);
_stateRequestItemList.TryAdd(cacheKey, item);
item.CancellationRegistration = cancellationToken.Register(() =>
{
StateRequestItem cancelledItem;
if (!_stateRequestItemList.TryRemove(cacheKey, out cancelledItem))
return;
cancelledItem.TaskCompletionSource.TrySetCanceled();
});
_evtClient.SubmitStateRequest(entity, propName);
Finally, you'll need to update your event handler completion code (not shown) to ignore the situation where the state request item has already been removed, and to dispose the CancellationRegistration if the state request item is found.
Once your method supports cancellation, then it's easy to cancel via a timer:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
CancellationToken token = cts.Token;
or from any other kind of situation. Say, if the user cancels whatever (s)he's doing. Or if another part of the system decides it doesn't need that data anymore. Once your code supports cancellation, it can handle cancellation for any reason.
You may pass a CancellationToken to your method, which can internally implement the cancellation logic:
public Task<StatePropertyEx> RequestStateForEntity(
EntityKey entity, string propName, CancellationToken token)
{
var tcs = new TaskCompletionSource<StateInfo>();
try
{
// Cache checking
_evtClient.SubmitStateRequest(entity, propName, token);
return tcs.Task;
}
catch (Exception ex)
{
tcs.SetException(ex);
return tcs.Task;
}
}
And inside SubmitStateRequest:
token.ThrowIfCancellationRequest();
Note ThrowIfCancellationRequest will throw a OperationCanceledException which you will need to handle. If you are making a blocking call, you can internally set the CancellationTokenSource with a TimeSpan:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));

Why use HttpClient for Synchronous Connection

I am building a class library to interact with an API. I need to call the API and process the XML response. I can see the benefits of using HttpClient for Asynchronous connectivity, but what I am doing is purely synchronous, so I cannot see any significant benefit over using HttpWebRequest.
If anyone can shed any light I would greatly appreciate it. I am not one for using new technology for the sake of it.
but what i am doing is purely synchronous
You could use HttpClient for synchronous requests just fine:
using (var client = new HttpClient())
{
var response = client.GetAsync("http://google.com").Result;
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
// by calling .Result you are synchronously reading the result
string responseString = responseContent.ReadAsStringAsync().Result;
Console.WriteLine(responseString);
}
}
As far as why you should use HttpClient over WebRequest is concerned, well, HttpClient is the new kid on the block and could contain improvements over the old client.
I'd re-iterate Donny V. answer and Josh's
"The only reason I wouldn't use the async version is if I were trying
to support an older version of .NET that does not already have built
in async support."
(and upvote if I had the reputation.)
I can't remember the last time if ever, I was grateful of the fact HttpWebRequest threw exceptions for status codes >= 400. To get around these issues you need to catch the exceptions immediately, and map them to some non-exception response mechanisms in your code...boring, tedious and error prone in itself. Whether it be communicating with a database, or implementing a bespoke web proxy, its 'nearly' always desirable that the Http driver just tell your application code what was returned, and leave it up to you to decide how to behave.
Hence HttpClient is preferable.
For anyone coming across this now, .NET 5.0 has added a synchronous Send method to HttpClient. https://github.com/dotnet/runtime/pull/34948
The merits as to why where discussed at length here: https://github.com/dotnet/runtime/issues/32125
You can therefore use this instead of SendAsync. For example
public string GetValue()
{
var client = new HttpClient();
var webRequest = new HttpRequestMessage(HttpMethod.Post, "http://your-api.com")
{
Content = new StringContent("{ 'some': 'value' }", Encoding.UTF8, "application/json")
};
var response = client.Send(webRequest);
using var reader = new StreamReader(response.Content.ReadAsStream());
return reader.ReadToEnd();
}
This code is just a simplified example - it's not production ready.
public static class AsyncHelper
{
private static readonly TaskFactory _taskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
=> _taskFactory
.StartNew(func)
.Unwrap()
.GetAwaiter()
.GetResult();
public static void RunSync(Func<Task> func)
=> _taskFactory
.StartNew(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
Then
AsyncHelper.RunSync(() => DoAsyncStuff());
if you use that class pass your async method as parameter you can call the async methods from sync methods in a safe way.
it's explained here :
https://cpratt.co/async-tips-tricks/
If you're building a class library, then perhaps the users of your library would like to use your library asynchronously. I think that's the biggest reason right there.
You also don't know how your library is going to be used. Perhaps the users will be processing lots and lots of requests, and doing so asynchronously will help it perform faster and more efficient.
If you can do so simply, try not to put the burden on the users of your library trying to make the flow asynchronous when you can take care of it for them.
The only reason I wouldn't use the async version is if I were trying to support an older version of .NET that does not already have built in async support.
In my case the accepted answer did not work. I was calling the API from an MVC application which had no async actions.
This is how I managed to make it work:
private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static T RunSync<T>(Func<Task<T>> func)
{
CultureInfo cultureUi = CultureInfo.CurrentUICulture;
CultureInfo culture = CultureInfo.CurrentCulture;
return _myTaskFactory.StartNew<Task<T>>(delegate
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap<T>().GetAwaiter().GetResult();
}
Then I called it like this:
Helper.RunSync(new Func<Task<ReturnTypeGoesHere>>(async () => await AsyncCallGoesHere(myparameter)));
In the current era, the shortest answer to this question is quite straightforward: Literally all other prior .NET options other than HttpClient are now deprecated/obsolete.

Categories

Resources