I have a static class with a async method in it . The async method awaits a user action (user needs to confirms on a dialog). I need to call this method in a constructor in App.xaml.cs (xamarin)
public static class StoragePermission
{
public static async Task Check()
{
try
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Storage);
if (status != PermissionStatus.Granted)
{
var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] { Permission.Storage });
status = results[Permission.Storage];
}
}
catch (Exception ex) {}
}
}
And I have tried calling this method in the constructor like this:
StoragePermission.Check().Wait();
But doing the above , I dont get the next code to invoke at all. After the user action, focus does not return to the code.
I have also tried it this way:
public Task StorageInitialization { get; private set; }
and then within constructor, I do
StorageInitialization = StoragePermission.Check();
But doing this code returns to the next line in constructor, but does not wait for the user action to be completed.
I appreciate if someone could help me with this
Related
I am trying to call Firebase functions inside a "DAL" class as I want to create some abstraction. I cannot figure out how to return a variable from inside the callback, or how to pass in a callback to execute from the controller class I created. How would I make UserExistsInFirebase async and call that from a controller class like
public void GetUser() {
bool exists = await firebase.UserExistsInFirebase("User1");
}
public Task<bool> UserExistsInFirebase(string Username)
{
bool isExists = false;
reference.Child("Users").Child(Username).Child("Username").Child(Username).GetValueAsync().ContinueWithOnMainThread(task =>
{
if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
//checks to see if we found another user with the same username
if (snapshot.GetValue(true) != null)
{
isExists = true;
}
}
});
return isExists;
}
In your current code the issue is that ContinueWithInMainThread is callback called some time later once the task is finished. In the meantime it doesn't block your UserExistsInFirebase at all which rather directly continues and returns false.
Note btw that a method would need to be async in order to be able to use await within it ;)
So instead of ContinueWithInMainThread you would rather make your task async and use await once more.
public async Task<bool> UserExistsInFirebase(string Username)
{
var snapShot = await reference.Child("Users").Child(Username).Child("Username").Child(Username).GetValueAsync();
return snapShot.GetValue(true) != null;
}
Now this would then not be returned in the main thread though ;)
You can however again ensure that by using e.g.
public void GetUser()
{
firebase.UserExistsInFirebase("User1").ContinueWithOnMainThread(exists =>
{
// Do something with the bool exists after the request has finished
});
// again anything here would be immediately executed before the request is finihed
}
so basically you have just abstracted the task but keep the handling of the callback on the top level where you are starting your request.
If you still want the error handling like before you could probably also do something like
public async Task<bool> UserExistsInFirebase(string Username)
{
bool isExists = false;
await reference.Child("Users").Child(Username).Child("Username").Child(Username).GetValueAsync().ContinueWith(task =>
{
if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
//checks to see if we found another user with the same username
if (snapshot.GetValue(true) != null)
{
isExists = true;
}
}
});
return isExists;
}
I'm trying to create a service to get all my boxes from the database like this :
[HttpGet]
[Route("GetBoxes/")]
[ResponseType(typeof(ResultQuery))]
public async Task<IHttpActionResult> GetBoxes()
{
try
{
var boxes = db.Boxes.ToList<Box>();
foreach (Box in boxes)
{
status.Add(GetBox(box));
}
return Ok(ConvertToResultQuery(boxes));
}
catch (Exception e)
{
return InternalServerError(e);
}
}
public GenericBox GetBox(Box box)
{
try
{
//Do a lot of stuff with the database
return genericBox;
}
catch (Exception e)
{
return null;
}
}
public static ResultQuery ConvertToResultQuery(object result)
{
return new ResultQuery(result);
}
Where ResultQuery has just one object attribute containing the result of my service.
The service is simple but for some reason it gives me this error when I try it in postman :
An asynchronous module or handler completed while an asynchronous operation was still pending.
VisualStudio also gives me a warning advising to use await but I don't understand where I should put it.
Your GetBoxes method is an async method, but your GetBox is not.
You say that you are doing db work inside the GetBox method but everything in that method is running synchronously. Consider changing the signature of GetBox to:
public async Task<GenericBox> GetBox(Box box)
{
try
{
//Do a lot of stuff with the database
//'await' database calls here
return genericBox;
}
catch (Exception e)
{
return null;
}
}
Then in your GetBoxes method change your foreach to a Task.WhenAll() instead:
var result = await Task.WhenAll(boxes.Select(x => GetBox(x)));
The result variable will be a Task
If you do not want to mess with Task.WhenAll(), you can simply await the GetBox method inside your loop:
status.Add(await GetBox(box));
I have a old data access library that I need to use in my ASP.NET MVC application but I'm having trouble bringing it into the MVC world where many server-side operations are expected to be asynchronous. My data access library looks a like this:
public class MyOldDAL
{
public TaskResult CreateUser(string userName)
{
try
{
// Do user creation
return new TaskResult { Success = true, Message = "success" };
}
catch (Exception ex)
{
return new TaskResult { Success = false, Message = ex.Message };
}
}
}
And is called by my MVC application like this:
public class MyUserStore : IUserStore<ApplicationUser>
{
private readonly MyOldDAL _dal = new MyOldDAL();
public async Task CreateAsync(ApplicationUser user)
{
await Task.Run(() => _dal.CreateUser(user.UserName));
}
}
This is fine until the method in Task.Run() fails for some reason. I'd like to be able to return TaskStatus.Faulted if TaskResult.Success is false but I can't find an example online on how to do it properly - all my Google searches turn up is how to use Task<T> which the IUserStore interface doesn't permit.
For the record, much as I'd like to I can't change MyOldDAL - it's targeting .NET 3.5 so no async or await there!
The normal way to report errors from tasks is via exceptions, so you'll just need to do that transformation yourself:
public class MyUserStore : IUserStore<ApplicationUser>
{
private readonly MyOldDAL _dal = new MyOldDAL();
public Task CreateAsync(ApplicationUser user)
{
var result = _dal.CreateUser(user.UserName);
if (result.Success)
return Task.CompletedTask;
return Task.FromException(new InvalidOperationException(result.Message));
}
}
Note that Task.Run should not be used on ASP.NET.
Note: As Stephen Cleary noticed in his answer, Task.Run should not be used on ASP.NET.
Original answer (before comments):
Your CreateAsync method should normally be like this:
public async Task<TaskResult> CreateAsync(ApplicationUser user)
{
return await Task.Run(() => _dal.CreateUser(user.UserName));
}
But if you can't return Task<TaskResult> from CreateAsync method... well, than you can't obtain TaskResult from CreateAsync by definition. In that case you can store result locally:
private TaskResult taskResult;
public async Task CreateAsync(ApplicationUser user)
{
var result = await Task.Run(() => _dal.CreateUser(user.UserName));
this.taskResult = result;
// process taskResult wherether you need
}
Or raise event with TaskResult payload, allowing client of MyUserStore to subscribe to this event:
public event EventHandler<TaskResult> TaskCompleted;
public async Task CreateAsync(ApplicationUser user)
{
var result = await Task.Run(() => _dal.CreateUser(user.UserName));
this.OnTaskCompleted(result);
}
private void OnTaskCompleted(TaskResult result)
{
this.TaskCompleted?.Invoke(this, result);
}
I'm working with the Philips Hue, and I need to get some information from the hue bridge before I can populate my application. The requests are made via HTTP/JSON. I have no issue when I run all my code async, but when I try to break out my code so that I have a separate method to update the UI upon loading I'm getting a System.NullReferenceException on myLights. I'm assuming that's because my startUpProcedure() isn't finished yet, hence myLights has not been set. I can't seem to figure out how to wait for the startUpProcedure() to finish before I run setupUI().
To further back that up, if I just run startUpProcedure() without setupUI() I get no issues. And then if I run setupUI() from say a button click it runs just fine.
Clearly I'm missing something here. I just can't seem to find the answer.
So to succinctly put the question: How do I wait for these Async calls to finish so that I can use variables that are depended on their return values?
public sealed partial class MainPage : Page
{
public string appKey;
public string myIP;
public IEnumerable<Light> myLights;
public ILocalHueClient myClient;
public MainPage()
{
this.InitializeComponent();
startUpPocedure();
setupUI();
}
public async Task startUpPocedure()
{
await startUp();
await getLights();
}
public async Task startUp()
{
if (await findBridgeIP())
{
Debug.WriteLine("Bridge Found...");
//Do Actions
}
else
{
//Error!!
Debug.WriteLine("No hue found");
}
Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
Windows.Storage.StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
try {
appKey = localSettings.Values["appKey"].ToString();
Debug.WriteLine("appKey loaded: " + appKey);
//Load up
myClient = new LocalHueClient(myIP);
myClient.Initialize(appKey);
}
catch {
Debug.WriteLine("Need to register app");
}
}
async Task getLights()
{
myLights = await myClient.GetLightsAsync();
Debug.WriteLine("Light Count " + myLights.Count());
IEnumerable<string> myLightNames;
List<string> myTempList = new List<string>();
foreach (Light l in myLights)
{
myTempList.Add(l.Name);
Debug.WriteLine(l.Name);
}
myLightNames = myTempList;
comboBox_LightSelect.ItemsSource = myLightNames;
}
private void setupUI()
{
//Populate the Combo Box
IEnumerable<string> myLightNames;
List<string> myTempList = new List<string>();
foreach (Light l in myLights)
{
myTempList.Add(l.Name);
Debug.WriteLine(l.Name);
}
myLightNames = myTempList;
comboBox_LightSelect.ItemsSource = myLightNames;
}
Your startUpPocedure method in MainPage constructor returns Task almost immediately, and because you're not awaiting it, code execution goes to the next line right after that and setupUI gets called. So when your code starts to enumerate myLights collection, it's still null because startUpPocedure and therefore getLights are still running.
Problem is, you can't await asynchronous methods in a constructor, so in your case solution will be to move both startUpPocedure and setupUI to single async method, await them inside this method and call it from constructor similar to this:
public MainPage()
{
this.InitializeComponent();
Startup();
}
private async void Startup()
{
await startUpPocedure();
setupUI();
}
You should leave only InitializeComponent in the constructor, and move all other logic to Loaded event handler,
https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.frameworkelement.loaded
Then you can mark that handler as async, and use await in it to await on async methods.
I expect there's probably a simple fix for this but I just can't see it.
I'm trying to insert data into an Azure Mobile Services DB from a C# Console program. However when the program is run from within VS (via F5), the data is not being inserted nor is an exception being thrown (that I can see) during the regular course of running the program. When I set a breakpoint to the await dataModel.InsertAsync(data) line and run that in the Immediate Window it throws a ThreadAbortException. Any help is appreciated.
Namespace TestApp {
class Program
{
public static MobileServiceClient MobileService = new MobileServiceClient(
"https://x.azure-mobile.net/",
"API key");
public static IMobileServiceTable<performance> dataModel = Program.MobileService.GetTable<performance>();
static void Main(string[] args)
{
try
{
var test = new performance("http://www.example.com");
var x = InsertItem(test);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.StackTrace);
}
}
static public async Task InsertItem(performance data)
{
await dataModel.InsertAsync(data).ConfigureAwait(false);
}
}
class performance
{
[JsonProperty(PropertyName = "id")]
string Id { get; set; }
[JsonProperty(PropertyName = "uri")]
string Uri { get; set; }
public performance(string uri)
{
Uri = uri;
}
}
}
Your problem comes from the fact that var x = InsertItem(test); is a non blocking call. When you get to await dataModel.InsertAsync(data).ConfigureAwait(false); the function InsertItem immediately returns with a Task.
Normally the correct approach would be do await InsertItem(test); however because your code is being called from Main you can't make the function async. So for this console application (it would not be the correct choice if running in WinForms or WPF app) You need to put a x.Wait() before the end of your try-catch block.
static void Main(string[] args)
{
try
{
var test = new performance("http://www.example.com");
var x = InsertItem(test);
//This makes the program wait for the returned Task to complete before continuing.
x.Wait();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.StackTrace);
}
}
However if you where to do this in a WPF or WinForms app you would just make the calling function (Assuming the function was a event) async.
private async void Button1_OnClick(object sender, EventArgs e)
{
try
{
var test = new performance("http://www.example.com");
//The code now waits here for the function to finish.
await InsertItem(test);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.StackTrace);
}
}
Do not do async void function calls unless you are in a event delegate function
I created a small test to (somewhat) simulate what you're doing. When the awaited task in InsertItem takes very little or no time at all, the task returned by the var x = InsertItem(test) line returns a task in the RanToCompletion state and the debugger acts as expected.
However, when I make the awaited task do something substantial, such as Thread.Sleep(5000), then I get the behavior you're describing and the task returned by the var x = InsertItem(test) line returns a task in the WaitingForActivation state.
When I put Task.WaitAll(x) following the var x = InsertItem(test) line, then I get the behavior that I think we both expect and x.Status is RanToCompletion.