I have the following scenario:
In my WPF app using MVVM (I'm fairly new to MVVM) I have a Window that contains a DataGrid. When the Window is loaded I want to fill the DataGrid with entries from a Database using Entity Framework. Additionally a certain column should use a ComboBox in edit mode which is also filled from the DB (many-to-many relation). These items should also be loaded when the Window loads. Oh yes and, of course, this should be done async so that the database queries do not block the UI.
I've read these excellent blog posts by Stephen Cleary: https://t1p.de/c12y8 and https://t1p.de/xkdh .
I have chosen the approach that my ViewModel is constructed synchronously and an asynchronous Initialize method is called from the Window-Loaded event. The Initialize method then triggers the two queries.
The ViewModel:
public class ViewModel : ViewModelBase
{
// this uses a slightly modified class from the first blog post
private NotifyTaskCompletion databaseAction;
public NotifyTaskCompletion DatabaseAction
{
get => databaseAction;
private set
{
databaseAction = value;
NotifyPropertyChanged();
}
}
public ViewModel()
{
// nothinc asynchronous going on here
}
public void Initialize()
{
DatabaseAction = new NotifyTaskCompletion(InitializeAsync());
}
private async Task InitializeAsync()
{
List<Task> tasks = new List<Task>();
tasks.Add(FirstQueryAsync());
tasks.Add(SecondQueryAsync());
await Task.WhenAll(tasks);
}
private async Task FirstQueryAsync()
{
using (var context = new SampleContext())
{
var query = await context.Beds.ToListAsync();
if (query.Count > 0)
{
beds = new ObservableCollection<Bed>();
query.ForEach(bed => beds.Add(bed));
}
else
{
LoadBedsFromFile(ref beds);
foreach (var bed in beds)
{
context.Beds.Add(bed);
}
await context.SaveChangesAsync();
}
}
}
private void LoadBedsFromFile(ref ObservableCollection<Bed> list)
{
if (File.Exists("Beds.xml"))
{
FileStream fs = new FileStream("Beds.xml", FileMode.Open);
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<Bed>));
list = (ObservableCollection<Bed>)serializer.Deserialize(fs);
fs.Close();
}
}
private async Task SecondQueryAsync()
{
using (var context = new SampleContext())
{
var query = await context.Samples.Where(...)
.Include(...)
.ToListAsync();
foreach (Sample item in query)
{
// each entry is put into a ViewModel itself
SampleViewModel vm = new SampleViewModel(item);
// sampleClass.Samples is an ObservableCollection
sampleClass.Samples.Add(vm);
}
}
}
The Window:
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
ViewModel vm = this.TryFindResource("viewModel") as ViewModel;
if (vm != null)
vm.Initialize();
}
}
Now here is the Issue:
The UI is unresponsive and does not update until the initialization is finished. Even if I use Task.Run(() => vm.Initialize());.
The strange thing ist this: If I put a Task.Delay() into the InitializeAsync method, the loading animation ist shown as intended und the UI is responsive. If i put the Delay into SecondQueryAsync for example the UI freezes for a few seconds and afterwards the loading animation rotates for the duration of the delay.
I suspect this might be some issue with creating the DbContext but i cannot pinpoint this.
I eventually solved this problem.
TheodorZoulias' comment and the link he posted to await Task.Run vs await gave me a hint towards the solution.
Replacing the new NotifyTaskCompletion(InitializeAsync()); with new NotifyTaskCompletion(Task.Run(() => InitializeAsync())); unfortunately raised other problems because I could not simply modify the ObservableCollection from that Task's context.
I really like writing code with async-await. The problem is when it hits code that is not async.
As I suspected the syncronous call to create the DbContext was blocking the thread.
So this is how I solved it - maybe it helps someone:
First I used a factory method to create the DbContext asynchronous:
public class SampleContext : DbContext
{
private SampleContext() : base()
{
...
}
public static async Task<SampleContext> CreateAsync()
{
return await Task.Run(() => { return new SampleContext(); }).ConfigureAwait(false);
}
}
Then I rewrote the methods that query the database in a way that they do not modify the ObservableCollection themselves but returned a list instead.
A second method takes the return value as a parameter and modifies the ObservableCollection. That way I can use ConfigureAwait to configure the context.
private async Task<List<Sample>> SecondQueryAsync()
{
var context = await SampleContext.CreateAsync().ConfigureAwait(false);
var query = await context.Samples.Where(...)
.Include(...)
.ToListAsync().ConfigureAwait(false);
context.Dispose();
return query;
}
private async Task InitializeAsync()
{
List<Task> tasks = new List<Task>();
var firstTask = FirstQueryAsync();
var secondTask = SecondQueryAsync();
tasks.Add(firstTask);
tasks.Add(secondTask);
await Task.WhenAll(tasks);
if (tasks.Any(t => t.IsFaulted))
{
Initialized = false;
}
else
{
Initialized = true;
FillFirstList(firstTask.Result);
FillSecondList(secondTask.Result);
}
}
I am working on the migration from SQLite/WebApis to Realms in an App in Xamarin Forms.
This application manages a messaging service (Chat through SignalR).
The problem that has arisen is when a message is received, which I try to store in a database, the application crashes unexpectedly. (Question that did not happen with SQLite).
I am getting the famous error: realm accessed from incorrect, exactly when the 3rd or 4th message is trying to insert, it has been a bit difficult to see the precise moment.
As I could read, both in questions right here on Stackoverflow, and on the Dot-Net Realm GitHub. the instance must be accessed in the same thread that it was created.
As they suggest, one way to achieve this is by using the correct SynchronizationContext through AsyncContext.Run Nito.AsyncEx by Stephen Cleary or Device.BeginInvokeOnMainThread.
I ask the question because unfortunately, I handle the subject of threads and tasks at a basic level, in what way do I have to approach the previous suggestions to my code? or what should I set correctly to access the thread in which it was created?
This is my code:
LobbyViewModel
public LobbyViewModel()
{
ChatService.OnReceivePrivateMessage += ChatService_OnReceivePrivateMessage;
}
private async void ChatService_OnReceivePrivateMessage(object sender, MessageEventArgs args)
{
await SendMessage(…);
}
private async Task SendMessage(…)
{
await UpdateMetaData(…);
}
public async Task UpdateMetaData(…)
{
await ManagerService.InsertMetaDataChatAsync(metaDataChat);
}
ManagerService
public async Task InsertMetaDataChatAsync (MetaDataChatModel MetaDataChatModel)
{
IMetaDataChatServices MetaDataChatServices = App.AppContainer.Resolve<IMetaDataChatServices>();
IResponse DataResult = (await MetaDataChatServices.Insert(MetaDataChatModel));
}
Repository
public async Task<IResponse> CreateAsync(MetaDataChatModel MetaDataChatModel)
{
MetaDataChatModel.Id = Guid.NewGuid().ToString();
MetaDataChatModel.Partition = _realmManager.projectPartition;
var MetaDataChatDto = MetaDataChatModel.ToDto();
var realmInstance = await _realmManager.GetInstanceAsync();
if (realmInstance == null)
return new Response(false, "Realm instance can't be null");
await realmInstance.WriteAsync(realm =>
{
realm.Add(MetaDataChatDto, false);
});
return new Response(true);
}
RealmManager
public RealmManager()
{
RealmApp = App.Create(appId);
//DataBase = Realms.Realm.GetInstance(syncConfig);
}
public Task<Realms.Realm> GetInstanceAsync()
{
return Realms.Realm.GetInstanceAsync(syncConfig);
}
public async Task RealmLogInAsync()
{
if (RealmApp.CurrentUser == null)
{
User user = await RealmApp.
LogInAsync(Credentials.EmailPassword("email", "password"));
if (user == null)
{
return;
}
}
projectPartition = $"project={RealmApp.CurrentUser.Id}";
syncConfig = new SyncConfiguration(projectPartition, RealmApp.CurrentUser);
}
public async Task RealmLogOutAsync()
{
if (RealmApp != null && RealmApp.CurrentUser!=null)
{
await RealmApp.CurrentUser?.LogOutAsync();
}
}
I am very new to c# so this could be very easy. I am busy creating a xamarin forms app and need the data for the next page on the UI. Its a tabbeb page and all 3 needs the info that will be returned by the base class.
The problem I am having is that I need to call google api service for company info. Due to this I created a asynchronous call. So now the code returns due to this. Due to the tabbed page I need the data to bind to the screen and I now need to wait for the data.So basically need it to be synchronous.
I have tried everything I could find on this topic. Maybe the way I am doing this is wrong but hopefully my code will show that.
This is the Tabbed Page:
public BusinessTabbedPage(string PlaceId)
{
Children.Add(new BusinessSpecialsPage(PlaceId));
InitializeComponent();
}
This will be one of the pages on the app that calls the viewmodel
public BusinessSpecialsPage(string PlaceId)
{
BindingContext = new BusinessSpecialsPageViewModel(PlaceId);
InitializeComponent();
}
Due the 3 pages needing the same data I created a base class. This will get the data and pass everything back to the UI.
public BusinessBaseViewModel(string placeId)
{
Task<List<CompanyProfileModel>> task = GBDFromGoogle(placeId);
task.Wait();
}
public async Task<List<CompanyProfileModel>> GBDFromGoogle(string PlaceId)
{
var info = await ApiGoogle.GetGoogleCompanySelectedDetails(PlaceId);
var Companyresult = info.result;
CompanyProfileModel CompList = new CompanyProfileModel
{
ContactDetails = Companyresult.formatted_phone_number,
Name = Companyresult.name,
Website = Companyresult.website,
};
ComPF.Add(CompList);
return ComPF;
}
This is the api call which i think is adding a new task and then the process deadlocks?
public static async Task<GoogleSelectedPlaceDetails> GGCSD(string place_id)
{
GoogleSelectedPlaceDetails results = null;
var client = new HttpClient();
var passcall = "https://maps.googleapis.com/maps/api/place/details/json?placeid=" + place_id + "&key=" + Constants.GoogleApiKey;
var json = await client.GetStringAsync(passcall);
//results = await Task.Run(() => JsonConvert.DeserializeObject<GoogleSelectedPlaceDetails>(json)).ConfigureAwait(false);
results = JsonConvert.DeserializeObject<GoogleSelectedPlaceDetails>(json);
return results;
}
I need the process not to deadlock. It needs to wait for the tasks to be done so that I can return the data to the screen.
Task.Wait will block the current thread so you should never use it in Xamarin applications and even less in the constructor, otherwise your application will freeze until you receive some data which might never happen if the service is down or the user loses connection.
Instead of calling the data in your ViewModel's constructor you could initialize it on the Appearing event of your ContentPage view.
To do that, you could create a custom behaviour or even better, you can use the following library which already does this for you:
Using NuGet: Install-Package Behaviors.Forms -Version 1.4.0
Once you have installed the library, you can use the EventHandlerBehavior to wire events to ViewModel commands, for example:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:behaviors="clr-namespace:Behaviors;assembly=Behaviors"
xmlns:viewModels="clr-namespace:YourApp.ViewModels"
Title="Business Page"
x:Class="YourApp.Views.BusinessPage">
<ContentPage.BindingContext>
<viewModels:BusinessViewModel />
</ContentPage.BindingContext>
<ContentPage.Behaviors>
<behaviors:EventHandlerBehavior EventName="Appearing">
<behaviors:InvokeCommandAction Command="{Binding AppearingCommand}" />
</behaviors:EventHandlerBehavior>
</ContentPage.Behaviors>
[...]
The ViewModel:
public BusinessBaseViewModel(string placeId)
{
AppearingCommand = new Command(Appearing);
PlaceId = placeId;
}
public ICommand AppearingCommand { get; private set; }
public string PlaceId { get; private set; }
private ObservableCollection<CompanyProfileModel> _googleGbd;
public ObservableCollection GoogleGbd
{
get { _googleGbd?? (_googleGbd = new ObservableCollection<CompanyProfileModel>()); };
set
{
if (_googleGdb != value)
{
_googleGdb = value;
NotifyPropertyChanged();
}
}
}
private async void Appearing()
{
var companyResult = await ApiGoogle.GetGoogleCompanySelectedDetails(PlaceId);
CompanyProfileModel companyProfile = new CompanyProfileModel
{
ContactDetails = companyResult.formatted_phone_number,
Name = companyResult.name,
Website = companyResult.website,
};
GoogleGbd.Add(companyProfile);
}
If you only want the data to be loaded the first time your View appears, then you can add a bool flag to know that you have already loaded the data.
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.
Summary: I would like to call an asynchronous method in a constructor. Is this possible?
Details: I have a method called getwritings() that parses JSON data. Everything works fine if I just call getwritings() in an async method and put await to left of it. However , when I create a LongListView in my page and try to populate it I'm finding that getWritings() is surprisingly returning null and the LongListView is empty.
To address this problem, I tried changing the return type of getWritings() to Task<List<Writing>> and then retrieving the result in the constructor via getWritings().Result. However, doing that ends up blocking the UI thread.
public partial class Page2 : PhoneApplicationPage
{
List<Writing> writings;
public Page2()
{
InitializeComponent();
getWritings();
}
private async void getWritings()
{
string jsonData = await JsonDataManager.GetJsonAsync("1");
JObject obj = JObject.Parse(jsonData);
JArray array = (JArray)obj["posts"];
for (int i = 0; i < array.Count; i++)
{
Writing writing = new Writing();
writing.content = JsonDataManager.JsonParse(array, i, "content");
writing.date = JsonDataManager.JsonParse(array, i, "date");
writing.image = JsonDataManager.JsonParse(array, i, "url");
writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
writing.title = JsonDataManager.JsonParse(array, i, "title");
writings.Add(writing);
}
myLongList.ItemsSource = writings;
}
}
The best solution is to acknowledge the asynchronous nature of the download and design for it.
In other words, decide what your application should look like while the data is downloading. Have the page constructor set up that view, and start the download. When the download completes update the page to display the data.
I have a blog post on asynchronous constructors that you may find useful. Also, some MSDN articles; one on asynchronous data-binding (if you're using MVVM) and another on asynchronous best practices (i.e., you should avoid async void).
You can also do just like this:
Task.Run(() => this.FunctionAsync()).Wait();
Note: Be careful about thread blocking!
I'd like to share a pattern that I've been using to solve these kinds of problems. It works rather well I think. Of course, it only works if you have control over what calls the constructor.
public class MyClass
{
public static async Task<MyClass> Create()
{
var myClass = new MyClass();
await myClass.Initialize();
return myClass;
}
private MyClass()
{
}
private async Task Initialize()
{
await Task.Delay(1000); // Do whatever asynchronous work you need to do
}
}
Basically what we do is we make the constructor private and make our own public static async method that is responsible for creating an instance of MyClass. By making the constructor private and keeping the static method within the same class we have made sure that no one could "accidentally" create an instance of this class without calling the proper initialization methods.
All the logic around the creation of the object is still contained within the class (just within a static method).
var myClass1 = new MyClass() // Cannot be done, the constructor is private
var myClass2 = MyClass.Create() // Returns a Task that promises an instance of MyClass once it's finished
var myClass3 = await MyClass.Create() // asynchronously creates and initializes an instance of MyClass
Implemented on the current scenario it would look something like:
public partial class Page2 : PhoneApplicationPage
{
public static async Task<Page2> Create()
{
var page = new Page2();
await page.getWritings();
return page;
}
List<Writing> writings;
private Page2()
{
InitializeComponent();
}
private async Task getWritings()
{
string jsonData = await JsonDataManager.GetJsonAsync("1");
JObject obj = JObject.Parse(jsonData);
JArray array = (JArray)obj["posts"];
for (int i = 0; i < array.Count; i++)
{
Writing writing = new Writing();
writing.content = JsonDataManager.JsonParse(array, i, "content");
writing.date = JsonDataManager.JsonParse(array, i, "date");
writing.image = JsonDataManager.JsonParse(array, i, "url");
writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
writing.title = JsonDataManager.JsonParse(array, i, "title");
writings.Add(writing);
}
myLongList.ItemsSource = writings;
}
}
Instead of doing
var page = new Page2();
you would be using:
var page = await Page2.Create();
A quick way to execute some time-consuming operation in any constructor is by creating an action and run them asynchronously.
new Action( async() => await InitializeThingsAsync())();
Running this piece of code will neither block your UI nor leave you with any loose threads. And if you need to update any UI (considering you are not using MVVM approach), you can use the Dispatcher to do so as many have suggested.
A Note: This option only provides you a way to start an execution of a method from the constructor if you don't have any init or onload or navigated overrides. Most likely this will keep on running even after the construction has been completed. Hence the result of this method call may NOT be available in the constructor itself.
My preferred approach:
// caution: fire and forget
Task.Run(async () => await someAsyncFunc());
Try to replace this:
myLongList.ItemsSource = writings;
with this
Dispatcher.BeginInvoke(() => myLongList.ItemsSource = writings);
To put it simply, referring to Stephen Cleary https://stackoverflow.com/a/23051370/267000
your page on creation should create tasks in constructor and you should declare those tasks as class members or put it in your task pool.
Your data are fetched during these tasks, but these tasks should awaited in the code i.e. on some UI manipulations, i.e. Ok Click etc.
I developped such apps in WP, we had a whole bunch of tasks created on start.
You could try AsyncMVVM.
Page2.xaml:
<PhoneApplicationPage x:Class="Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<ListView ItemsSource="{Binding Writings}" />
</PhoneApplicationPage>
Page2.xaml.cs:
public partial class Page2
{
InitializeComponent();
DataContext = new ViewModel2();
}
ViewModel2.cs:
public class ViewModel2: AsyncBindableBase
{
public IEnumerable<Writing> Writings
{
get { return Property.Get(GetWritingsAsync); }
}
private async Task<IEnumerable<Writing>> GetWritingsAsync()
{
string jsonData = await JsonDataManager.GetJsonAsync("1");
JObject obj = JObject.Parse(jsonData);
JArray array = (JArray)obj["posts"];
for (int i = 0; i < array.Count; i++)
{
Writing writing = new Writing();
writing.content = JsonDataManager.JsonParse(array, i, "content");
writing.date = JsonDataManager.JsonParse(array, i, "date");
writing.image = JsonDataManager.JsonParse(array, i, "url");
writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
writing.title = JsonDataManager.JsonParse(array, i, "title");
yield return writing;
}
}
}
Don't ever call .Wait() or .Result as this is going to lock your app.
Don't spin up a new Task either, just call the ContinueWith
public class myClass
{
public myClass
{
GetMessageAsync.ContinueWith(GetResultAsync);
}
async Task<string> GetMessageAsync()
{
return await Service.GetMessageFromAPI();
}
private async Task GetResultAsync(Task<string> resultTask)
{
if (resultTask.IsFaulted)
{
Log(resultTask.Exception);
}
eles
{
//do what ever you need from the result
}
}
}
https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern
A little late to the party, but I think many are struggling with this...
I've been searching for this as well. And to get your method/action running async without waiting or blocking the thread, you'll need to queue it via the SynchronizationContext, so I came up with this solution:
I've made a helper-class for it.
public static class ASyncHelper
{
public static void RunAsync(Func<Task> func)
{
var context = SynchronizationContext.Current;
// you don't want to run it on a threadpool. So if it is null,
// you're not on a UI thread.
if (context == null)
throw new NotSupportedException(
"The current thread doesn't have a SynchronizationContext");
// post an Action as async and await the function in it.
context.Post(new SendOrPostCallback(async state => await func()), null);
}
public static void RunAsync<T>(Func<T, Task> func, T argument)
{
var context = SynchronizationContext.Current;
// you don't want to run it on a threadpool. So if it is null,
// you're not on a UI thread.
if (context == null)
throw new NotSupportedException(
"The current thread doesn't have a SynchronizationContext");
// post an Action as async and await the function in it.
context.Post(new SendOrPostCallback(async state => await func((T)state)), argument);
}
}
Usage/Example:
public partial class Form1 : Form
{
private async Task Initialize()
{
// replace code here...
await Task.Delay(1000);
}
private async Task Run(string myString)
{
// replace code here...
await Task.Delay(1000);
}
public Form1()
{
InitializeComponent();
// you don't have to await nothing.. (the thread must be running)
ASyncHelper.RunAsync(Initialize);
ASyncHelper.RunAsync(Run, "test");
// In your case
ASyncHelper.RunAsync(getWritings);
}
}
This works for Windows.Forms and WPF
In order to use async within the constructor and ensure the data is available when you instantiate the class, you can use this simple pattern:
class FooClass : IFooAsync
{
FooClass
{
this.FooAsync = InitFooTask();
}
public Task FooAsync { get; }
private async Task InitFooTask()
{
await Task.Delay(5000);
}
}
The interface:
public interface IFooAsync
{
Task FooAsync { get; }
}
The usage:
FooClass foo = new FooClass();
if (foo is IFooAsync)
await foo.FooAsync;
Brian Lagunas has shown a solution that I really like. More info his youtube video
Solution:
Add a TaskExtensions method
public static class TaskExtensions
{
public static async void Await(this Task task, Action completedCallback = null ,Action<Exception> errorCallBack = null )
{
try
{
await task;
completedCallback?.Invoke();
}
catch (Exception e)
{
errorCallBack?.Invoke(e);
}
}
}
Usage:
public class MyClass
{
public MyClass()
{
DoSomething().Await();
// DoSomething().Await(Completed, HandleError);
}
async Task DoSomething()
{
await Task.Delay(3000);
//Some works here
//throw new Exception("Thrown in task");
}
private void Completed()
{
//some thing;
}
private void HandleError(Exception ex)
{
//handle error
}
}
The answer is simple, If you are developing an UWP app, then add the async function to the Page_Loaded method of the page.
if you want it to wait task to be done you can improve madlars codes like below. (I tried on .net core 3.1 it worked )
var taskVar = Task.Run(async () => await someAsyncFunc());
taskVar.Wait();
You could put the async calls in a separate method and call that method in the constructor.
Although, this may lead to a situation where some variable values not being available at the time you expect them.
public NewTravelPageVM(){
GetVenues();
}
async void GetVenues(){
var locator = CrossGeolocator.Current;
var position = await locator.GetPositionAsync();
Venues = await Venue.GetVenues(position.Latitude, position.Longitude);
}