I've encountered a few dificulties trying to handle some errors in the best way possible. One of my cases for example is NullReferenceException.
To be more clear let me explain it in a few words. When I make a call to a server to receive some pieces of information, in some cases the server might have some problems and it will return of course null.
What I've done is I display an alert to let the user know that he can try later again. After this I try to send him in the previous page for example. And after all this my app still crushes.
What I'd like to do is to simply display the alert and then let the user stay in the same page without the application crushing.
These are a few pieces of my code:
tasks.cs
public async Task<List<Idea>> GetIdeaAsync(string accesToken)
{
List<Idea> ideas = null;
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesToken);
var json = await client.GetStringAsync("http://www.getdata.de/api/ideas/");
var ideas = JsonConvert.DeserializeObject<List<Idea>>(json);
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Server Error", "There has been an server error. Please try later.", "OK");
if (ideas == null)
{
await Application.Current.MainPage.Navigation.PopAsync(); //actually I would like to stay in the same page
}
}
return ideas;
}
view.xaml.cs
private async void Button_Clicked(object sender, EventArgs e)
{
Tasks ts = new Tasks();
var ideas = await ts.GetIdeasAsync();
if (ideas == null)
{
Debug.WriteLine("hello");
//do nothing since the display alert is already shown
}
else
{
//code here
}
I would really appreciate if anyone can guide me to a "best-practice" approach. Thanks :)
You are declaring ideas in the try block, and then trying to access it in the catch block, where it is out of scope. (Visual Studio should give an Intellisense error)
Also, whenever manipulating the UI, you should always do it on the main thread. so move your DisplayAlert() code into
Device.BeginInvokeOnMainThread(async () =>
{
// await DisplayAlert(); move it into here
});
In addition, any PopAsync or PushAsync calls should also be done on the main UI thread. But calling PopAsync after an asynchronous call to an API not be a good idea, as the user may have already pressed the back button by the time the call returns.
As for the NullReferenceException, check to see if json is null before passing it to the DeserializeObject() function.
The problem it was pretty obvious actually since I after catch-ing the exception I would later continue with the code. So what I did was :
public async Task<List<Idea>> GetIdeaAsync(string accesToken)
{
List<Idea> ideas = null;
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesToken);
var json = await client.GetStringAsync("http://www.getdata.de/api/ideas/");
var ideas = JsonConvert.DeserializeObject<List<Idea>>(json);
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Server Error", "There has been an server error. Please try later.", "OK");
if (ideas == null)
{
//actually I would like to stay in the same page
return null; //-- added this line
}
}
return ideas;
}
Maybe it's not the best idea but it's working for me. Any other approach would be highly appreciated. :)
Related
If someone doesn't have a profile picture, a NotFound exception kicks me out of the code. How can I work around this?
My Method:
if(graphManager.Client.Users[user.Id].Photo.Content.Request().GetAsync().IsNull())
{
Debug.Log("profilepicture not found");
}
else
{
var photo = await graphManager.Client.Users[mitarbeiter.Id].Photo.Content.Request().GetAsync();`
}
Response: It skips the if cause it thinks it's not null
What I need is a Method to create a Debug.Log if the exception happens and not throw me out
It seems that getting an exception when a photo is not found is a valid case. See docs on errors.
Try to handle it in the following way:
try
{
Stream photoContent = await graphManager.Client.Users[user.Id].Photo.Content.Request().GetAsync();
}
catch (ServiceException ex) when (ex.IsMatch(GraphErrorCode.ItemNotFound.ToString()))
{
Debug.Log("profilepicture not found");
}
P.S. I'm not sure precisely which GraphErrorCode should be used here.
I don't know the library you are using but is it possible that in the moment you do
graphManager.Client.Users[user.Id].Photo.Content.Request().GetAsync().IsNull()
what happens is you are basically doing
var asyncResult = graphManager.Client.Users[user.Id].Photo.Content.Request().GetAsync();
if(asyncResult.IsNull())
which of course would be true since you didn't give it any time to actually perform the request and finish.
It should probably rather be
var photo = await graphManager.Client.Users[mitarbeiter.Id].Photo.Content.Request().GetAsync();
if(photo.IsNull() )
{
Debug.Log("profilepicture not found");
}
else
{
...
}
I am writing a Visual Studio extension in C# and I get a strange behavior on managing exception and displaying error messages. Basically, I just want to add some details to the exception message to help me investigate in case of a problem.
It all starts from a command on a context menu item and I suspect it may be related to threads management behind the async/await mechanism. But I am not sure I guess correctly and I am not able to find any solution. HELP!
It starts from my menu item callback:
internal sealed class My_RunAnalysis
{
//...
public static async Task InitializeAsync(AsyncPackage package)
{
// Switch to the main thread - the call to AddCommand in PS_RunAnalysis's constructor requires
// the UI thread.
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
OleMenuCommandService commandService = await package.GetServiceAsync((typeof(IMenuCommandService))) as OleMenuCommandService;
Instance = new My_RunAnalysis(package, commandService);
}
//...
private async void ExecuteAsync(object sender, EventArgs e)
{
try
{
await My_ViewModel.RunAnalysisAsync();
}
catch (Exception exc)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
MessageBox.Show(exc.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
//...
class My_ViewModel
{
async public static Task RunAnalysisAsync()
{
await My_Model.GetResultsListAsync();
}
}
//...
class My_Model
async public static Task GetResultsListAsync()
{
ResultsList = new My_ResultsList();
var rawResultsList = await QueryServerAsync<RawResultsListResponse>("GET", My_Request.GetResults());
//...
}
async public static Task<JsonResponse> QueryServerAsync<JsonResponse>(string method,
string request)
{
try
{
HttpResponseMessage response;
switch (method)
{
case "GET":
response = await _httpClient.GetAsync(request);
break;
case "POST":
default:
StringContent httpContent = new StringContent("", Encoding.UTF8, "application/json");
response = await _httpClient.PostAsync(request, httpContent);
break;
}
if (!response.IsSuccessStatusCode) //<<<<<<CASE #1
{
throw new My_Exception(
response.ReasonPhrase,
"Exception while querying server for " + request);
}
string serializedJson = await response.Content.ReadAsStringAsync();
// CASE #2>>>>>
var jsonResponse = serializer.Deserialize<JsonResponse>(serializedJson);
return jsonResponse;
}
catch (Exception e)
{
throw new My_Exception(
e.Message,
"Exception while querying server for " + request);
}
}
The strange thing is that:
When an error occurs in case #1 and I create a custom exception (my server responded but there was an internal error and I have a clean error code), the MessageBox in the catch of My_ViewModel::RunAnalysisAsync() will show correctly and immediately.
When a native exception occurs in case #2 (my server responded with malformed json and I get an exception from serializer.Deserialize), the MessageBox in the catch of My_ViewModel::RunAnalysisAsync() will not show, the IDE will hang for around 15s before restarting (and still not show the MessageBox).
Any idea what's wrong?
Thanks!
EDIT:
Seeing that the template for my custom command initializes also with SwitchToMainThreadAsync, I have tried to do the same with the Execute method. I updated the code above but it still does not work: an exception thrown by serializer.Deserialize will still freeze the UI for 10 to 15s and the MessageBox will not show!
Also note that the debugger can step immediately on "await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);" and go on next step to MessageBox. I would tend to suppose it means that the switch to the main thread is immediate but there is still something wrong...
Any idea what's wrong? I really need to capture exceptions a reliable way...
I could not find any explanation to the MessageBox working on a case and not on the other one. I ended up going to some log solution using FileStream.WriteAsync. Hence everything keeps async and I don't have to use MessageBox anymore.
use await JoinableTaskFactory.SwitchToMainThreadAsync(); to switch to the main thread JoinableTaskFactory is a member of AsyncPackage.
If it still doesn't work try
public static void ShowMessageBox(string title, string text)
{
Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
IVsUIShell uiShell = Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider.GetService(typeof(SVsUIShell)) as IVsUIShell;
Guid clsid = Guid.Empty;
int result;
Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(
0,
ref clsid,
title,
text,
string.Empty,
0,
OLEMSGBUTTON.OLEMSGBUTTON_OK,
OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
OLEMSGICON.OLEMSGICON_INFO,
0, // false
out result));
}
So I've been searching like a mad man after a way to solve this issue, but I can't seem to find an answer.
So, I need to send a POST request with HTTPClient in C# to a server, and if the server isn't running it will keep sending the request until it connects (or dies after a nr of attempts). But I always get the exception System.Net.Http.HttpRequestException, which wouldn't be a problem if I just could store it (or something) and try again.
I found a couple of ways that people tried to do this, and I've tried them all. Creating a for-loop that loops and catches the exception that the program throws, adds to the counter and tries again. Creating a while-loop that loops until the HttpResponseMessage.IsSuccessStatusCode == true. I've even gone to such lengts as restarting the program if it can't connect (yeah, I'm that desperate).
So, I had to see if anyone of you guys might have a solution to this problem, or if you maybe had a better way to solve this problem.
Here is the code im running, thanks for your help!
EDIT: Just to add some clarity, the exception is thrown at the "rep"-variable, and the code never runs further than to that variable. And I've tried to make the HTTPResponseMessage variable just a "var" and await the Postasync method to.
HttpResponseMessage rep = new HttpResponseMessage();
try
{
rep = client.PostAsync("https://localhost:9999/", content).Result;
}
catch (Exception e)
{
}
Task t1 = Task.Factory.StartNew(() => ContinueTrasmission(client, c1.name, c1.state));
You have to look for retry libraries, for example Polly
var policy = Policy
.Handle<HttpRequestException>()
.WaitAndRetry(_retryCount, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
policy.Execute(() => DoSomething());
A solution could be:
bool success = false;
while (!success)
{
var rep = new HttpResponseMessage();
try
{
rep = client.PostAsync("https://localhost:9999/", content).Result;
//No exception here. Check your condition and set success = true if satisfied.
}
catch (Exception e)
{
//Log your exception if needed
}
}
I recently updated sdk level to 6.0 in Xamarin.forms.
I used Xaml to place a map on a page. Since I updated to 6.0 permission is required to show the map. My problem now is I can't figure out how to request permission to show the map before the app attempts to show it. As a result I get an unhandled exception.
public MapPage()
{
Init();
InitializeComponent();
azureService = AzureService.defaultManager;
}
private async Task Init()
{
await RequestLocationPermission();
}
protected async override void OnAppearing()
{
base.OnAppearing();
MyMap.MoveToRegion(
MapSpan.FromCenterAndRadius(
new Position(0, 0),
Distance.FromMiles(10.0)));
}
private async Task RequestLocationPermission()
{
try
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Location);
if (status != PermissionStatus.Granted)
{
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Location))
{
await DisplayAlert("Need location", "Gunna need that location", "OK");
}
var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] { Permission.Location });
status = results[Permission.Location];
}
if (status == PermissionStatus.Granted)
{
}
else if (status != PermissionStatus.Unknown)
{
await DisplayAlert("Location Denied", "Can not continue, try again.", "OK");
}
}
catch (Exception ex)
{
}
}
If the map is set up in Xaml, how can I request permission before showing it?
The way your constructor is set up right now, you are starting the permission request in a task which will run in a separate thread. That means that InitializeComponent() will probably run before the user can grant permission. The problem is you can't make the constructor an async method so there isn't an easy way to get around this.
To make this work without to much effort, you can move the InitializeComponent() from your constructor into your "if (status == PermissionStatus.Granted)" block. It would probably look something like this:
if (status == PermissionStatus.Granted)
{
Device.BeginInvokeOnMainThread(() =>
{
InitializeComponent()
});
}
With this approach you will have to be careful what you do in OnAppearing() as it will probably be called before InitializeComponent(). If you try to access any of your UI components at that point it will fail.
However I think the better way to handle this is to move your permission request code one level up. In other words put it in the class where you are instantiating this page from. Then you can show this page if access is granted, or another page that does not have the map if access is denied. It would make for a better user experience.
I have used GetGeoPositionAsync() in my app and it works fine in the code of another feature, however when using it in the feature I am currently implementing, it freezes the first time I try to call it, and works the second time. I call this function when navigating from another page. Here's the code:
private async Task GetLocation()
{
Geolocator myGeolocator = new Geolocator();
Geoposition myGeoposition = null;
try
{
myGeoposition = await myGeolocator.GetGeopositionAsync();
}
catch (Exception ex)
{
if ((uint)ex.HResult == 0x80004004)
{
MessageBox.Show("Unauthorized access.");
}
}
}
I tried browsing SO, but haven't found a working solution for this. Why does it work in some pieces of code, and in some it just doesn't. What is the cause of this behaviour?
Ok, so I found a solution:
try
{
IAsyncOperation<Geoposition> locationTask = null;
try
{
locationTask = myGeolocator.GetGeopositionAsync(TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(3));
myGeoposition = await locationTask;
}
finally
{
if (locationTask != null)
{
if (locationTask.Status == AsyncStatus.Started)
locationTask.Cancel();
locationTask.Close();
}
}
}
This sounds like the position is cached! The first time you call the method it tries to get your position (this can take many seconds if you are inside a building), the second time it seems to use the cached position.