WPF , howto to know that a task has completed - wpf

I am developping a MVVM WPF app, and I have some task to do.
first load files csv and parse it
In background don´t block the ui Thread and save the values in the database.To save the rows to the database I need to be with Async Await Task.
My problem is I don´t know how to notice the user with a popup notification or something else that values are already saved in database.
in My ViewModel
private void SaveDatasInDatabase()
{
ShowLoadingPanel = true;
_service.SaveValuesInDatabase(rows);
}
private async void startActions()
{
await LoadandParseCsv();
await SaveDatasInDatabase();
}
in my Service.cs
public string SaveDatasInDatabase(List<Object> rows)
{
Task.Run(async () =>
{
await SaveEntity(rows);
return "Done";
});
}
Thanks in advance.
Jolynce

You know that the task has completed once the remainder of the startActions() method is executed:
private async void startActions()
{
await LoadandParseCsv();
await SaveDatasInDatabase();
MessageBox.Show("done");
}
...provided that actually await the SaveEntity method in the SaveDatasInDatabase() method:
public async Task<string> SaveDatasInDatabase(List<Object> rows)
{
await SaveEntity(rows);
return "Done";
}
If you just call Task.Run without awaiting the returned Task, you don't know when it has finished.
The return type of the SaveDatasInDatabase method should be Task or Task<T> for you to be able to await it. The same thing applies to the SaveEntity method.

Related

How to create asynchronous code in Flutter?

In my Code I get some User Input by TextFormFields and save them in a Map in Firestore. In the next step I want to take calculations with this data and show them the user in the same widget.
I think because of the delay by saving in database I need to wait till the data is saved. But how can I do this? Here is my Code where I want to wait:
void validate() {
if (_formKey.currentState.validate()) {
UserManager.userdata["userStats"][0]["userActivity"] = userActivity;
savetoremote_dynamic(context);
AppBuilder.of(context).rebuild();
}
}
This is the rebuild class, so that the UI gets updated:
class AppBuilderState extends State<AppBuilder> {
#override
Widget build(BuildContext context) {
return widget.builder(context);
}
void rebuild() {
setState(() {});
}
}
I want that as soon as the data is saved the Widget gets rebuild an the updated data is shown to the user, how to use async and await in this code?
I recommend you to use Provider/Riverpod or any suitable State Management that fits you. Alternatively, You can use FutureBuilder to show indicator while loading data from FireStore or any other API.
It's not clear to me what is the async code in your snippet.
However, in flutter a minimal pattern to update the gui based on the result of an async operation is this:
in a flutter callback (for example ElevatedButton.onTap, or initState if the code is to be executed at startup; in your case it could be validate) call your method with the async code -for example getUserDataFromFirestore()- without await the result. You can not usually mark flutter callback such as validate() with async.
the method with the async operation, getUserDataFromFirestore(), can be marked with async, so inside it you can use await future instead of future.then() (but it is only syntactic sugar).
When you got the asynchronous data, you value a variable, for example userData, and rebuild calling setState().
In the build method, you check if userData is not null and then you can show the user data (for example with a collection if in the Column.children list).
If the async operation has to be executed at startup (when the page load, or similar), FutureBuilder is very handy: instead of a nullable field such as User? user, you can declare a Future<User> field:
In your State subclass:
late Future<User> user;
#override
initState() {
super.initState();
user = getUserDataFromFirestore();
}
Furure<User> getUserDataFromFirestore() async {
//your async code; returns the user data
}
#override
build(context) {
return FutureBuilder(
future: user,
builder: (context, asyncSnapshot) {
if (! asyncSnapshot.hasData) {
//user data is not available.
//returns a gui that does not display the user data
} else {
//user data is available..
}
}
);
}
A simple example about a minimal async operation in flutter, using this pattern, is in this cookbook page:
https://flutter.dev/docs/cookbook/networking/fetch-data

How to use async functions to connect to database in Flutter?

