WinRT - call to async CSLA factory method causes delay in rendering

WinRT - call to async CSLA factory method causes delay in rendering

Old forum URL: forums.lhotka.net/forums/t/11640.aspx


TSF posted on Thursday, October 11, 2012

This may not be related to CSLA, but I wanted to ask here anyway just in case.  In my WinRT app, when I navigate to another page from the main page, I have the following code in the LoadState event:

ViewModels.MyViewModel vm = new ViewModels.MyViewModel();
this.DataContext = vm;

In the constructor of MyViewModel is the following:

EditableRoot.NewEditableRoot((o, e) =>
{
   if (e.Error != null)
 throw e.Error;
   else
    Model = e.Object;
});

And in the EditableRoot CSLA object, I have the following method:

public static void NewEditableRoot(EventHandler<DataPortalResult<EditableRoot>> callback)
{
    DataPortal.BeginCreate<EditableRoot>(callback);
}

What I would expect is that, because the NewEditableRoot factory method is being called asynchronously, there should be no lag in the WinRT app's ability to display the page to which I'm navigating.  But that isn't the case.  The app pauses like it is trying to create the object, and after about 1 second it then displays the new page.  If I remove the call to EditableRoot.NewEditableRoot() within the VM constructor, the page loads immediately.  The new object being created doesn't even do a lot and is marked with [RunLocal].  Regardless, though, shouldn't the WinRT page simply load up fast since the object creation isn't taking place in the UI thread?

I'd appreciate any pointers on what to look for.  But in the meantime, to get around this I have used a DispatcherTimer in the page being loaded.  The interval is set to 1/2 second, at which point it calls a public method on the VM to do the creation of the object, following which I rebind the VM to the page's data context.  Very ugly, but it does give the intended effect...the page loads quickly.

RockfordLhotka replied on Friday, October 12, 2012

I have had difficulties with initializing the model in the constructor, though in your code I'd expect it to work.

But you can't mark a constructor as async, so you can't use await there. Because of this, the 4.5 ViewModelBase now includes an InitAsync method so you can do this in your UI code:

this.DataContext = await new MyViewModel().InitAsync();

Then in the viewmodel class you don't implement a constructor, or at least not one that starts any async work. Instead you override another method:

protected override async Task<T> DoInitAsync()
{
  return await MyBusinessClass.GetMyBusinessClassAsync();
}

This has been working well for me, and I think you could do the equivalent with the older event style async methods as well.

TSF replied on Friday, October 12, 2012

Thanks.  So does the concept of BeginRefresh in the VM not play a role anymore?  Since BeginRefresh doesn't return anything, what would I be returning in the DoInitAynsc?

Or should I not use BeginRefresh and instead do as you did going forward...just calling the static factory method in the business object?  (e.g. MyBusinessClass.GetMyBusinessClassAsync() )

TSF replied on Friday, October 12, 2012

I changed my object to use the newer async style, and used the InitAsync call when setting the DataContext, as you show above.  But it still does the same thing...the initial view hangs for about 1.5 seconds and then displays the new view.  I'm probably doing something wrong somewhere else.  It seems as if I have three options:

  1. The old style (plus using the timer mechanism to which I referred). 
    • Pro: the second view displays immediately;  can use a progress indicator until the fetched data is retrieved and bound
    • Con: messy solution (using a dispatcher timer)
  2. The new style
    • Pro: seems like the proper way to code;  when the second view does finally get displayed, the data is already bound and ready
    • Con: the first view noticeably hangs in an unresponsive state;  because of that I can't use a progress indicator...it simply won't display since the UI is blocked
  3. Calling the Async factory method (for the second view) while still in the original view (when the user clicks a button in the app bar) while displaying progress indicator, and then calling Frame.Navigate() to go to the second view while passing the business object as the argument to the navigationParameter.
    • Con: this forces the initial view to have knowledge about the second view (i.e. its business object)

I'm leaning toward #1 because at least that way I can display a progress bar to let the user know something is happening.

RockfordLhotka replied on Friday, October 12, 2012

Try breaking the line of code in two:

var vm = new ViewModel();
this.DataContext = await vm.InitAsync();

Also, it might be that you are putting this code in a blocking method/event. I typically put this code in the launching event handler, which of course must be decorated with the async keyword.

TSF replied on Friday, October 12, 2012

Thanks for your continued help.  I tried that, but no change, unfortunately.  As to where I have it, I'm calling this in the LoadState event of the second view (the one being navigated to).  I added "async" to it...assuming that is acceptable.

protected override async void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)

Copyright (c) Marimer LLC