This is the error I get:
One or more errors occurred.
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at System.Threading.Tasks.Task`1.get_Result()
at myProject.mymethod(Int32[] myIds) in path\MyClass.cs:line 758
And here's that method:
private void mymethod(int[] myIds)
{
var uri = new Uri(string.Format(UriPath, string.Format(MyPath)));
var client = GetHttpClient(uri);
var postModel = new PostModel
{
Ids = myIds,
LastUpdate = NewItem ? null : _lastUpdated
};
if (client != null)
{
var response = client.PostAsJsonAsync(uri, postModel).Result;//this line crashes
if (response.IsSuccessStatusCode)
{
//doSomething
}
}
}
I call a lot of methods like this and all of them work except this one. When it's hit, it takes a lot of time and then this exception is throws. With all of the other methods the error doesn't happen.
This is the inner exception:
Inner.System.Threading.Tasks.TaskCanceledException: A task was canceled.
Here's my GetCLient() method:
private HttpClient GetHttpClient(Uri uri)
{
var handler = new HttpClientHandler
{
CookieContainer = CoockieContainer
};
return new HttpClient(handler)
{
BaseAddress = uri
};
}
Here's the API method:
[HttpPost]
public IList<MyModel> MyAPIMethod(PostModel model)
{
List<MyModel> myTranslations;
using (var db = new myEntities(GetDbConnStrByUser(new GetCookies().GetUserName())))
{
myTranslations = db.tblTranslations.Where(it => model.Ids.Contains(it.id)
&& (!model.Update.HasValue || it.update > model.LastUpdate.Value))
.Select(it => new MyModel
{
Id = it.id,
Name = it.name,
Description = it.desc,
LanguageId = it.language_id
}).ToList();
}
return myTranslations.GroupBy(x => new { x.Id, x.LanguageId }).Select(x => x.First()).ToList();
}
Maybe a timeout occurs.
Fiddler returns this error: The wait operation timed out.
.Result tries to turn Task into T. It synchronously blocks waiting for the task to complete and will only return T if no exceptions occur. The two types of exceptions you should anticipate are OperationCanceledExceptions/TaskCanceledExceptions (when the HTTP request times out or a CancellationToken was used and the token was cancelled--I don't think the latter is applicable here) and general/HTTP-related exceptions.
You're probably running into the HTTP timeout scenario. Is it taking a long time for the exception to get thrown? If not, what does your GetHttpClient method look like? Is it explicitly setting timeouts or cancellation tokens?
If you want to stick with the synchronous approach and not returns Tasks, you should do something like this instead:
try
{
var response = client.PostAsJsonAsync(uri, postModel).Result;//this line crashes
}
catch (OperationCanceledException oce)
{
// Tell the user that the request timed our or you cancelled a CancellationToken
}
catch (Exception exc)
{
// Look at the HttpClient docs and try to catch something more specific. You don't want
// to swallow StackOverflowExceptions or OutOfMemoryExceptions.
}
If you're willing to take the async plunge, this is more what you're looking for:
private async Task mymethodAsync(int[] myIds)
{
var uri = new Uri(string.Format(UriPath, string.Format(MyPath)));
var client = GetHttpClient(uri);
var postModel = new PostModel { ... };
if (client != null)
{
try
{
var response = await client.PostAsJsonAsync(uri, postModel);
if (response.IsSuccessStatusCode)
{
//doSomething
}
}
catch (OperationCanceledException oce)
{
// because timeouts are still possible
}
catch (Exception exc) {
// Because other things can still go wrong too (HTTP 500, parsing errors)
}
}
}
See here for more info on how .Result call works w/HttpClient: What happens while waiting on a Task's Result?
Related
In the below code for each request we are calling rest client in an Async manner. Rest client is basically a http client which is calling some webapis in a batch. If suppose AsyncTaskCount value is 5 then 5 request will be called asynchronously and then in the while block we are getting the result for each call. If any response out of those 5 request has an exception then the response will be faulted and IsFaulted becomes true for that particular request and in the response we can get the inner exception.
private async Task<List<RequestResponse>> ProcessInvestment(List<Request> Requests, List<Result> Results, ILogger log)
{
var requestResponses = new List<RequestResponse>();
var asyncTaskCount = Convert.ToInt32(Environment.GetEnvironmentVariable("AsyncTaskCount"));
log.LogInformation($"Start processing {Requests.Count} in batches of {asyncTaskCount}");
for (int index = 0; index < Requests.Count; index = index + asyncTaskCount)
{
var requestBatch = Requests.Skip(index).Take(asyncTaskCount).ToList();
var requests = requestBatch.Select(x => _restClient.RequestResponse(x)).ToList();
while (requests.Count > 0)
{
// Identify the first task that completes.
Task<RequestResponse> requestResponseTask = await Task.WhenAny(requests);
var requestResponse = new RequestResponse();
// ***Remove the selected task from the list so that you don't process it more than once
requests.Remove(requestResponseTask);
if (!requestResponseTask.IsFaulted)
{
// Await the completed task.
requestResponse = await requestResponseTask;
requestResponses.Add(requestResponse);
}
else
{
if (requestResponseTask.Exception.InnerException != null && requestResponseTask.Exception.InnerException is Exception)
{
var result = new Result();
result = ResponseTransformComponent.ResponseToResult(((Exception)requestResponseTask.Exception.InnerException).Request, null);
result.SetBadRequestErrorDetails(((Exception)RequestResponseTask.Exception.InnerException).BadRequestResponse);
results.Add(Result);
}
else
{
throw requestResponseTask.Exception;
}
}
}
log.LogInformation($"Number of records processed = {requestResponses.Count}");
}
log.LogInformation($"Total invalid and Bad requests count = {results.Count}");
return RequestResponses;
}
Below is the code for restclient which is called from the above method.
public async Task<Response> RequestResponse(Request request)
{
var response = await GetDataFromService("calculation", "CalculateCapital", request);
return JsonConvert.DeserializeObject<Response>(response);
}
public async Task<string> GetDataFromService(string controller, string method, object request)
{
var client = _httpClientFactory.CreateClient(ServiceEnum.DCS);
string baseAddress = client.BaseAddress.ToString();
var requestUrl = $"api/{controller}/{method}";
var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");
var response = await client.PostAsync(requestUrl, content).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var responseResult = response.Content.ReadAsStringAsync().Result;
if (response.StatusCode == HttpStatusCode.BadRequest)
{
throw new CalculatorServiceException("Bad Request", JsonConvert.DeserializeObject<BadRequestResponse>(responseResult), (Request)request);
}
throw new Exception($"Status code: {response.StatusCode}. {responseResult}");
}
return response.Content.ReadAsStringAsync().Result;
}
GetDataFromService method is called from Calculate method. And GetDataFromService method will return a custom exception if the request is a Bad Request.
I am trying to write the unit test case for the above method and try to mock a request so that it will return a faulted task and then IsFaulted should become true. Below is the part of my unit test case.
_restClient
.When(a => a.RequestResponse(Arg.Any<Request>()))
.Do(a => {Task.FromException(new CalculatorServiceException(string.Empty, new BadRequestResponse { Message = string.Empty, ModelState = new Dictionary<string, string[]> { { "CalculationDates.StartDate", new string[] { "0002: Duration too short to execute calculations (CalculationDates.StartDate)" } } } }, Arg.Any<Request>())); });
If i mock my restclient method like above then it is throwing the exception instead of giving the response with IsFaulted to true. So how should i mock the restclient method so that it will return a faulted task which has an exception instead of throwing it. Please suggest.
Thanks in advance.
When..Do is for wiring up callbacks for when a member is called.
Try using Returns instead:
_restClient.RequestResponse(Arg.Any<Request>())
.Returns(x => Task.FromException<RequestResponse>(...));
// (assuming RequestResponse(..) returns a Task<RequestResponse>. Tweak as required)
The following code run M1, M2, M3, M4 in parallel. Each method may raise exceptions. The method should return the results of the four async methods - either the int returned by methods or the Exceptions.
async Task<string> RunAll()
{
int m1result, m2result, m3result, m4result;
try
{
var m1task = M1();
var m2task = M2();
var m3task = M3();
var m4task = M4();
// await Task.WhenAll(new Task<int>[] { m1task, m2task, m3task, m4task });
m1result = await m1task;
m2result = await m2task;
m3result = await m3task;
m4result = await m4task;
}
catch (Exception ex)
{
// need to return the ex of the failed task. How?
}
// How to implement M1HasException, M2HasException, ... in the following lines?
var m1msg = M1HasException ? M1ExceptionMessage : m1result.ToString();
var m2msg = M2HasException ? M2ExceptionMessage : m2result.ToString();
var m3msg = M3HasException ? M3ExceptionMessage : m3result.ToString();
var m4msg = M4HasException ? M4ExceptionMessage : m4result.ToString();
return $"M1: {m1msg}, M2: {m2msg}, M3: {m3msg}, M4: {m4msg}";
}
How to capture the individual exceptions of the failed task?
For example, if only M2 threw an exception,
"M1: 1, M2: Excpetion...., M3: 3, M4: 4"
Each task has a Status and and Exception property.
You may want to see if it has faulted:
myTask.Status == TaskStatus.Faulted
Or if it has excepted:
if (myTask.Exception != null)
You can use ContinueWhenAll to run all the tasks and then check the status.
See the docs here.
As other answers/comments pointed out, one possible approach is by using ContinueWith or ContinueWhenAll. This is a clever trick because Task has the Exception property:
Gets the AggregateException that caused the Task to end prematurely.
If the Task completed successfully or has not yet thrown any
exceptions, this will return null.
Using ContinueWith whether a task completes successfully or not, it will be passed as the argument to the delegate function. From there you can check if an exception was thrown.
Task<string> GetStringedResult<T>(Task<T> initialTask)
{
return initialTask.ContinueWith(t => {
return t.Exception?.InnerException.Message ?? t.Result.ToString();
});
}
async Task<string> RunAll()
{
string m1result, m2result, m3result, m4result;
var m1task = GetStringedResult(M1());
var m2task = GetStringedResult(M2());
var m3task = GetStringedResult(M3());
var m4task = GetStringedResult(M4());
m1result = await m1task;
m2result = await m2task;
m3result = await m3task;
m4result = await m4task;
return $"M1: {m1result}, M2: {m2result}, M3: {m3result}, M4: {m4result}";
}
You can wrap the tasks inside a WaitAll and catch the AggregateException (docs),
try
{
Task.WaitAll(new[] { task1, task2 }, token);
}
catch (AggregateException ae)
{
foreach (var ex in ae.InnerExceptions)
//Do what ever you want with the ex.
}
Could you wrap each await in try-catch block and capture the exception message if any, seems feasible...
var results = new List<string>();
try { results.Add(await t1); } catch { results.Add("Exception"); };
try { results.Add(await t2); } catch { results.Add("Exception"); };
try { results.Add(await t3); } catch { results.Add("Exception"); };
return string.Join("|", results);
if you want to use WhenAll you could await for it and ignore exceptions and then do the same exercise as shown above to retrieve individual task results...
try { await Task.WhenAll(t1, t2, t3); } catch { };
// ^^^^^^^^^
// then same as ^ above
I have the code :
public static async Task Download()
{
var urls = new[] {
"https://github.com/naudio/NAudio",
"https://twitter.com/mark_heath",
"https://github.com/markheath/azure-functions-links",
"https://pluralsight.com/authors/mark-heath",
"https://github.com/markheath/advent-of-code-js",
"http://stackoverflow.com/users/7532/mark-heath",
"https://mvp.microsoft.com/en-us/mvp/Mark%20%20Heath-5002551",
"https://github.com/markheath/func-todo-backend",
"https://github.com/markheath/typescript-tetris",};
var client = new HttpClient();
foreach (var url in urls)
{
var html = await client.GetStringAsync(url);
Console.WriteLine($"retrieved {html.Length} characters from {url}");
}
}
The all task client.GetStringAsync(url) will execute as the same time. But I wanna call client.GetStringAsync each url after timeout (no need to await the previous task complete). Ex :
at 00:00:01 - GetStringAsync url1
at 00:00:05 - GetStringAsync url2
at 00:00:09 - GetStringAsync url3
Each task will be started after 4 seconds. So how can I do that? Thanks
If you don't want to await the querying of the url, then don't await it. If you want to run the continuation after 4 seconds (or any other fixed period of time), then use Task.Delay.
public static async Task Download()
{
var urls = new[] {
"https://github.com/naudio/NAudio",
"https://twitter.com/mark_heath",
"https://github.com/markheath/azure-functions-links",
"https://pluralsight.com/authors/mark-heath",
"https://github.com/markheath/advent-of-code-js",
"http://stackoverflow.com/users/7532/mark-heath",
"https://mvp.microsoft.com/en-us/mvp/Mark%20%20Heath-5002551",
"https://github.com/markheath/func-todo-backend",
"https://github.com/markheath/typescript-tetris",};
var client = new HttpClient();
foreach (var url in urls)
{
ProcessURL(url);
await Task.Delay(TimeSpan.FromSeconds(4));
}
async Task ProcessURL(string url)
{
try
{
var html = await client.GetStringAsync(url);
Console.WriteLine($"retrieved {html.Length} characters from {url}");
}
catch (Exception e)
{
//TODO handle any errors in processing the URL
}
}
}
Note that since nothing is awaiting ProcessURL, it'll need to be responsible for handling any errors that might happen internally, since no caller will be able to handle them.
Do you mean you want to wait 4 seconds between tech call, instead of waiting for the previous call to complete?
You can use Task.Delay for that -
public static async Task Download()
{
var urls = new[] {
"https://github.com/naudio/NAudio",
"https://twitter.com/mark_heath",
"https://github.com/markheath/azure-functions-links",
"https://pluralsight.com/authors/mark-heath",
"https://github.com/markheath/advent-of-code-js",
"http://stackoverflow.com/users/7532/mark-heath",
"https://mvp.microsoft.com/en-us/mvp/Mark%20%20Heath-5002551",
"https://github.com/markheath/func-todo-backend",
"https://github.com/markheath/typescript-tetris",};
var client = new HttpClient();
foreach (var url in urls)
{
var waitingTask = Task.Delay(1000 * 4); //4000 milliseconds
var fireAndForget = client.GetStringAsync(url);
await waitingTask;
}
}
A couple of things to notice here -
If sending the HTTP call takes
more than 4 seconds, your code will be delayed for the amount of
time it takes to send it out, even if it's more than 4 seconds.
By
not waiting for the HTTP call to complete, you can't use the
resulting HTML
You can solve this issue with something like a continuation -
client.GetStringAsync(url).ContinueWith(task => Console.WriteLine($"retrieved {task.Result.Length} characters from {url}"));
Edit - adding for completeness as per #Servy's comment -
My continuation example above is very partial, and meant to convey the idea of handling the result of a task that was not awaited. There are multiple ways to handle the the download process, both in terms of using the result and of handling errors. Here is an approach that uses ContinueWith, but checks to see how the download completed instead of assuming is succeeded -
var task = client.GetStringAsync(url);
task.ContinueWith(task =>
{
// task completed correctly, do something with the result, like -
Console.WriteLine($"retrieved {task.Result.Length} characters from {url}");
}, TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(task =>
{
// task did not complete successfully, you can check why using the task iteslf, for example
if (task.IsFaulted)
{
// the task failed with an unhandled exception, you can access the exception instance if you want, for example -
Console.WriteLine($"Error while downloading from {url} - {task.Exception}");
}
}, TaskContinuationOptions.NotOnRanToCompletion);
Use Task.Delay Check
and wait 4 secands between each call
HttpClientHelper: HTTPrequest Wrapper
HttpResult: wrap request result
public static async Task Download()
{
HttpClientHelper clientHelper = new HttpClientHelper();
List<HttpResult> httpResults = new List<HttpResult>();
var urls = new[] {
"https://github.com/naudio/NAudio",
"https://twitter.com/mark_heath",
"https://github.com/markheath/azure-functions-links",
"https://pluralsight.com/authors/mark-heath",
"https://github.com/markheath/advent-of-code-js",
"https://stackoverflow.com/users/7532/mark-heath",
"https://mvp.microsoft.com/en-us/mvp/Mark%20%20Heath-5002551",
"https://github.com/markheath/func-todo-backend",
"https://github.com/markheath/typescript-tetris",};
foreach (var url in urls)
{
HttpResult httpResult = clientHelper.GetStringAsync(url);
httpResults.Add(httpResult);
if (httpResult.HasError)
{
Console.WriteLine($"Error occurred: '{httpResult.Error}' on characters from {url}");
}
else
{
Console.WriteLine($"retrieved {httpResult.Result.Length} characters from {url}");
}
await Task.Delay(5000);
}
}
public class HttpClientHelper
{
public async Task<HttpResult> GetStringAsync(string url)
{
HttpResult httpResult = new HttpResult
{
URL = url
};
try
{
HttpClient client = new HttpClient();
httpResult.Result = await client.GetStringAsync(url);
}
catch (Exception e)
{
//todo:handel Exception
httpResult.HasError = true;
httpResult.Error = e.Message + Environment.NewLine + e.InnerException?.Message;
}
return httpResult;
}
}
public class HttpResult
{
public string URL { get; set; }
public bool HasError { get; set; }
public string Error { get; set; }
public string Result { get; set; }
}
I use RestSharp to pass data between the clien-side (Xamarin android app) and my server.
When there is an error (usually because the server is down) the method that execute the request throw an exception.
I want the exception to go back all the way to the method who called it, so I can throw an error to the user.
For example, I want to login, but lets say the server is down.
A - The method that execute the request
public Task<T> ExecuteAsync<T>(RestRequest request) where T : new()
{
var client = new RestClient
{
BaseUrl = new Uri(BaseUrl),
Authenticator = new HttpBasicAuthenticator(_accountName, _password)
};
var taskCompletionSource = new TaskCompletionSource<T>();
client.ExecuteAsync<T>(request, restResponse =>
{
if (restResponse.ErrorException != null)
{
throw (new Exception("Server returned an error"));
}
taskCompletionSource.SetResult(restResponse.Data);
});
return taskCompletionSource.Task;
}
B - Method that uses method A to execute a request
public static async Task<LoginObject> Login(string accessNumber, string password, string token)
{
var request = new RestRequest
{
Method = Method.POST,
Resource = "Login"
};
request.AddJsonBody(
new
{
accessNumber = accessNumber,
password = password,
token = token
});
var isDone = await Api.ExecuteAsync<LoginObject>(request);
return isDone;
}
C - The method where I want to handle the exception
public async Task Login(string PhoneNumber, string Password)
{
try
{
LoginObject login = await LoginServices.Login(PhoneNumber, Password, Token);
if (login.IsOk)
{
// Move to next activity
}
else
{
Toast.MakeText(this, "Login Error", ToastLength.Short).Show();
}
}
catch (Exception ex) // Here I want to throw the server error
{
Toast.MakeText(this, "Server Error", ToastLength.Short).Show();
return null;
}
}
Now when I run the code, the error is being thrown in A, and the app crash,
I want it to go from A to B and from B to C, and then I'll show an error to the user.
Edit: I tried to put a try/catch block but it still throws the exception in A.
Change method A to have async in the signature and then change your last line to return await taskCompletionSource.Task;
In your A-method, please use taskCompletionSource.SetException like this:
if (restResponse.ErrorException != null)
{
//throw new Exception("Server returned an error");
taskCompletionSource.SetException(new Exception("Server returned an error"));
}
else
{
taskCompletionSource.SetResult(restResponse.Data);
}
In your B-method replace this line:
var isDone = await Api.ExecuteAsync<LoginObject>(request);
with this to re-throw the exception to you C-method:
LoginObject isDone=null;
try
{
isDone = await Api.ExecuteAsync<LoginObject>(request);
}
catch (Exception e)
{
throw e;
}
This article is talking about TaskCompletionSource
I would like to fire several tasks while setting a timeout on them. The idea is to gather the results from the tasks that beat the clock, and cancel (or even just ignore) the other tasks.
I tried using extension methods WithCancellation as explained here, however throwing an exception caused WhenAll to return and supply no results.
Here's what I tried, but I'm opened to other directions as well (note however that I need to use await rather than Task.Run since I need the httpContext in the Tasks):
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
IEnumerable<Task<MyResults>> tasks =
from url in urls
select taskAsync(url).WithCancellation(cts.Token);
Task<MyResults>[] excutedTasks = null;
MyResults[] res = null;
try
{
// Execute the query and start the searches:
excutedTasks = tasks.ToArray();
res = await Task.WhenAll(excutedTasks);
}
catch (Exception exc)
{
if (excutedTasks != null)
{
foreach (Task<MyResults> faulted in excutedTasks.Where(t => t.IsFaulted))
{
// work with faulted and faulted.Exception
}
}
}
// work with res
EDIT:
Following #Servy's answer below, this is the implementation I went with:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
IEnumerable<Task<MyResults>> tasks =
from url in urls
select taskAsync(url).WithCancellation(cts.Token);
// Execute the query and start the searches:
Task<MyResults>[] excutedTasks = tasks.ToArray();
try
{
await Task.WhenAll(excutedTasks);
}
catch (OperationCanceledException)
{
// Do nothing - we expect this if a timeout has occurred
}
IEnumerable<Task<MyResults>> completedTasks = excutedTasks.Where(t => t.Status == TaskStatus.RanToCompletion);
var results = new List<MyResults>();
completedTasks.ForEach(async t => results.Add(await t));
If any of the tasks fail to complete you are correct that WhenAll doesn't return the results of any that did complete, it just wraps an aggregate exception of all of the failures. Fortunately, you have the original collection of tasks, so you can get the results that completed successfully from there.
var completedTasks = excutedTasks.Where(t => t.Status == TaskStatus.RanToCompletion);
Just use that instead of res.
I tried you code and it worked just fine, except the cancelled tasks are in not in a Faulted state, but rather in the Cancelled. So if you want to process the cancelled tasks use t.IsCanceled instead. The non cancelled tasks ran to completion. Here is the code I used:
public static async Task MainAsync()
{
var urls = new List<string> {"url1", "url2", "url3", "url4", "url5", "url6"};
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
IEnumerable<Task<MyResults>> tasks =
from url in urls
select taskAsync(url).WithCancellation(cts.Token);
Task<MyResults>[] excutedTasks = null;
MyResults[] res = null;
try
{
// Execute the query and start the searches:
excutedTasks = tasks.ToArray();
res = await Task.WhenAll(excutedTasks);
}
catch (Exception exc)
{
if (excutedTasks != null)
{
foreach (Task<MyResults> faulted in excutedTasks.Where(t => t.IsFaulted))
{
// work with faulted and faulted.Exception
}
}
}
}
public static async Task<MyResults> taskAsync(string url)
{
Console.WriteLine("Start " + url);
var random = new Random();
var delay = random.Next(10);
await Task.Delay(TimeSpan.FromSeconds(delay));
Console.WriteLine("End " + url);
return new MyResults();
}
private static void Main(string[] args)
{
MainAsync().Wait();
}