I have the following code:
WebClient wc = new WebClient();
string result;
try
{
result = await wc.DownloadStringTaskAsync( new Uri( "http://badurl" ) );
}
catch
{
result = await wc.DownloadStringTaskAsync( new Uri( "http://fallbackurl" ) );
}
Basically I want to download from a URL and when it fails with an exception I want to download from another URL. Both time async of course. However the code does not compile, because of
error CS1985: Cannot await in the body of a catch clause
OK, it's forbidden for whatever reason but what's the correct code pattern here?
EDIT:
The good news is that C# 6.0 will likely allow await calls both in catch and finally blocks.
Update: C# 6.0 supports await in catch
Old Answer: You can rewrite that code to move the await from the catch block using a flag:
WebClient wc = new WebClient();
string result = null;
bool downloadSucceeded;
try
{
result = await wc.DownloadStringTaskAsync( new Uri( "http://badurl" ) );
downloadSucceeded = true;
}
catch
{
downloadSucceeded = false;
}
if (!downloadSucceeded)
result = await wc.DownloadStringTaskAsync( new Uri( "http://fallbackurl" ) );
Awaiting in a catch block is now possible as of the End User Preview of Roslyn as shown here (Listed under Await in catch/finally) and will be included in C# 6.
The example listed is
try … catch { await … } finally { await … }
Update: Added newer link, and that it will be in C# 6
This seems to work.
WebClient wc = new WebClient();
string result;
Task<string> downloadTask = wc.DownloadStringTaskAsync(new Uri("http://badurl"));
downloadTask = downloadTask.ContinueWith(
t => {
return wc.DownloadStringTaskAsync(new Uri("http://google.com/")).Result;
}, TaskContinuationOptions.OnlyOnFaulted);
result = await downloadTask;
Give this a try:
try
{
await AsyncFunction(...);
}
catch(Exception ex)
{
Utilities.LogExceptionToFile(ex).Wait();
//instead of "await Utilities.LogExceptionToFile(ex);"
}
(See the Wait() ending)
Use C# 6.0. see this Link
public async Task SubmitDataToServer()
{
try
{
// Submit Data
}
catch
{
await LogExceptionAsync();
}
finally
{
await CloseConnectionAsync();
}
}
You could put the await after the catch block followed by a label, and put a goto in the try block.
(No, really! Goto's aren't that bad!)
The pattern I use to rethrow the exception after await on a fallback task:
ExceptionDispatchInfo capturedException = null;
try
{
await SomeWork();
}
catch (Exception e)
{
capturedException = ExceptionDispatchInfo.Capture(e);
}
if (capturedException != null)
{
await FallbackWork();
capturedException.Throw();
}
You can use a lambda expression as follows:
try
{
//.....
}
catch (Exception ex)
{
Action<Exception> lambda;
lambda = async (x) =>
{
// await (...);
};
lambda(ex);
}
In a similar instance, I was unable to await in a catch block. However, I was able to set a flag, and use the flag in an if statement (Code below)
---------------------------------------...
boolean exceptionFlag = false;
try
{
do your thing
}
catch
{
exceptionFlag = true;
}
if(exceptionFlag == true){
do what you wanted to do in the catch block
}
Related
I want to do 4 actions and each one can throw an exception. I need to execute them all and log all errors that happened along the way
I can do it in a big and clumsy way :
var exceptions = new List<ExceptionType1>();
try {
result1 = await action1();
} catch (ExceptionType1 ex1) {
exceptions.Add(ex1);
logger.Log(ex1);
}
try {
result2 = await action2();
} catch (ExceptionType1 ex1) {
exceptions.Add(ex1);
logger.Log(ex1);
}
try {
result3 = await action3(result1);
} catch (ExceptionType2 ex2) {
var ex1 = new ExceptionType1(ex2);
exceptions.Add(ex1);
logger.Log(ex1);
}
try {
result4 = await action4(result2);
} catch (ExceptionType2 ex2) {
var ex1 = new ExceptionType1(ex2);
exceptions.Add(ex1);
logger.Log(ex1);
}
if (exceptions.Count > 0) {
return false;
}
Obviously sometimes things like that bloat in size quickly
I would like to do it in more elegant way :
var exceptions = new List<ExceptionType1>();
try {
result1 = await action1();
result2 = await action2();
result3 = await action3(result1);
result4 = await action4(result2);
} catch(Exception ex) {
var ex1 = new ExceptionType1(ex);
exceptions.Add(ex1);
logger.Log(ex1);
???
~~Goto try block and continue executing~~
???
}
if (exceptions.Count > 0) {
return false;
}
I tried to find info how to get back into try block and continue executing but was unsuccessfull. Is it possible. Is there any other way around the problem that I didn't consider?
One way I guess (given your reequipments) is to create a local method
var exceptions = new List<Exception>();
async Task<T> DoAsync<T>(Func<Task<T>> func)
{
try
{
return await func();
}
catch (Exception ex)
{
exceptions.Add(ex);
logger.Log(ex);
}
return default;
}
result1 = await DoAsync(action1);
result2 = await DoAsync(action2);
if (exceptions.Count > 0)
{
return false;
}
Maybe you can extract this into a local function:
// declare this inside the method that contains the code you showed
async void GetResult<TResult, TException>(out TResult result, Func<Task<TResul>> resultGetter, Func<TException, ExceptionType1> mapper)
where TException: Exception {
try {
result = await resultGetter();
} catch (TException ex) {
var ex1 = mapper(ex)
exceptions.Add(ex1);
logger.Log(ex1);
}
}
Callers:
GetResult(out result1, action1, (ExceptionType1 e) => e);
GetResult(out result2, action2, (ExceptionType1 e) => e);
GetResult(out result3, async () => await action3(result1), (ExceptionType2 e) => new ExceptionType1(e));
GetResult(out result4, async () => await action4(result2), (ExceptionType2 e) => new ExceptionType1(e));
how to get back into try block and continue executing.. Is it possible?
You can't do what you are looking for: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-catch
The block is executed until an exception is thrown or it is completed successfully.
Once out of the block, even the variables that have been initialized are rolled back.
It's the whole point of the exception to stop the execution of the safe guarded code.
Follow the recommendations of the other two answers to simplify your code
I think WhenAll() method for Task will suit your purpose:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-catch#taskwhenall-example
First of all, your actions are already async. Moreover, you can divide to several WhenAll() blocks if you need to follow any order of execution
Example from docs:
public async Task action1()
{
throw new InvalidOperationException();
}
public async Task action2()
{
throw new NotImplementedException();
}
public async Task action3()
{
throw new InvalidCastException();
}
public async Task DoMultipleAsync()
{
Task theTask1 = action1();
Task theTask2 = action2();
Task theTask3 = action3();
Task allTasks = Task.WhenAll(theTask1, theTask2, theTask3);
try
{
await allTasks;
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex.Message);
Console.WriteLine("Task IsFaulted: " + allTasks.IsFaulted);
foreach (var inEx in allTasks.Exception.InnerExceptions)
{
Console.WriteLine("Task Inner Exception: " + inEx.Message);
}
}
}
// Output:
// Exception: Operation is not valid due to the current state of the object.
// Task IsFaulted: True
// Task Inner Exception: Operation is not valid due to the current state of the object.
// Task Inner Exception: The method or operation is not implemented.
// Task Inner Exception: Specified cast is not valid.
I have a C# Windows application from where I'm making calls to a API using below code:
while (true)
{
try
{
using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, "Some URL"))
{
requestMessage.Headers.Add("Accept", "application/json");
response = await myHttpHelper.SendHttpRequest(requestMessage).ConfigureAwait(false);
}
break; // where the code smells is shown
}
catch (TaskCanceledException )
{
if (++attemptCount > 3)
{
throw;
}
Thread.Sleep(10000);
}
catch (Exception ex2)
{
throw;
}
}
Usually what happens is the get request to APIs gets cancelled when ever there is some network issue. So what I have done is whenever the task is getting cancelled, I attempt it for three times. If it doesn't work , then I throw an exception to the calling method. If it is successful with in this 3 attempts, I'm breaking the loop.
Now when I run Sonar analysis on my code, it is showing to remove break statement and refactor the code. How can I do that?
while (true) is an infinite loop.
Instead, I would rather use a (boolean) variable to check in while().
That gives you the opportunity to set this variable to false and avoid the break.
You shouldn't generally use while (true) (that's a potential infinite loop), even if you have somewhere a break statement it can sometimes happen that this statement might not be reached.
I would recommend a for loop:
for (int i = 0; i < 3; i++)
{
try
{
using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, "Some URL"))
{
requestMessage.Headers.Add("Accept", "application/json");
response = await myHttpHelper.SendHttpRequest(requestMessage).ConfigureAwait(false);
}
}
catch (TaskCanceledException)
{
Thread.Sleep(10000);
}
catch (Exception ex2)
{
throw;
}
}
Create a boolean variable called success and use that as the while loop condition:
boolean success = false;
while (!success) {
try
{
using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, "Some URL"))
{
requestMessage.Headers.Add("Accept", "application/json");
response = await myHttpHelper.SendHttpRequest(requestMessage).ConfigureAwait(false);
}
success = true; <--- set to true here
}
...
}
If you don't go with Polly I would recommend a combination of #Sweeper's and #Magnus' reply:
const int retryLimit = 3;
boolean success = false;
int retryCounter = 0;
while (!success
&& retryCounter++ < retryLimit) {
try
{
// http request as is
success = true;
}
// Exception handling
}
This way you controll the number of retries and jump out of the loop when the request is successful.
I have this piece of code in a WebApi controller:
var task = await Request.Content.ParseMultipartAsync()
.ContinueWith<IHttpActionResult>(result =>
{
var data = result.Result;
var validateImage = new ImageValidator();
if (!validateImage.Validate(data.Files["image"]))
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
try
{
var newCategory = this.categoryService.Add(new Model.BusinessObjects.Category
{
Name = data.Fields["Name"].Value,
Description = data.Fields["description"].Value,
Logo = data.Files["image"].File
});
return Ok(newCategory.Id);
}
catch (System.Exception e)
{
return Content(HttpStatusCode.InternalServerError, e);
}
}).ConfigureAwait(false);
return task;
The sonar lint tell me to do that to avoid the warning but I don't know what does it mean, is it correct? before my code only has this line:
var data = await Request.Content.ParseMultipartAsync();
Does the first part of code solve the problem observed by sonar lint?
It seems like putting Task.WhenAll() in a try-catch block doesn't work. The code should upload all the images to ftp, but it seems like if one upload fails (for example, the image file is open in another process), the whole uploadTasks will stop, and counts is empty.
private async Task Upload(Ftp ftpHost)
{
var images = GetFileInfo() // will get a List<FileInfo>
var uploadTasks = images.Where(fi => fi.Exists).Select(async fi =>
{
var ret = await ftp.Upload(fi,_cancellationTokenSource.Token);
fi.Delete();
return ret;
});
IEnumerable<int> counts = new List<int>();
try
{
counts = await Task.WhenAll(uploadTasks);
}
catch
{
//ignore to allow all upload complete
}
var uploadedOrderFileByteCount = counts.Sum();
var uploadedOrderFileCount = counts.Count();
}
Apart from the fact that an empty catch block is often a bad idea (try to catch the exception which the ftp upload can throw especifically), if that's what you want to do, then the easiest way is to catch inside the operation itself, something similar to the code below.
var uploadTasks = images.Where(fi => fi.Exists).Select(async fi =>
{
try
{
var ret = await ftp.Upload(fi,_cancellationTokenSource.Token);
fi.Delete();
return ret;
} catch {
// again, please don't do this...
return 0;
}
});
IEnumerable<int> counts = new List<int>();
try
{
counts = await Task.WhenAll(uploadTasks);
}
catch
{
// Something bad happened, don't just ignore it
}
I want to hit a plethora (100k+) of JSON files as rapidly as possible, serialize them, and store the HTTP response status code of the request (whether it succeeded or failed). (I am using System.Runtime.Serialization.Json and a DataContract). I intend to do further work with the status code and serialized object, but as a test bed I have this snippet of code:
List<int> ids = new List<int>();
for (int i = MIN; i < MAX; i++)
ids.Add(i);
var tasks = ids.Select(id =>
{
var request = WebRequest.Create(GetURL(id));
return Task
.Factory
.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, id)
.ContinueWith(t =>
{
HttpStatusCode code = HttpStatusCode.OK;
Item item = null;
try
{
using (var stream = t.Result.GetResponseStream())
{
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(Item));
item = ((Item)jsonSerializer.ReadObject(stream));
}
}
catch (AggregateException ex)
{
if (ex.InnerException is WebException)
code = ((HttpWebResponse)((WebException)ex.InnerException).Response).StatusCode;
}
});
}).ToArray();
Task.WaitAll(tasks);
Using this approach I was able to process files much more quickly than the synchronous approach I was doing before.
Though, I know that the GetResponseStream() throws the WebException when the status code is 4xx or 5xx. So to capture those status codes I need to catch this exception. However, in the context of this TPL it is nested in an InnerException on an AggregateException. This makes this line really confusing:
code = ((HttpWebResponse)((WebException)ex.InnerException).Response).StatusCode;
Though, this works... I was wondering if there is a better/clearer way to capture such an exception in this context?
Take a look at MSDN article: Exception Handling (Task Parallel Library)
For example, you might want to rewrite your code as follows:
try
{
using (var stream = t.Result.GetResponseStream())
{
DataContractJsonSerializer jsonSerializer = new
DataContractJsonSerializer(typeof(Item));
item = ((Item)jsonSerializer.ReadObject(stream));
}
}
catch (AggregateException ex)
{
foreach (var e in ex.InnerExceptions)
{
bool isHandled = false;
if (e is WebException)
{
WebException webException = (WebException)e;
HttpWebResponse response = webException.Response as HttpWebResponse;
if (response != null)
{
code = response.StatusCode;
isHandled = true;
}
}
if (!isHandled)
throw;
}
}
Try this on for size. GetBaseException returns the exception that caused the problem.
try
{
}
catch (System.AggregateException aex)
{
var baseEx = aex.GetBaseException() as WebException;
if (baseEx != null)
{
var httpWebResp = baseEx.Response as HttpWebResponse;
if (httpWebResp != null)
{
var code = httpWebResp.StatusCode;
// Handle it...
}
}
throw;
}