How to handle UnauthorizedAccessException in asynchronous code? - c#

Catching an UnauthorizedAccessException works fine, but it doesn't when I make the File.WriteAllBytes -> await Task.Run(() => File.WriteAllBytes(Path + vid.FullName, vid.GetBytes()));
When I do this the exception doesn't get caught. What's the best way to catch this exception, so that the MessageBox shows? Thanks.
(NOTE: This code is clearly a YouTube converter, but it is for my own personal use)
public async Task ConvertVideoAndDownloadToFolder(string link, string MP3Name = null)
{
try
{
var youtube = YouTube.Default;
var vid = await Task.Run(() => youtube.GetVideo(link));
await Task.Run(() => File.WriteAllBytes(Path + vid.FullName, vid.GetBytes()));
var inputFile = new MediaFile { Filename = Path + vid.FullName };
MediaFile outputFile;
//Creates mp4 and mp3
if (String.IsNullOrEmpty(MP3Name)) { outputFile = new MediaFile { Filename = $"{Path + vid.FullName}.mp3" }; }
//only creates MP4
else { outputFile = new MediaFile { Filename = $"{MP3Name}.mp3" }; }
using (var engine = new Engine())
{
await Task.Run(() => engine.GetMetadata(inputFile));
await Task.Run(() => engine.Convert(inputFile, outputFile));
}
}
catch (Exception ex)
{
if(ex is UnauthorizedAccessException)
{
MessageBox.Show($"Can't write on '{Path}' as it is protected.\rPlease choose another folder. ", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
else if(ex is ArgumentException)
{
MessageBox.Show($"'{link}' is not a valid YouTube link, \rMake sure the video still works, or check the link for typos. ", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
throw;
}
}
}

In Asynchronous programming it is very easy for exceptions to be swallowed as they typically run on another thread. This needs to be carefully managed
AggregateException stores all the exceptions that happen in different threads and therefore you do not lose any exception
catch (AggregateException aex)
{
foreach(var ex in aex.InnerExceptions) //This will consolidate t
{
Console.WriteLine(ex.Message);
}
}
Reference

Related

Task Cancellation Exception after 3rd call

I'm new here and I hope you can help me with a problem I've got.
I'm trying to call some API's from Unity UWP, but every time on the third call I've got an Task Cancellation Exception, no matter what the order is of calling the api's.
Here is my piece of code:
public class DashboardManager : MonoBehaviour
{
public TMP_Text Status;
async Task Awake()
{
await GetPerformanceInfo().ConfigureAwait(false);
await GetProductionInfo().ConfigureAwait(false);
await GetChecklistInfo().ConfigureAwait(false);
}
private async Task GetChecklistInfo()
{
try
{
HttpClient httpClient = new HttpClient((HttpMessageHandler)ApiHelpers.HttpClientHandler);
httpClient.BaseAddress = new Uri(ApiHelpers.PlantApiBaseAddress.CheckBaseUrl(true));
var plantApiClient = new PlantApiClient(httpClient, default); // await ProgramManager.ServiceProvider.GetService<PlantApiClient>()
var checklists = await plantApiClient.GetChecklistWidgetInfo(UserSettings.Location);
//Status.text = "Checklist OK";
}
catch (LogoutException ex)
{
SceneManager.LoadScene("LoginScene");
}
catch (TaskCanceledException ex)
{
Status.text = "Checklist: " + ex.Message;
}
catch (Exception ex)
{
Status.text = "Checklist: " + ex.Message;
}
}
public void OnClickButton(string guid)
{
GlobalManager.CurrentChecklistID = guid;
SceneManager.LoadScene("ChecklistsScene");
}
private async Task GetPerformanceInfo()
{
try
{
HttpClient httpClient = new HttpClient((HttpMessageHandler)ApiHelpers.HttpClientHandler);
httpClient.BaseAddress = new Uri(ApiHelpers.PlantApiBaseAddress.CheckBaseUrl(true));
var plantApiClient = new PlantApiClient(httpClient, default); // await ProgramManager.ServiceProvider.GetService<PlantApiClient>()
var perfInfo = await plantApiClient.GetPerformanceWidgetInfo(UserSettings.Location);
//Status.text = "Performance OK";
}
catch (LogoutException ex)
{
SceneManager.LoadScene("LoginScene");
}
catch (TaskCanceledException ex)
{
Status.text = "Performance: " + ex.Message;
}
catch (Exception ex)
{
Status.text = "Performance: " + ex.Message;
}
}
private async Task GetProductionInfo()
{
try
{
HttpClient httpClient = new HttpClient((HttpMessageHandler)ApiHelpers.HttpClientHandler);
httpClient.BaseAddress = new Uri(ApiHelpers.PlantApiBaseAddress.CheckBaseUrl(true));
var plantApiClient = new PlantApiClient(httpClient, default); // await ProgramManager.ServiceProvider.GetService<PlantApiClient>()
var prodInfo = await plantApiClient.GetProductionWidgetInfo(UserSettings.Location);
//Status.text = "Production OK";
}
catch (LogoutException ex)
{
SceneManager.LoadScene("LoginScene");
}
catch (TaskCanceledException ex)
{
Status.text = "Production: " + ex.Message;
}
catch (Exception ex)
{
Status.text = "Production: " + ex.Message;
}
}
}
In the Awake Task, the 3rd call always fails with an TaskCancellation Exception.
If I change the order, it always fails on the third call.
There is nothing wrong with the api calls, because they are working as expected. If I just call one or two, everything works fine, but when I call a 3rd one, it fails.
What's happening here, I hope somebody can help me.
Damn, this took me 2 wasted days, just to figure out this was a bug in Unity 2020.3.11f1.
I installed version 2021.2.8f1 and everything is working fine now.

Using Parallel ForEach to POST in API and single Handle each Response

I would like to know if the code I produced is good practice and does not produce leaks, I have more than 7000 objects Participant which I will push individually and Handle the Response to save the "external" id in our database. First I use the Parallel ForEach on the list pPartcipant:
Parallel.ForEach(pParticipant, participant =>
{
try
{
//Create
if (participant.id == null)
{
ExecuteRequestCreate(res, participant);
}
else
{//Update
ExecuteRequestUpdate(res, participant);
}
}
catch (Exception ex)
{
LogHelper.Log("Fail Parallel ", ex);
}
});
Then I do a classic (not async request), but after I need to "handle" the response (print in the console, save in a text file in async mode, and Update in my database)
private async void ExecuteRequestCreate(Uri pRes, ParticipantDo pParticipant)
{
try
{
var request = SetRequest(pParticipant);
//lTaskAll.Add(Task.Run(() => { ExecuteAll(request, pRes, pParticipant); }));
//Task.Run(() => ExecuteAll(request, pRes, pParticipant));
var result = RestExecute(request, pRes);
await HandleResult(result, pParticipant);
//lTaskHandle.Add(Task.Run(() => { HandleResult(result, pParticipant); }));
}
catch (Exception e)
{
lTaskLog.Add(LogHelper.Log(e.Message + " " + e.InnerException));
}
}
Should I run a new task for handeling the result (as commented) ? Will it improve the performance ?
In comment you can see that I created a list of tasks so I can wait all at the end (tasklog is all my task to write in a textfile) :
int nbtask = lTaskHandle.Count;
try
{
Task.WhenAll(lTaskHandle).Wait();
Task.WhenAll(lTaskLog).Wait();
}
catch (Exception ex)
{
LogHelper.Log("Fail on API calls tasks", ex);
}
I don't have any interface it is a console program.
I would like to know if the code I produced is good practice
No; you should avoid async void and also avoid Parallel for async work.
Here's a similar top-level method that uses asynchronous concurrency instead of Parallel:
var tasks = pParticipant
.Select(participant =>
{
try
{
//Create
if (participant.id == null)
{
await ExecuteRequestCreateAsync(res, participant);
}
else
{//Update
await ExecuteRequestUpdateAsync(res, participant);
}
}
catch (Exception ex)
{
LogHelper.Log("Fail Parallel ", ex);
}
})
.ToList();
await Task.WhenAll(tasks);
And your work methods should be async Task instead of async void:
private async Task ExecuteRequestCreateAsync(Uri pRes, ParticipantDo pParticipant)
{
try
{
var request = SetRequest(pParticipant);
var result = await RestExecuteAsync(request, pRes);
await HandleResult(result, pParticipant);
}
catch (Exception e)
{
LogHelper.Log(e.Message + " " + e.InnerException);
}
}

Xamarin computer vision

I am trying to create a Xamarin application using Azure computer vision API, I got the API key and endpoint URL whenever i click a picture and set in image view and then describe image function not working and i am not getting output captions, my code is below:
private async void Button_Clicked(object sender, EventArgs e)
{
await CrossMedia.Current.Initialize();
try
{
if(!CrossMedia.Current.IsTakePhotoSupported&&!CrossMedia.Current.IsPickPhotoSupported)
{
await DisplayAlert("INFO", "CAMERA NOT AVAILABEL", "OK");
}
else
{
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
Directory="Images",
Name="test.jpg"
});
if (file==null)
{
await DisplayAlert("ERROR", "FILE NOT FOUND", "OK");
return;
}
img.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
return stream;
});
var visionapi = new ComputerVisionClient(new ApiKeyServiceClientCredentials(""));
visionapi.Endpoint = "";
var desc = await visionapi.DescribeImageInStreamAsync(file.GetStream());
foreach (var tag in desc.Captions)
{
cap.Text = cap.Text + "\n" + tag;
}
}
}
catch(Exception )
{
await DisplayAlert("", "ERROR", "OK");
}
}
DescribeImageInStreamAsync functon is not working it is moving into catch block after few minutes with the below error:
Microsoft.Azure.CognitiveServices.Vision.ComputerVisionErrororException: Operation returned an invalid status code 'Not Found'
Exception

