Populate ObservableCollection with data from awaited Task.Run - c#

I am making Xamarin application and I have a problem with display data in my list view. So, basically I have web service from where I fetch data (that endpoint is non-async), so I don't want to block UI thread of my application, so I wrap the call to web service in Task.Run and await that task.
public class HomeDetailPageViewModel : ViewModelNavigatable
{
public ObservableCollection<CarViewModel> cars;
public ObservableCollection<CarViewModel> Cars
{
get { return this.cars; }
}
public HomeDetailPageViewModel(INavigationService navigationService)
:base(navigationService)
{
this.cars = new ObservableCollection<CarViewModel>();
AppearingCommand = new AsyncCommand(this.OnAppearingCommandExecuted);
}
public IAsyncCommand AppearingCommand { get; set; }
public async Task OnAppearingCommandExecuted()
{
using (UserDialogs.Instance.Loading("Loading..."))
{
this.Cars.Clear();
IEnumerable<Car> carsFromEndpoint = await Task.Run(() => CarEndpoint.GetAll(client.Context)).ConfigureAwait(false);
Device.BeginInvokeOnMainThread(() =>
{
foreach (var carFromEndpoint in carsFromEndpoint.ToList())
this.Cars.Add(new CarViewModel
{
Manufacturer = carFromEndpoint.Code,
Model = carFromEndpoint.Name,
Price = carFromEndpoint.Price,
Year = carFromEndpoint.Year
});
});
}
}
}
As I said CarEndpoint.GetAll(client.Context) is synchronous endpoint. If I use:
Task.Run(() => CarEndpoint.GetAll(client.Context)).Result or
CarEndpoint.GetAll(client.Context)
everything works as expected but that is not acceptable because it's block UI thread until Task is finished. I know it is not a good idea to use Task.Run to make fake asynchronous calls, but I don't see another way to keep app responsive, because I cannot change the endpoint of web service.
Thank you for the answer.
Cheers :)

I know it is not a good idea to use Task.Run to make fake asynchronous calls
Using Task.Run to unblock a UI thread - even in a fake-asynchronous way - is fine.
I cannot change the endpoint of web service.
This sentence doesn't make as much sense. All web services are asynchronous by nature. However, it's possible that your client-side library is strictly synchronous, in which case Task.Run is a fine way to unblock the UI while calling it. But you'll be working around a limitation in the client-side library, not the web service itself.
I have a problem with display data in my list view.
Your problem is likely due to the IEnumerable<T>, not Task.Run. Here's some code I expect would work; the key is to move the ToList inside the Task.Run delegate:
public async Task OnAppearingCommandExecuted()
{
using (UserDialogs.Instance.Loading("Loading..."))
{
this.Cars.Clear();
List<Car> carsFromEndpoint = await Task.Run(() => CarEndpoint.GetAll(client.Context).ToList());
foreach (var carFromEndpoint in carsFromEndpoint)
this.Cars.Add(new CarViewModel
{
Manufacturer = carFromEndpoint.Code,
Model = carFromEndpoint.Name,
Price = carFromEndpoint.Price,
Year = carFromEndpoint.Year
});
}
}
Notes:
BeginInvokeOnMainThread is unnecessary if you remove the ConfigureAwait(false); the await resumes on the UI thread for us. In fact, BeginInvokeOnMainThread is a code smell.
You probably don't want an asynchronous command here; you just want to asynchronously load data.

