0

I'm new to async and I'm trying to create async WPF MVVM application with MVVM Light framework and have a problem with "A second operation started on this context before a previous asynchronous operation completed..."

In my program, when I clik button new View is shown and if there is a message with data ViewModel receives it and fills the appropriate controls with data, if not it is ready to add new ones. Therefore in the CTOR there is method GetInitialValues() and messenger (MVVMLight).

When openinig the View its ViewModel CTOR looks like in the code below So in CTOR:

  1. I provide a UOF using AutoFac. Injected UOF uses one dbContext for all Repositories per MyViewModel (acc. to the video: https://youtu.be/rtXpYpZdOzM?t=1328, I introduced Async methods in generic repository and its interface)
  2. Getting initial values for combobox lists etc.
  3. Registering Messenger using MVVMLight

The thing is that during GetInitialValues() async method is executing the CTOR goes to the end and Messenger received the Notification and switch the execution path to the handler OnSomeEntitySent()

Taking into consideration that we are on the one Thread generated by GetInitialValues() and that it was interrupted by OnSomeEntitySent() I got the Exeption: "A second operation started on this context before a previous asynchronous operation completed"

I tried to use in GetInitialValues() Task.WhenAll (I thought it will block the eventhandler to the end of the method) but the same situation appears - "A second operation started on this context before a previous asynchronous operation completed..."

public MyViewModel(IUnitOfWork unitOfWork)
{
    this.unitOfWork = unitOfWork;

    Task.Run(async () => await GetInitialValues());

    Messenger.Default.Register<SomeEntity>(this, OnSomeEntitySent);
}


private async Task GetInitialValues()
{
    CboList1= await unitOfWork.SomeEntity1.GetAllAsync().ConfigureAwait(false);
    CboList2= await unitOfWork.SomeEntity1.GetAllAsync().ConfigureAwait(false);
    CboList3= await unitOfWork.SomeEntity1.GetAllAsync().ConfigureAwait(false);
}

private async void OnSomeEntitySent()
{
    SomeEntity4 = await unitOfWork.SomeEntity4.GetAsync().ConfigureAwait(false);
    SomeEntity5 = await unitOfWork.SomeEntity5.GetAsync().ConfigureAwait(false);
}

I do not know how to handle following things:

  1. How to await OnSomeEntitySent() eventhandler to wait until GetInitialValues() method goes to the end.
  2. How to make parallel tasks when using UOF based on one dbContext (i.e. using Task.WhenAll)

Thank you in advance for help.

  • 2
    Your constructor is synchronous so it won't actually wait until the GetInitialValues() method has completed. You could try a static async method that returns a class instance created by a private constructor (see https://stackoverflow.com/a/12520574/9266796 or https://blog.stephencleary.com/2013/01/async-oop-2-constructors.html). – Johan Maes Jul 14 '19 at 11:46
  • 3
    Never put async code into a constructor. In fact you shouldn't have DB code in a constructor either. You could have an `Initialize` method instead, but one could even argue that a view model shouldn't know anything about the DB. Also you shouldn't have just one `DbContext` but instead one per transaction. – juharr Jul 14 '19 at 12:40
  • Thank you for the comments. In fact I moved DB code from constructor to Loaded event in ViewModel and introduced IUnitOfWorkFactory where UnitOfWork with its dbContext is created each time I need it and I use it in the using block such us below. using (var unitOfWork= unitOfWorkFactory.Create()) {db code using unitOfWork} Please let me know if this is the right approach. – Tomasz Strączek Jul 14 '19 at 15:41

0 Answers0