OneDrive file only uploads when app is terminated

I'm having a problem uploading a file to OneDrive from a universal app that I can't understand or figure out how to debug. I used this guide to go through the process of getting file IDs and such and it worked great until a few hours ago.
Now I can get folder and file ids, so I assume that I am still successfully connecting to OneDrive and my internet connection is still working. But when I step into the BackgroundUploadAsync the thread or whatever that was was executing before never returns. In the code below, the message "Uploading new file to OneDrive..." never disappears.
Strangely, while it is uploading I can refresh my OneDrive folder on ie and I'll never see what I'm trying to upload. But once I stop the debugger, or terminate the app on the phone, I can instantly refresh and the file will be there.
Here is the method for uploading:
public async Task UploadToOneDrive(string folderID, string localFileName)
{
try
{
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync(localFileName);
string fileName = "backup-" + DateTime.Now.ToString("dd-MM") + ".db";
await file.CopyAsync(ApplicationData.Current.LocalFolder, fileName, NameCollisionOption.ReplaceExisting);
file = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
await connectClient.BackgroundUploadAsync(folderID,
fileName, file, OverwriteOption.Overwrite);
}
catch (LiveConnectException)
{
MessageDialog m = new MessageDialog("Could not connect to to OneDrive. Cloud sync will be stopped.");
m.ShowAsync();
}
catch (LiveAuthException)
{
MessageDialog m = new MessageDialog("Error authenticating OneDrive service. Please try cloud sync again later.");
m.ShowAsync();
}
catch (Exception ex)
{
MessageDialog m = new MessageDialog("Unknown exception occurred.\n\nError:{0}", ex.Message);
m.ShowAsync();
}
}
And here is the sync process
public async Task sync()
{
var sb = StatusBar.GetForCurrentView();
sb.ProgressIndicator.Text = "Syncing with OneDrive...";
await sb.ProgressIndicator.ShowAsync();
string cloudFolderID = await syncManager.CreateOrGetOneDriveFolderID("GlucoseCalculator", "Documents/");
string cloudFileID = await syncManager.GetFileID(DataManager.sqlFileName, "Documents/GlucoseCalculator/");
try
{
if (cloudFileID != null)
{
if (!(await dbManager.DoesFileExist(DataManager.sqlFileName)))
{
sb.ProgressIndicator.Text = "Downloading file from OneDrive...";
await syncManager.DownloadFromOneDrive(cloudFileID, DataManager.sqlFileName);
goto BREAK;
}
DateTime cloudLastEditDateTime = DateTime.Parse(await syncManager.GetFileProperty(cloudFileID, "updated_time"));
DateTime localLastEditDateTime = ApplicationData.Current.LocalFolder.GetFileAsync(DataManager.sqlFileName).GetResults().GetBasicPropertiesAsync().GetResults().DateModified.DateTime;
if (cloudLastEditDateTime > localLastEditDateTime)
{
sb.ProgressIndicator.Text = "Downloading file from OneDrive...";
await syncManager.DownloadFromOneDrive(cloudFileID, DataManager.sqlFileName);
}
else if (cloudLastEditDateTime < localLastEditDateTime)
{
sb.ProgressIndicator.Text = "Uploading file to OneDrive...";
await syncManager.UploadToOneDrive(cloudFolderID, DataManager.sqlFileName);
}
}
else if (cloudFileID == null)
{
sb.ProgressIndicator.Text = "Uploading new file to OneDrive...";
await syncManager.UploadToOneDrive(cloudFolderID, DataManager.sqlFileName);
}
}
catch (Exception)
{
sb.ProgressIndicator.Text = "Cloud synchronization failed.";
sb.ProgressIndicator.HideAsync();
return;
}
sb.ProgressIndicator.Text = "Synchronization complete!";
BREAK:;
await sb.ProgressIndicator.HideAsync();
}
Most likely, you are creating some object that implements IDisposible, but it is not in a using block. Maybe the StorageFile.

Await in catch block

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
}

Categories