8

I have a view model with a constructor that requires a service. I'm using GalaSoft's MvvmLight which uses a service locator to wire up the view to the view model.

SimpleIOC handles providing the service to the viewmodels constructor fine, but I somehow need to populate my viewmodel with data from a data source . My Viewmodel looks a like this:-

    public class MainPageViewModel : ViewModelBase
{
    private readonly GroupService _groupService;
    private readonly GroupFactory _groupFactory;
    private readonly ObservableCollection<GroupVM> _groupVms = new ObservableCollection<GroupVM>();


    public MainPageViewModel(Domain.Services.GroupService groupService, VMFactories.GroupFactory groupFactory)
    {
        _groupService = groupService;
        _groupFactory = groupFactory;
    }

    public async Task Init()
    {
        var groups = await _groupService.LoadGroups();
        foreach (var group in groups)
        {
            GroupVms.Add(_groupFactory.Create(group));
        }
    }

    public ObservableCollection<GroupVM> GroupVms { get { return _groupVms; } }
}

Somehow the init method needs to be called an awaited, but I don't know how best to do this? I can think of three options:-

  1. I just call Init on the constructor, but not await it (I know thats really bad practice)
  2. I call Init on ViewModelLocator object, but since I can't return a task I again can't await the init
  3. On the view's load I cast the DataContext to some sort of IAsyncViewmodel and await the init method.

I have used option 3 in a previous windows 8 store project but it just feels wrong. Any advice would be really appreciated!

Thanks

Ross

S2S2
  • 8,322
  • 5
  • 37
  • 65
Ross Dargan
  • 5,876
  • 4
  • 40
  • 53

2 Answers2

8

I am curious why you consider not awaiting an async call a bad practice. In my opinion, it is not bad as long as you know what that means, that the call will be executed in the background and might return any time.

Typically what I do is that I call the async method in the constructor for design time data creation purpose and I don't await it. I just take care of raising the PropertyChanged and CollectionChanged events where needed for the bindings to be updated, and that does the trick.

Cheers Laurent

LBugnion
  • 6,672
  • 2
  • 24
  • 28
  • Good point, it was more "one of those" things I had taken in but not really thought about. Here has a bit more info about: http://stackoverflow.com/questions/7261173/c-sharp-start-async-method-within-object-constructor-bad-practice Basically it's because when the object is constructed it's still "doing stuff" perhaps unexpectedly, plus it's very hard to handle exceptions that might occur. I have tried both ways, and I think I prefer the init in the constructor, but still not sure! – Ross Dargan Dec 17 '12 at 14:36
  • tip: to avoid the 'squigglies' in Visual Studio just assign the value of the returned Task to a dummy variable – Simon_Weaver Jan 25 '17 at 10:25
1

I'm not as familiar with MvvmLight as I once was, but I've tried to deal with async constructors in a variety of ways.

Option (1) has error handling complexities. Option (3) forces InitAsync to be accessible.

I prefer an async factory approach (e.g., option (2), where the DataContext would stay null until the VM initialization completes), but it sounds like ViewModelLocator won't work for that. So (for now) you have to choose between an async factory that does not use IoC and one of the other options (1 or 3) that are not ideal.

Personally, I would toss out IoC for VM/View binding (I never use IoC for that anyway; my views depend on my VMs) and use an async factory method. However, the other options are perfectly viable if you want to stay with IoC. For VMs I think option (1) would be slightly better than option (3) if you always had a top-level try/catch in your InitAsync methods.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810