I am trying to connect to a static database as it is explained in this answer. I therefore created an asynchronous function that looks like this:
Future<void> loadDataBase() async {
// Construct a file path to copy database to
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "asset_worldcities.db");
// Only copy if the database doesn't exist
if (FileSystemEntity.typeSync(path) == FileSystemEntityType.notFound) {
// Load database from asset and copy
ByteData data = await rootBundle.load(join('assets', 'worldcities.db'));
List<int> bytes = data.buffer.asUint8List(
data.offsetInBytes, data.lengthInBytes);
// Save copied asset to documents
await new File(path).writeAsBytes(bytes);
}
}
Now I thought I could access my database inside my main widget by using this function and then call
Directory appDocDir = await getApplicationDocumentsDirectory();
String databasePath = join(appDocDir.path, 'asset_database.db');
this.db = await openDatabase(databasePath);
initialized = true;
Future<List<Page>> search(String word, int parentId) async {
if (!initialized) await this._initialize();
String query = '''
SELECT * FROM users
LIMIT 25''';
return await this.db.rawQuery(query);
}
but this way I am not allowed to use this.db and also not await as I am not inside an async function. Where do I need to put this database request so that it works?
Depending whether you need to do this every time and the database could grow, or whether it's a one-time operation (which it seems like it might be?) and the database is small enough that it's not going to take long to query it, there are different approaches I'd take.
If it's a one-time per install sort of thing and the database will always be small, making the user wait while it copies the file across probably isn't a huge deal. In that case I'd do something like this:
main() async {
WidgetsFlutterBinding.ensureInitialized();
if (needToLoadDatabase()) {
await loadDatabase();
}
let users = await queryUsers();
runApp(MainWidget(users: users));
}
However, if you're reading from the database and it's something that could take any significant amount of time, I'd recommend initiating the load and then passing the future into your main widget, where it could use a FutureBuilder to build an intermediate UI.
That'd look something like this:
main() async {
WidgetsFlutterBinding.ensureInitialized();
let loadUsers = () async {
if (needToLoadDatabase()) {
await loadDatabase();
}
return await queryUsers();
}();
runApp(MainWidget(loadUsers: loadUsers));
}
class MainApp extends StatelessWidget {
final Future<Users> loadUsers;
MainApp({#required this.loadUsers, Key key}): super(key: key);
Widget build(BuildContext context) {
return FutureBuilder(
builder: (ctx, snapshot) {
if (snapshot.hasData) {
// build your UI with data
} else {
// build your UI without data
}
}
);
}
}
Also note that there's no reason you have to do the loading in the main function - you could make your widget stateful and kick that off in the initState, or any number of places like directly where you use the list. You could also look at the FutureProvider from the Provider package.

Discord.NET C# How do I make the bot wait for an input before proceeding with the command

I was wondering how I could do something like this in my commands class:
public string yuh;
[Command("test")]
public async Task test(params string[] args)
{
if (args.Length == 0)
{
await ReplyAsync("Tell me something.");
***WAIT FOR A USER INPUT***
yuh = Context.Message.Content;
}
await ReplyAsync(yuh);
}
What do I put in the WAIT FOR A USER INPUT?
You can use Discord.Addons.Interactive. It's a "Addon" for Discord.Net installed with NuGet.
var response = await NextMessageAsync();
GitHub File Example

Quartz.net: type of GetDefaultScheduler()

type of StdSchedulerFactory.GetDefaultScheduler() is task not IScheduler,so I got following error.
how can I fix this?
image
You have to await for the result. I suppose you are not awaiting.
StdSchedulerFactory factory = new StdSchedulerFactory(props);
IScheduler scheduler = await factory.GetScheduler();
await scheduler.Start();
Here is complete example from Quartz docs.
private static async Task RunProgram()
{
try
{
// Grab the Scheduler instance from the Factory
NameValueCollection props = new NameValueCollection
{
{ "quartz.serializer.type", "binary" }
};
StdSchedulerFactory factory = new StdSchedulerFactory(props);
IScheduler scheduler = await factory.GetScheduler();
// and start it off
await scheduler.Start();
// some sleep to show what's happening
await Task.Delay(TimeSpan.FromSeconds(60));
// and last shut down the scheduler when you are ready to close your program
await scheduler.Shutdown();
}
catch (SchedulerException se)
{
await Console.Error.WriteLineAsync(se.ToString());
}
}

Async-await seems to use the UI thread

In a view-model I use a factory:
private async Task<BaseData> InitializeAsync()
{
await InstancesAsync();
await ProjectsAsync();
await AdminAsync();
return this;
}
public static async Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return await ret.InitializeAsync();
}
The awaited methods are rather staightforward, with e.g.
var instances = await TaskEx.Run(new Func<List<string>>(() => Agent.GetInstances()));
In the wpf view I want to set the DataContext in the constructor:
Loaded += delegate
{
Dispatcher.Invoke(new Action(async () => { DataContext = await BasisGegevens.CreateAsync(); }));
};
Although it works, I feel rather uncomfortable because the UI thread is used everywhere, also after the callbacks when the awaits complete. What am I missing?
Also I don't understand how to use the factory pattern for the DataContext because without the Invoke above I get the error that a different thread owns the object.
EDIT: using the ideas of Mr. Cleary I get:
Loaded += async (object sender, RoutedEventArgs e) =>
{ DataContext = await BaseData.CreateAsync(); };
public static Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return ret.InitializeAsync();
}
private async Task<BaseData> InitializeAsync()
{
// UI thread here
await InstancesAsync().ConfigureAwait(false);
// thread 'a' here
await ProjectsAsync().ConfigureAwait(false);
// thread 'a' sometimes 'b' here
await AdminAsync().ConfigureAwait(false);
// thread 'a' or 'b' here
return this;
}
This works fine, except I cannot understand how ConfigureAwait(false) works.
Inside the method InstancesAsync() I have the awaited task:
var instances = await TaskEx.Run(new Func<List<string>>(() => Agent.GetInstances()));
After awaiting the repsonse, I return in the UI thread - I never expected that to happen!
Note that ProjectsAsync() and AdminAsync() behave the same, although they start on a worker (or background) thread!
I thougth that ConfigureAwait(true) has the effect of returning in the calling thread (in my case UI thread). I tested that and it is so.
Why do I see this with ConfigureAwait(false) too: because of a nested await, see comments.
I find it most useful to treat the ViewModel as having UI thread affinity. Think of it as the logical UI, even if it's not the actual UI. So all property and observable collection updates on ViewModel classes should be done on the UI thread.
In your async methods, if you don't need to return to the UI thread, then you can use ConfigureAwait(false) to avoid resuming on the UI thread. For example, if your various initialization methods are independent, you could do something like this:
private async Task<BaseData> InitializeAsync()
{
// Start all methods on the UI thread.
var instancesTask = InstancesAsync();
var projectsTask = ProjectsAsync();
var adminTask = AdminAsync();
// Await for them all to complete, and resume this method on a background thread.
await Task.WhenAll(instancesTask, projectsTask, adminTask).ConfigureAwait(false);
return this;
}
Also, any time you have return await, take another look to see if you can just avoid async/await entirely:
public static Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return ret.InitializeAsync();
}
Finally, you should strongly avoid Dispatcher. Your Loaded event could be simplified to this:
Loaded += async ()
{
DataContext = await BasisGegevens.CreateAsync();
};

Resources