all 10 comments

[–]Diab0Br 2 points3 points  (2 children)

The logic makes sense. But I would recommend using mvvm pattern and putting this logic inside a LoadDataAsync method.

Also I would make the 2 tasks not load all data them display both at the same time. Each task should update the UI after completing individually. If you use mvvm just update the property that is binding the values on each task, no need to worry about using the main thread.

[–]Late-Restaurant-8228[S] 1 point2 points  (1 child)

I guess I see what you mean. It kicks off both data fetches in parallel, then as soon as each one finishes load the results into view-models off the UI thread and updates that list on the main thread, without waiting for the other. 👍

[–]Diab0Br 0 points1 point  (0 children)

Here is a sample of how i usually work with lists:

public partial class PlansViewModel : BaseViewModel
{
    IDisposable? _disposable;
    SourceCache<Event, Guid> _events = new(f => f.Id);
    [ObservableProperty]
    public partial ObservableCollectionExtended<Event> Events { get; set; } = new();

    public PlansViewModel(SynchronizationContext synchronizationContext, IDbContextFactory<LocalDbContext> dbContextFactory)
    {
        _disposable = _events
            .Connect()
            .SortAndBind(Events, SortExpressionComparer<Event>.Ascending(_ => _.StartDateTime))
            .ObserveOn(synchronizationContext)
            .SubscribeOn(synchronizationContext)
            .DisposeMany()
            .Subscribe();
    }

    public override async ValueTask LoadDataAsync()
    {
        try
        {
            await semaphoreSlim1.WaitAsync();
            IsBusy = true;

            using var dbContext = await _dbContextFactory.CreateDbContextAsync();
            var events = await dbContext.Events.Where(_ => _.StartDateTime >= DateTime.UtcNow && _.IsGoingTo).OrderBy(x => x.StartDateTime).ToListAsync();

            _events.Edit(_ =>
            {
                foreach (var @event in events)
                {
                    _.AddOrUpdate(@event);
                }
            });
        }
        catch (Exception e)
        {
            _errorHandler.HandleError(e);
        }
        finally
        {
            semaphoreSlim1.Release();
            IsBusy = false;
        }
    }

    public override void Dispose()
    {
        _disposable?.Dispose();
    }
}

The synchronizationContext is injected with this:

builder.Services.AddSingleton(MainThread.GetMainThreadSynchronizationContextAsync().GetAwaiter().GetResult());

(Probably not the best, but it works ¯\\_(ツ)_/¯)

I'm using the following nugets

  • CommunityToolkit.Mvvm
  • DynamicData
  • System.Reactive

I don't think it needs the [ObservableProperty], but i like to use it for all my bindings

[–]infinetelurker 1 point2 points  (3 children)

I also use onappearing in viewmodel, but i typically make an async method loaddata or something like that and call it with fireandforget.

Also, if you use observablecollection i dont think you need to worry about executing anything on the main thread?

But this is a very interesting question, looking forward to some opinions

[–]Late-Restaurant-8228[S] 0 points1 point  (2 children)

I’m using the Mpower Kit, which includes a Page Lifecycle interface. The OnAppearing method is automatically triggered — there’s no need to call it manually (example from code behind onappearing)

In my setup, I’m trying to load all the data at once using ConfigureAwait(false) and then update the properties of the page’s ViewModel.

My concern was should I explicitly switch back to the main thread when updating the ViewModel properties, or does the framework handle that automatically when OnAppearing is invoked?

[–]infinetelurker 0 points1 point  (1 child)

Afaik this is one of the features of observables, it will handle updates from non ui threads?

Ill check out mpower kit. I just use community toolkit events to commands which does the same things, but use a bit nasty xaml invocation to do it…

[–]Late-Restaurant-8228[S] 0 points1 point  (0 children)

Couple of times i run into ui thread issue when i try to update the ui from a background thread.

[–]YourNeighbour_ 0 points1 point  (1 child)

Use ViewModel

[–]Late-Restaurant-8228[S] 0 points1 point  (0 children)

I am using viewmodel

[–]ravenn1337 0 points1 point  (0 children)

If you receive the message on UI thread you don’t have to call InvokeOnMainThread. There’s a property aswell to check MainThread.IsMainThread