When you're using Async commands in MVVM, the main problem is NotSupportedException while modifying the ObservableCollection. All the rest don't cause any issues if you're careful with concurrency.
Here's a class representing ObservableCollection for use from multiple threads which forwards all actions to SynchronizationsContext of the Thread where it was constructed.
Just use it instead of ObservableCollection (not mine, grabbed from GitHub)
public class AsyncObservableCollection<T> : ObservableCollection<T>
{
private readonly SynchronizationContext _synchronizationContext = SynchronizationContext.Current;
public AsyncObservableCollection() : base() { }
public AsyncObservableCollection(IEnumerable<T> collection) : base(collection) { }
public AsyncObservableCollection(List<T> list) : base(list) { }
private void ExecuteOnSyncContext(Action action)
{
if (SynchronizationContext.Current == _synchronizationContext)
action();
else
_synchronizationContext.Send(_ => action(), null);
}
protected override void InsertItem(int index, T item) => ExecuteOnSyncContext(() => base.InsertItem(index, item));
protected override void RemoveItem(int index) => ExecuteOnSyncContext(() => base.RemoveItem(index));
protected override void SetItem(int index, T item) => ExecuteOnSyncContext(() => base.SetItem(index, item));
protected override void MoveItem(int oldIndex, int newIndex) => ExecuteOnSyncContext(() => base.MoveItem(oldIndex, newIndex));
protected override void ClearItems() => ExecuteOnSyncContext(() => base.ClearItems());
}
And this AsyncRelayCommand class that I've made with help on StackOverflow (Russian Community). It doesn't freeze anything.
public interface IAsyncCommand : ICommand
{
Task ExecuteAsync(object param);
}
public class AsyncRelayCommand : IAsyncCommand
{
private bool _isExecuting;
private readonly SynchronizationContext _context;
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public AsyncRelayCommand(Action<object> execute, Predicate<object> canExecute = null)
=> (_execute, _canExecute, _context) = (execute, canExecute, SynchronizationContext.Current);
private void InvalidateRequerySuggested()
{
if (_context.Equals(SynchronizationContext.Current))
CommandManager.InvalidateRequerySuggested();
else
_context.Send(_ => CommandManager.InvalidateRequerySuggested(), null);
}
public bool CanExecute(object parameter) => !_isExecuting && (_canExecute == null || _canExecute(parameter));
public async Task ExecuteAsync(object parameter)
{
if (CanExecute(parameter))
{
try
{
_isExecuting = true;
InvalidateRequerySuggested();
await Task.Run(() => _execute(parameter));
}
finally
{
_isExecuting = false;
InvalidateRequerySuggested();
}
}
}
public void Execute(object parameter) => _ = ExecuteAsync(parameter);
}
Usage as with regular RelayCommand class from this article.
private IAsyncCommand _myAsyncCommand;
// "lazy" instantiation with single instance
public IAsyncCommand MyAsyncCommand => _myAsyncCommand ?? (_myAsyncCommand = new AsyncRelayCommand(parameter =>
{
}));
<Button Content="Click me!" Command="{Binding MyAsyncCommand}" />
CommandParameter is also supported.
With this you don't need pushing collecton change calls into UI Thread, use it as in usual sync code. And Thread.Sleep() or heavy job in the command will not freeze UI because it will run on a separate thread.
Usage with your code
private IAsyncCommand _appearingCommand;
public AsyncObservableCollection<CarViewModel> cars; // are you sure that it must be public?
public HomeDetailPageViewModel(INavigationService navigationService)
:base(navigationService)
{
this.cars = new AsyncObservableCollection<CarViewModel>();
}
public AsyncObservableCollection<CarViewModel> Cars
{
get => this.cars;
}
public IAsyncCommand AppearingCommand => _appearingCommand ?? (_appearingCommand = new AsyncRelayCommand(parameter =>
{
// edit: fixed regarding to the accepted answer
List<Car> carsFromEndpoint = CarEndpoint.GetAll(client.Context).ToList();
foreach (var carFromEndpoint in carsFromEndpoint)
this.Cars.Add(new CarViewModel
{
Manufacturer = carFromEndpoint.Code,
Model = carFromEndpoint.Name,
Price = carFromEndpoint.Price,
Year = carFromEndpoint.Year
});
}));

Related

async Task blocking UI

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);
}
}

.net core - Passing an unknown number of IProgress<T> to class library

