Please consider the code as shown below. By calling GetBrands, property Brands will be assigned with proper data.
public class BrandsViewModel : ViewModelBase
{
private IEnumerable<Brand> _brands;
public IEnumerable<Brand> Brands
{
get { return _brands; }
set { SetProperty(ref _brands, value); }
}
public async void GetBrands()
{
// ......
Brands = await _dataHelper.GetFavoriteBrands();
// ......
}
}
But if I test it as shown below, the test failed. How do I wait for the async call inside method GetBrands?
[TestMethod]
public void AllBrandsTest()
{
BrandsViewModel viewModel = new BrandsViewModel();
viewModel.GetBrands();
Assert.IsTrue(viewModel.Brands.Any());
}
The simple answer here is: don't make it an async void. In fact, don't ever make something an async void unless it absolutely has to be to work as an event-handler. The things that async void loses are precisely the things that you want here for your test (and presumably for your real code).
Make it an async Task method instead, and you now have the ability to wait for completion (with timeout) / add a continuation, and to check whether it exited with success or an exception.
This is a single word change, to:
public async Task GetBrands()
{
// ......
Brands = await _dataHelper.GetFavoriteBrands();
// ......
}
and then in the test:
[TestMethod]
public async Task AllBrandsTest()
{
BrandsViewModel viewModel = new BrandsViewModel();
var task = viewModel.GetBrands();
Assert.IsTrue(task.Wait(YOUR_TIMEOUT), "failed to load in time");
Assert.IsTrue(viewModel.Brands.Any(), "no brands");
}
Your model (a DTO) is populating itself (data access). This is too much for one class to do. Usually when you ask yourself "How on earth can I test this", it's time for refactoring. Create a separate data access class:
BrandsViewModel viewModel = new BrandsViewModel();
var brandAccess = new BrandsDataAccess();
viewModel.Brands = await brandAccess.GetAllBrands();
Assert.IsTrue(viewModel.Brands.Any());
Now you can test BrandsDataAccess.GetAllBrands().
Related
I have a simple Winforms application. I would like to background TCP connections/print requests and check the output of all tasks at a set point in my code.
I would expect ReportOnTasks to block until WaitAll is complete. Please could someone explain why this is not the case? I'm also worried I haven't structured this correctly.
Edit, to clarify my intentions:
I would like to send the print jobs as soon as I receive the data. Then continue with some other DB operations. Once all the print operations are complete, I would like to update the UI to state the result.
I've attempted to simplify the code as much as I can. Maybe too much. HomeController just inits some stuff. There are buttons on the form and file watchers that trigger the main functionality.
public class HomeController
{
public HomeController(){
MessageBox.Show("1");
oPrintController.PrintAsync("192.168.2.213", Encoding.ASCII.GetBytes("string to print"));
MessageBox.Show("2");
// Block here untill tasks are complete
ReportOnTasks();
MessageBox.Show("Report on tasks complete");
}
public async void ReportOnTasks()
{
await Task.WhenAll(oPrintController.Tasks);
foreach(Task<PrintController.PrintResult> PR in oPrintController.Tasks)
{
// do something with the result of task
}
}
}
and the PrintController
public class PrintController
{
public List<Task<PrintResult>> Tasks = new List<Task<PrintResult>>();
public async void PrintAsync(string sIP, List<byte[]> lsToPrint, int iPort = 9100)
{
var s = await Task.Run(() => PrintAsync1(sIP, lsToPrint));
}
public async System.Threading.Tasks.Task<PrintResult> PrintAsync1(string sIP, List<byte[]> lsToPrint, int iPort = 9100)
{
using (TcpClient tc = new TcpClient())
{
await tc.ConnectAsync(sIP, iPort);
using (var ns = tc.GetStream())
{
foreach (byte[] btLabel in lsToPrint)
{
await ns.WriteAsync(btLabel, 0, btLabel.Length);
}
}
}
Thread.Sleep(10000);
return new PrintResult();
}
}
public class PrintResult
{
bool bSuccess = false;
}
You are not awaiting the call to ReportOnTasks()
Moreover, you can't await within a ctor, because they can't be async.
Depending on how your HomeController is used, you could use a static async method which returns an instance of HomeController, created by a private ctor instead:
Something like this:
public class HomeController
{
//notice private - you can't new up a HomeController - you have to use `CreateInstance`
private HomeController(){
MessageBox.Show("1");
//not clear from your code where oPrintController comes from??
oPrintController.PrintAsync("192.168.2.213", Encoding.ASCII.GetBytes("string to print"));
MessageBox.Show("2");
MessageBox.Show("Report on tasks complete");
}
public static async Task<HomeController> CreateInstance() {
var homeController = new HomeController();
await homeController.ReportOnTasks();
return homeController;
}
//don't use async void! Change to Task
public async Task ReportOnTasks()
{
//not clear from your code where oPrintController comes from??
await Task.WhenAll(oPrintController.Tasks);
foreach(Task<PrintController.PrintResult> PR in oPrintController.Tasks)
{
// do something with the result of task
}
}
}
Usage:
var homeControllerInstance = await HomeController.CreateInstance();
It's generally not recommended to perform heavy operations in class constructors, but I suppose you won't change that part, so in order to wait for ReportOnTasks to finish, you need to make it synchronous.
Take into account, that constructor itself doesn't support async/await, it's not possible to mark it async.
Having said that, you won't have real performance enhancement marking void ReportOnTasks as async. In addition, it is not recommended to mark void methods as async due to issues with exceptions handling, which is usually not possible.
So, you can either postpone ReportOnTasks like Alex showed you, or you can synchronously wait until all tasks are finished (which is possible inside ctor).
public void ReportOnTasks()
{
Task.WhenAll(oPrintController.Tasks).GetAwaiter().GetResult(); //synchronously wait
foreach(Task<PrintController.PrintResult> PR in oPrintController.Tasks)
{
// do something with the result of task
}
}
However, I wouldn't suggest this approach, because instance creation will take a while and most importantly block UI thread - and that's usually signal something is really fishy
I'm not sure about this state. I need to get data from database asynchrony.
I have class DB
public class Db{
public async Task<ObservableCollection<Person>> GetAllPerson()
{
using (var context = new Db())
{
// get data and return ObservableCollection<Person>
}
}
}
In the ViewModel I call LoadData function.
public class VM{
public ObservableCollection<Person> Person { get; set; }
private readonly DB sqlRepository;
public VM()
{
sqlRepository=new DB();
LoadData();
}
private async void LoadData()
{
Person= await sqlRepository.GetAllPerson();
}
}
I got warning: Warning CS1998 This async method lacks 'await' operators and will run synchronously.
How can I run my function asynchronously?
Should I use ?
Person=await Task.Run(()=>this.sqlRepository.GetAllPerson());
How can I run my function asynchronously?
You're approaching your problem from the wrong direction. You're trying to go "outside in" - your ViewModel wants to load the database data asynchronously. And that's a fine way of describing the problem, but it's the wrong way to solve it.
To solve it more easily, start at the other end. Whatever methods are actually calling into the database (e.g., Entity Framework calls) should be made asynchronous first, and then let async grow out from there. Eventually you'll end up with something like:
public async Task<ObservableCollection<Person>> GetAllPersonAsync()
{
using (var context = new Db())
{
// This code wasn't shown in the question.
// But from the compiler warning, it was probably using something like
// var people = People.ToList();
// return new ObservableCollection<Person>(people);
// And the async version should be:
var people = await People.ToListAsync();
return new ObservableCollection<Person>(people);
}
}
Which you could consume as:
private async void LoadData()
{
Person = await sqlRepository.GetAllPersonAsync();
}
But I recommend consuming it via NotifyTask as described in my MVVM async data binding article. That approach would give you the ability to data-bind busy spinners and whatnot.
Should I use [Task.Run]?
No. That's "fake asynchrony" - where your code acts like it's asynchronous but it's really just synchronously running on a background thread.
I have a WPF application that initializes the state of the UI via methods in the constructor. However, it's never returning from the Wait(); in the constructor.
Here's what I am currently doing via a fairly-contrived sample:
public class SomeViewModel
{
public ICommand AddDataCommand { get { return RelayCommand(AddDataExecute); } }
public ObservableCollection<int> UIData { /* Property with INotifyPropertyChanged */}
public SomeViewModel()
{
//Load synch. here
LoadData().Wait();
}
public async Task LoadData()
{
UIData = await Task.Run(() => SomeService.SelectAllUIData());
}
public async void AddDataExecute()
{
//Add new data item to database on separate thread to keep UI responsive
await Task.Run(() => SomeService.AddNewData(0));
//Reload data from database and update UI, has to happen after AddNewData completes
await LoadData();
}
}
I believe it's hanging because I am never actually returning a Task. However, I don't know of a different way to assign UIData asynchronously that works both in the constructor and the Commands that call it.
You're seeing the classic deadlock situation that I describe on my blog.
To solve it, use async all the way, as I describe in my MSDN article on async best practices.
This can be difficult in some scenarios. I have a blog post describing a few approaches for async constructors. However, since you're talking about a ViewModel and data for the UI, you'll probably find my blog post on async properties more helpful - in particular, the section on data binding.
Don't construct the object through a constructor, if you require construction to be asynchronous. Use a static factory method:
public class SomeViewModel
{
private SomeViewModel()
{ }
public static async Task<SomeViewModel> Create()
{
SomeViewModel model = new SomeViewModel();
await model.LoadData();
return model;
}
public async Task LoadData()
{
UIData = await Task.Run(() => SomeService.SelectAllUIData());
}
//...
}
As for why your code isn't working, you're getting the standard await deadlock in which you're blocking on an asynchronous operation, that blocking is preventing continuations from being called on the UI thread, and with two different operations each waiting on the other to continue, you get a deadlock. This is why it's important to "async all the way up" rather than synchronously blocking on an asynchronous operation.
I'm trying to implement MVVM and in the ViewModel I'm doing some async fetching of data. For that purpose I've tried to loading data in the constructor:
MyModel Model { get; set; }
public MyViewModel()
{
Model = new MyModel();
Model.Foo = await LoadDataFromIsolatedStorage();
But this isnt valid as you cant append async to the contructor. So I tried a public static load function:
MyModel Model { get; set; }
public MyViewModel()
{
Model = new MyModel();
}
async public static void Load()
{
Model.Foo = await LoadDataFromIsolatedStorage();
But here WP8 complains that it Cannot await void. Because you would set up the ViewModel and bind it to the View in the code behind of the view. Boring.
Lastly a fix is making the Load function return a ViewModel, so that you in the code behind of the view can do something like:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
MyViewModel viewModel = await MyViewModel.Load();
with the following code to load:
MyModel Model { get; set; }
public MyViewModel()
{
Model = new MyModel();
}
async public static Task<MyViewModel> Load()
{
MyViewModel viewModel = new MyViewModel();
viewModel.Model.Foo = await LoadDataFromIsolatedStorage();
return viewModel;
NOW, the problem at hand is that I have no control if the data loaded should force the application to navigate to another page. Lets say MyViewModel loads a variable from isolated storage, that should then make the app navigate to another page?
I've set up eventlistener to MyViewModel to make the app navigate, but I cant do this when I initiate it.
Does not work with events:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
MyViewModel viewModel = await MyViewModel.Load();
viewModel.NavigationAction += viewmodel_NavigationAction;
}
void viewmodel_NavigationAction(sender, args)
{
NavigationService.Navigate(...)
}
Would work but I "cannot await void":
async protected override void OnNavigatedTo(NavigationEventArgs e)
{
MyViewModel viewModel = new MyViewModel();
viewModel.NavigationAction += viewmodel_NavigationAction;
await viewModel.Load(); // given the Load only is a async void and not Task<T>
}
void viewmodel_NavigationAction(sender, args)
{
NavigationService.Navigate(...)
}
I assume this code block "doesn't work" because the event is set too late, after the data is loaded:
MyViewModel viewModel = await MyViewModel.Load();
viewModel.NavigationAction += viewmodel_NavigationAction;
In that case, fixing your last code block is simple enough: have Load return Task and it will work. Task is the async equivalent of void; you should never use async void unless you're writing an event handler. See my best practices article for more information.
However, even when you get this working, you'll end up with an empty view until the VM loads; this may be a poor user experience if your load could take a long time (or errors out, say, if there's no network connectivity).
You may want to consider using NotifyTaskCompletion from my AsyncEx library, which I describe on my blog. That pattern allows you to (immediately) create a VM in a "loading" state, which will transition (via INotifyPropertyChanged) to a "loaded" or a "loading error" state. That pattern provides a better UX, IMO.
What roliu wrote makes pretty much sense since the whole concept of async/await pattern is based on Tasks. When writing an asynchronous method you ALWAYS need to return a task to make sure the following await will work. Usually the compiler will make some special replacements in the background, so you don't have to care about it.
You will never get a bool as a result of an asynchronous operation - but a generic bool typed Task! Make sure you understood the pattern like described at MSDN.
Then you can create something like this. It is not a Win8 App. For simplicity I chose it to be a console application.
class Program
{
static void Main(string[] args)
{
DoWork();
}
static async void DoWork()
{
await YourVoidMethod();
}
static Task YourVoidMethod()
{
Task task = Task.Run(() =>
{
// Your payload code
}
);
return task;
}
}
Just as a hint: When working with GUIs you also need to work with the dispatcher. When changing data of the UI thread you otherwise could generate cross thread exceptions.
i'm currently coding C# app for Windows Store.
I have Cache class, News UserControl class and MainPage class
I'm calling in MainPage constructor Cache class and then call InitializeData for News class where i using data from Cache, but there is problem, in Cache constructor i receiving datas but he didnt do whole function, he switching from Cache constructor to InitializeData at third await function.
MainPage:
public MainPage()
{
this.InitializeComponent();
Cache.Cache cache = new Cache.Cache();
NewsContent.InitializeData(cache.MyData);
}
Cache:
public Cache()
{
Initialization = Init();
}
public Task Initialization
{
get;
private set;
}
private async Task Init()
{
try
{
cS = await folder.CreateFileAsync("cache.txt", CreationCollisionOption.OpenIfExists);
cS_titles = await folder.CreateFileAsync("titles_cache.txt", CreationCollisionOption.OpenIfExists);
string contentOfFile = await FileIO.ReadTextAsync(cS);
int contentLength = contentOfFile.Length;
if (contentLength == 0) // download data for first using
{
await debug.Write("Is empty!");
//.......
// ....
await FileIO.AppendTextAsync(cS, file_content);
await FileIO.AppendTextAsync(cS_titles, file_content_titles);
}
else // check for same data, if isnt same download new, else nothing
{
await debug.Write(String.Format("Isnt empty. Is long: {0}", contentLength)); // here he break and continue to NewsContent.InitializeData(cache.MyData);
// ....
// ....
}
await MyFunction(); // i need get constructor to this point then he will do NewsContent.InitializeData(cache.MyData);
}
catch (Exception)
{
}
}
Is this possible to do it? For any idea thank you!
Stephen Cleary's article on async and constructors describes how to make this work.
In your case, I think the factory pattern (as suggested in Jon's answer) won't work for MainPage, because it's a GUI component. But the second approach, The Asynchronous Initialization Pattern, will work.
You already implemented that pattern for Cache, now you also need to implement it for MainPage:
public MainPage()
{
Initialization = InitializeAsync();
}
public Task Initialization { get; private set; }
private async Task InitializeAsync()
{
Cache.Cache cache = new Cache.Cache();
await cache.Initialization;
NewsContent.InitializeData(cache.MyData);
}
If MainPage has some events that depend on the initialization being complete, you can make then async and add await this.Initialization at their beginning. Also, you might want to enable buttons or things like that at the end of MainPage's InitializeAsync().
This is what happens when you call an async method and never wait for it to finish, basically.
The whole point of an async method is that you don't block... and your constructor can't be asynchronous itself.
One option would be to write an asynchronous static method to create a cache:
static async Task<Cache> CreateCache()
{
// Change your InitializeData to return the data which the cache needs
var data = await InitializeData();
return new Cache(data);
}
Fundamentally you still need whatever calls CreateCache to understand that it's happening asynchronously though. You don't want to block the UI thread waiting for it all to initialize.
EDIT: I hadn't spotted that this is called from the MainPage constructor. You could potentially apply the same approach again:
public static async Task<MainPage> CreateMainPage()
{
var cache = await Cache.CreateCache();
return new MainPage(cache);
}
This is assuming you really, really can't let the main page be created without the cache being completely initialized. If you could handle that (e.g. showing something like a "Loading..." status until it's finished initializing) then that would be better.