I have a console app which uses a class library to execute some long running tasks. This is a .net core console app and uses the .net core Generic Host. I also use the ShellProgressBar library to display some progress bars.
My Hosted service looks like this
internal class MyHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private readonly IMyService _myService;
private readonly IProgress<MyCustomProgress> _progress;
private readonly IApplicationLifetime _appLifetime;
private readonly ProgressBar _progressBar;
private readonly IProgressBarFactory _progressBarFactory;
public MyHostedService(
ILogger<MyHostedService> logger,
IMyService myService,
IProgressBarFactory progressBarFactory,
IApplicationLifetime appLifetime)
{
_logger = logger;
_myService = myService;
_appLifetime = appLifetime;
_progressBarFactory = progressBarFactory;
_progressBar = _progressBarFactory.GetProgressBar(); // this just returns an instance of ShellProgressBar
_progress = new Progress<MyCustomProgress>(progress =>
{
_progressBar.Tick(progress.Current);
});
}
public void Dispose()
{
_progressBar.Dispose();
}
public Task StartAsync(CancellationToken cancellationToken)
{
_myService.RunJobs(_progress);
_appLifetime.StopApplication();
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
Where MyCustomProgress looks like this
public class MyCustomProgress
{
public int Current {get; set;}
public int Total {get; set;}
}
and MyService looks something like so (Job1, Job2, Job3 implement IJob)
public class MyService : IMyService
{
private void List<IJob> _jobsToRun;
public MyService()
{
_jobsToRun.Add(new Job1());
_jobsToRun.Add(new Job2());
_jobsToRun.Add(new Job3());
}
public void RunJobs(IProgress<MyCustomProgress> progress)
{
_jobsToRun.ForEach(job =>
{
job.Execute();
progress.Report(new MyCustomProgress { Current = _jobsToRun.IndexOf(job) + 1, Total = _jobsToRun.Count() });
});
}
}
And IJob is
public interface IJob
{
void Execute();
}
This setup works well and I'm able to display the progress bar from my HostedService by creating a ShellProgressBar instance and using the one IProgress instance I have to update it.
However, I have another implementation of IMyService that I also need to run that looks something like this
public class MyService2 : IMyService
{
private void List<IJob> _sequentialJobsToRun;
private void List<IJob> _parallelJobsToRun;
public MyService()
{
_sequentialJobsToRun.Add(new Job1());
_sequentialJobsToRun.Add(new Job2());
_sequentialJobsToRun.Add(new Job3());
_parallelJobsToRun.Add(new Job4());
_parallelJobsToRun.Add(new Job5());
_parallelJobsToRun.Add(new Job6());
}
public void RunJobs(IProgress<MyCustomProgress> progress)
{
_sequentialJobsToRun.ForEach(job =>
{
job.Execute();
progress.Report(new MyCustomProgress { Current = _jobsToRun.IndexOf(job) + 1, Total = _jobsToRun.Count() });
});
Parallel.ForEach(_parallelJobsToRun, job =>
{
job.Execute();
// Report progress here
});
}
}
This is the one I'm struggling with. when _parallelJobsToRun is executed, I need to be able to create a new child ShellProgressBar (ShellProgressBar.Spawn) and display them as child progress bars of let's say 'Parallel Jobs'.
This is where I'm looking for some help as to how I can achieve this.
Note: I don't want to take a dependency on ShellProgressBar in my class library containing MyService
Any help much appreciated.
I am a little confused by your description, but let's see if I understand what you are up to. So if you wrap all of this in a class, then taskList1 and taskList2 could be class variables. (By the way taskList1/2 should be named better: say parallelTaskList and whatever . . . anyway.) Then you could write a new method on the class CheckTaskStatus() and just iterate over the two class variables. Does that help or have I completely missed your question?
Can you modify it like this?
public Task<ICollection<IProgress<int>>> StartAsync(CancellationToken cancellationToken)
{
var progressList = _myServiceFromLibrary.RunTasks();
return Task.FromResult(progressList);
}
public ICollection<IProgress<int>> RunTasks()
{
var taskList1 = new List<ITask> { Task1, Task2 };
var plist1 = taskList1.Select(t => t.Progress).ToList();
var taskList2 = new List<ITask> { Task3, Task4, Task5 }:
var plist2 = taskList2.Select(t => t.Progress).ToList();
taskList1.foreach( task => task.Run() );
Parallel.Foreach(taskList2, task => { task.Run() });
return plist1.Concat(plist2).ToList();
}
Task.Progress there is probably a progress getter. realistically IProgress should probably be injected via Tasks constructors. But the point is your public interface doesn't accept list of tasks, thus it should just return collection of progress reports.
How to inject progress reporters into your tasks is a different story that depends on tasks implementations and it may or may not be supported. out of the box.
However what you probably should do is to supply progress callback or progress factory so that progress reporters of your choice are created:
public Task StartAsync(CancellationToken cancellationToken, Action<Task,int> onprogress)
{
_myServiceFromLibrary.RunTasks(onprogress);
return Task.CompletedTask;
}
public class SimpleProgress : IProgress<int>
{
private readonly Task task;
private readonly Action<Task,int> action;
public SimpleProgress(Task task, Action<Task,int> action)
{
this.task = task;
this.action = action;
}
public void Report(int progress)
{
action(task, progress);
}
}
public ICollection<IProgress<int>> RunTasks(Action<Task,int> onprogress)
{
var taskList1 = new List<ITask> { Task1, Task2 };
taskList1.foreach(t => t.Progress = new SimpleProgress(t, onprogress));
var taskList2 = new List<ITask> { Task3, Task4, Task5 }:
taskList2.foreach(t => t.Progress = new SimpleProgress(t, onprogress));
taskList1.foreach( task => task.Run() );
Parallel.Foreach(taskList2, task => { task.Run() });
}
you may see here, that it really is mostly question about how your tasks are going to call IProgress<T>.Report(T value) method.
Honestly I would just use an event in your task prototype.
It's not really clear exactly what you want because the code you posted doesn't match the names you then reference in your question text... It would be helpful to have all the code (the RunTasks function for example, your IProgress prototype, etc).
Nevertheless, an event exists specifically to signal calling code. Let's go back to the basics. Let's say you have library called MyLib, with a method DoThings().
Create a new class that inherits from EventArgs, and that will carry your task's progress reports...
public class ProgressEventArgs : EventArgs
{
private int _taskId;
private int _percent;
private string _message;
public int TaskId => _taskId;
public int Percent => _percent;
public string Message => _message;
public ProgressEventArgs(int taskId, int percent, string message)
{
_taskId = taskId;
_percent = percent;
_message = message;
}
}
Then on your library's class definition, add an event like so:
public event EventHandler<ProgressEventArgs> Progress;
And in your console application, create a handler for progress events:
void ProgressHandler(object sender, ProgressEventArgs e)
{
// Do whatever you want with your progress report here, all your
// info is in the e variable
}
And subscribe to your class library's event:
var lib = new MyLib();
lib.Progress += ProgressHandler;
lib.DoThings();
When you are done, unsubscribe from the event:
lib.Progress -= ProgressHandler;
In your class library, now you can send back progress reports by raising the event in your code. First create a stub method to invoke the event:
protected virtual void OnProgress(ProgressEventArgs e)
{
var handler = Progress;
if (handler != null)
{
handler(this, e);
}
}
And then add this to your task's code where you want it:
OnProgress(new ProgressEventArgs(2452343, 10, "Reindexing google..."));
The only thing to be careful about is to report progress sparingly, because each time your event fires it interrupts your console application, and you can really bog it down hard if you send 10 million events all at once. Be logical about it.
Alternate way; If you own the code IProgress<T> and Progress
IProgress<T>
{
IProgress<T> CreateNew();
Report(T progress);
}
Progress<T> : IProgress<T>
{
Progress(ShellProgressClass)
{
// initialize progressBar or span new
}
....
IProgress<T> CreateNew()
{
return new Progress();
}
}
you can later improvise to have one big progressBar (collection of Sequential or Parallel) and what not
Your MyService could have a dependency similar to:
public interface IJobContainer
{
void Add(IJob job);
void RunJobs(IProgress<MyProgress> progress, Action<IJob>? callback = null); // Using an action for extra work you may want to do
}
This way you don't have to worry about reporting progress in MyService (which doesn't feel like it should be MyService's job anyway. The implementation could look something like this for the parallel job container:
public class MyParallelJobContainer
{
private readonly IList<IJob> parallelJobs = new List<IJob>();
public MyParallelJobContainer()
{
this.progress = progress;
}
public void Add(IJob job) { ... }
void RunJobs(IProgress<MyProgress> progress, Action<IJob>? callback = null)
{
using (var progressBar = new ProgressBar(options...))
{
Parallel.ForEach(parallelJobs, job =>
{
callback?.Invoke(job);
job.Execute();
progressBar.Tick();
})
}
}
}
MyService would then look like this:
public class MyService : IMyService
{
private readonly IJobContainer sequentialJobs;
private readonly IJobContainer parallelJobs;
public MyService(
IJobContainer sequentialJobs,
IJobContainer parallelJobs)
{
this.sequentialJobs = sequentialJobs;
this.parallelJobs = parallelJobs;
this.sequentialJobs.Add(new DoSequentialJob1());
this.sequentialJobs.Add(new DoSequentialJob2());
this.sequentialJobs.Add(new DoSequentialJob3));
this.parallelJobs.Add(new DoParallelJobA());
this.parallelJobs.Add(new DoParallelJobB());
this.parallelJobs.Add(new DoParallelJobC());
}
public void RunJobs(IProgress<MyCustomProgress> progress)
{
sequentialJobs.RunJobs(progress, job =>
{
// do something with the job if necessary
});
parallelJobs.RunJobs(progress, job =>
{
// do something with the job if necessary
});
}
The advantage of this way is that MyService only has one job and doesn't have to worry about what you do once the job is completed.
From my understanding of your issue the question is how do you display progress across both completion of the synchronous jobs and parallelized jobs.
In theory the parallel jobs could start and finish at the same time, so you could treat the parallel jobs as a single job. Instead of using the count of sequential jobs as your total, increase that number by one. This might be satisfactory for a small number of parallel jobs.
If you want to add progress between the parallel jobs, you will need to handle multi-threading in your code because the parallel jobs will be running concurrently.
object pJobLock = new object();
int numProcessed = 0;
foreach(var parallelJob in parallelJobs)
{
parallelJob.DoWork();
lock (pJobLock)
{
numProcessed++;
progress.Report(new MyCustomProgress { Current = numProcessed, Total = parallelJobs.Count() });
}
}

Automated test for a async Command in MVVM

I have an asynchronous Command class, like so:
public AsyncDelegateCommand(Func<Task> execute, Func<bool> canExecute)
{
this.execute = execute;
this.canExecute = canExecute;
}
public virtual bool CanExecute(object parameter)
{
if(executing)
return false;
if(canExecute == null)
return true;
return canExecute();
}
public async void Execute(object parameter) // Notice "async void"
{
executing = true;
CommandManager.InvalidateRequerySuggested();
if(parameter != null && executeWithParameter != null)
await executeWithParameter(parameter);
else if(execute != null)
await execute();
executing = false;
CommandManager.InvalidateRequerySuggested();
}
And it is called like so:
FindProductCommand = new AsyncDelegateCommand(TryToFindProduct, () => CanFindProduct() && connector.HasConnection);
private async Task TryToFindProduct()
{
//code
}
When I unit tests, it works just fine, since I'm returning instantly from tasks.
However, when writing my integration test, I run into trouble. I am not able to await Execute, since it is void, and I am not able to change it to Task. I end up doing this: :/
findProductViewModel.FindProductCommand.Execute(null);
Thread.Sleep(2000);
var informationViewModel = findProductViewModel.ProductViewModel.ProductInformationViewModel;
Assert.AreEqual("AFG00", informationViewModel.ProductGroup);
Is there a better solution for this test? Maybe something that is reliant on how long it actually takes, and doesn't estimate how long to wait.
You can refer to a good blog post by #StephenCleary: https://msdn.microsoft.com/en-us/magazine/dn630647.aspx
async void is generally to be avoided, so he introduces a new interface (and its base implementation) for an async command: IAsyncCommand. This interface contains a method async Task ExecuteAsync(object parameter) that you could await in your tests.
public interface IAsyncCommand : ICommand
{
Task ExecuteAsync(object parameter);
}
public abstract class AsyncCommandBase : IAsyncCommand
{
public abstract bool CanExecute(object parameter);
public abstract Task ExecuteAsync(object parameter);
public async void Execute(object parameter)
{
await ExecuteAsync(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
protected void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
}
The simplest implementation of such an async command would look like this:
public class AsyncCommand : AsyncCommandBase
{
private readonly Func<Task> _command;
public AsyncCommand(Func<Task> command)
{
_command = command;
}
public override bool CanExecute(object parameter)
{
return true;
}
public override Task ExecuteAsync(object parameter)
{
return _command();
}
}
But there are more advanced variants that you can find in the linked blog post. You can use the IAsyncCommands in your code all the way, so you can test them. The MVVM framework you use will also be happy, because that interface is based on the ICommand.
Is there a better solution for this test? Maybe something that is reliant on how long it actually takes, and doesn't estimate how long to wait.
Certainly: Make the command awaitable and await it. async void methods are bad practice and are only meant to be used for event handlers.
There are awaitable commands available in Mvvm.Async and ReactiveUI that you can use or at least take a look at for reference.
If you have a state to check for, you should probably just use SpinWait.SpinUntil:
It will be much more reliable than Thread.Sleepas it allows you to check if a condition is true before continuing.
e.g.
findProductViewModel.FindProductCommand.Execute(null);
SpinWait.SpinUntil(() => findProductViewModel.ProductViewModel.ProductInformationViewModel != null, 5000);
var informationViewModel = findProductViewModel.ProductViewModel.ProductInformationViewModel;
Assert.AreEqual("AFG00", informationViewModel.ProductGroup);

How can I use async in an mvvmcross view model?

I have a long running process in an mvvmcross viewmodel and wish to make it async (http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx).
The async keyword is currently supported in the beta channel for Xamarin.
Below is an example of how I'm currently implementing async. The IsBusy flag ccould be bound to a UI element and display a loading message.
Is this the correct way?
public class MyModel: MvxViewModel
{
private readonly IMyService _myService;
private bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set { _isBusy = value; RaisePropertyChanged(() => IsBusy); ; }
}
public ICommand MyCommand
{
get
{
return new MvxCommand(DoMyCommand);
}
}
public MyModel(IMyService myService)
{
_myService = myService;
}
public async void DoMyCommand()
{
IsBusy = true;
await Task.Factory.StartNew(() =>
{
_myService.LongRunningProcess();
});
IsBusy = false;
}
}
You should avoid async void. When you're dealing with ICommand, you do need to use async void, but its scope should be minimized.
This modified code exposes your action as an async Task, which is unit testable and consumable from other parts of your code:
public class MyModel: MvxViewModel
{
private readonly IMyService _myService;
private bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set { _isBusy = value; RaisePropertyChanged(() => IsBusy); ; }
}
public ICommand MyCommand
{
get
{
return new MvxCommand(async () => await DoMyCommand());
}
}
public MyModel(IMyService myService)
{
_myService = myService;
}
public async Task DoMyCommand()
{
IsBusy = true;
await Task.Run(() =>
{
_myService.LongRunningProcess();
});
IsBusy = false;
}
}
Your use of IsBusy is fine; that's one common approach in asynchronous UIs.
I did change Task.Factory.StartNew to Task.Run; Task.Run is preferred in async code for reasons described by Stephen Toub.
MvvmCross now has MvxAsyncCommand (see GitHub commit).
So instead of doing this
public ICommand MyCommand
{
get
{
return new MvxCommand(async () => await DoMyCommand());
}
}
You can do this
public ICommand MyCommand
{
get
{
return new MvxAsyncCommand(DoMyCommand);
}
}
Looks OK except I would add a try catch finally around that await.
public async void DoMyCommand()
{
IsBusy = true;
try{
await Task.Factory.StartNew(() =>
{
_myService.LongRunningProcess();
});
}catch{
//Log Exception
}finally{
IsBusy = false;
}
}
Further more I have an example on my blog using an MvxCommand with async. Very similar to your example http://deapsquatter.blogspot.com/2013/03/updating-my-mobile-apps-for-async.html
You can also use MethodBinding plugin to avoid boiler plate code (commands), and bind your UI directly to the async method.
Besides, if you use Fody PropertyChanged, your code would look like this:
[ImplementPropertyChanged]
public class MyModel: MvxViewModel
{
private readonly IMyService _myService;
public bool IsBusy { get; set; }
public MyModel(IMyService myService)
{
_myService = myService;
}
public async Task DoSomething()
{
IsBusy = true;
await Task.Factory.StartNew(() =>
{
_myService.LongRunningProcess();
});
IsBusy = false;
}
}
You can make the binding like: "Click DoSomething".
On the other hand, rather than using await Task.Factory.StartNew(), why not making _myService.LongRunningProcess async?
It would look much better:
public async Task DoSomething()
{
IsBusy = true;
await _myService.LongRunningProcess();
IsBusy = false;
}

How to Unit Test DelegateCommand that calls async methods in MVVM

I am new to Unit Testing MVVM and using PRISM on my project. I am implementing Unit Testing on our current project and not having luck finding resources online that would tell me how totest DelegateCommand that calls async method. This is a follow up question to my post - How to Unit Test a ViewModel with async method. on how to unit test an async methods in MVVM and was answered that public methods can be tested using async TestMethod. This scenario will work only if the method that I want to test are public methods.
The problem is I want to test my DelegateCommand as this are the only public details that I want to expose on other classes and everything else are private. I can expose my private methods as public but I will never do this as its a bad design. I am not sure on how to go about this - Is DelegateCommand needs to be tested, or there are some other work around this? I am interested to know how other go about this and somehow lead me to the right path.
Here are my codes again
async void GetTasksAsync()
{
this.SimpleTasks.Clear();
Func<IList<ISimpleTask>> taskAction = () =>
{
var result = this.dataService.GetTasks();
if (token.IsCancellationRequested)
return null;
return result;
};
IsBusyTreeView = true;
Task<IList<ISimpleTask>> getTasksTask = Task<IList<ISimpleTask>>.Factory.StartNew(taskAction, token);
var l = await getTasksTask; // waits for getTasksTask
if (l != null)
{
foreach (ISimpleTask t in l)
{
this.SimpleTasks.Add(t); // adds to ViewModel.SimpleTask
}
}
}
also here is the command in my VM that calls the async method above
this.GetTasksCommand = new DelegateCommand(this.GetTasks);
void GetTasks()
{
GetTasksAsync();
}
and now my Test Method goes like
[TestMethod]
public void Command_Test_GetTasksCommand()
{
MyViewModel.GetTaskCommand.Execute(); // this should populate ViewModel.SimpleTask
Assert.IsTrue(MyBiewModel.SimpleTask != null)
}
Currently what I am getting is that my ViewModel.SimpleTask = null this is because it does not wait for the async method to finish.
I wrote an AsyncCommand class that returns a Task object from the Execute method. You then need to implement ICommand.Execute explicitly, awaiting the Task from your Execute implementation:
public class AsyncCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public Func<Task> ExecutedHandler { get; private set; }
public Func<bool> CanExecuteHandler { get; private set; }
public AsyncCommand(Func<Task> executedHandler, Func<bool> canExecuteHandler = null)
{
if (executedHandler == null)
{
throw new ArgumentNullException("executedHandler");
}
this.ExecutedHandler = executedHandler;
this.CanExecuteHandler = canExecuteHandler;
}
public Task Execute()
{
return this.ExecutedHandler();
}
public bool CanExecute()
{
return this.CanExecuteHandler == null || this.CanExecuteHandler();
}
public void RaiseCanExecuteChanged()
{
if (this.CanExecuteChanged != null)
{
this.CanExecuteChanged(this, new EventArgs());
}
}
bool ICommand.CanExecute(object parameter)
{
return this.CanExecute();
}
async void ICommand.Execute(object parameter)
{
await this.Execute();
}
}
You can then pass async Task-returning methods to the command class:
public class ViewModel
{
public AsyncCommand AsyncCommand { get; private set; }
public bool Executed { get; private set; }
public ViewModel()
{
Executed = false;
AsyncCommand = new AsyncCommand(Execute);
}
private async Task Execute()
{
await(Task.Delay(1000));
Executed = true;
}
}
In your unit tests, you simply await the Execute method:
[TestMethod]
public async Task TestAsyncCommand()
{
var viewModel = new ViewModel();
Assert.IsFalse(viewModel.Executed);
await viewModel.AsyncCommand.Execute();
Assert.IsTrue(viewModel.Executed);
}
The UI, on the other hand, will call the explicitly implemented ICommand.Execute method which takes care of awaiting the task.
(*) In the meantime I noticed that if you follow common naming conventions, the Task-returning method should actually be named ExecuteAsync.
In Prism 6, you can create DelegateCommand and DelegateCommand<T> from async handler.
For example:
startParsingCommand=DelegateCommand
.FromAsyncHandler(StartParsingAsync,CanStartParsing)
.ObservesProperty(()=> IsParserStarted);
Since I cannot add comments, for completeness sake, in PRISM 6 you could try:
ParsingCommand = new DelegateCommand<string>(async (x) => await StartParsing(x));

Categories