2

Using a library Сatel latest version (3.8.1 beta).

How can I use TAP method from dialog window?

Example. In main ViewModel calling a method

private bool ShowDialogWindow()
{
    var typeFactory = TypeFactory.Default ;
    var vm = typeFactory.CreateInstanceWithParametersAndAutoCompletion<LoginWindowViewModel>();
    return _uiVisualizerService.ShowDialog(vm) ?? false;
}

In LoginWindowViewModel I have Command (also try AsynchronousCommand) which is called method

public async Task<int> Test(string login, string password)
{
     var a = await Task<int>.Factory.StartNew(() =>
     {
         using (var uow = new UnitOfWork<TSDbContext>())
         {
             var userRep = uow.GetRepository<IUserRepository>();
             userRep.GetAll();
             return 5;
         }
     });
     a++;
     return a;
}

I got the results from awaited method only when close the dialog window. Lock appears on line

var uow = new UnitOfWork()

ConfigureAwait(false) - does not help solve the problem

When I delete UnitOfWork - method works

When I Change Method Code to This var d = TypeFactory.Default.CreateInstanceWithParameters(); return 5;

Blocking is also reproduced on the line TypeFactory...

Depending on the services Catel are not allowed in the dialog box

noseratio
  • 59,932
  • 34
  • 208
  • 486
bobah75
  • 3,500
  • 1
  • 16
  • 25
  • Well what does your `UnitOfWork` constructor do? – Jon Skeet Feb 05 '14 at 06:53
  • https://github.com/Catel/Catel/tree/develop/src/Catel.Extensions.EntityFramework5/Catel.Extensions.EntityFramework5.NET40/Data – bobah75 Feb 05 '14 at 07:18
  • And have you debugged in to work out where within that it's hanging? I can't see anything obviously problematic there... – Jon Skeet Feb 05 '14 at 07:21
  • I do not see as locks. I think the point here is not exactly in the unitofwork as a mechanism to access configuration or dependency resolution – bobah75 Feb 05 '14 at 07:24
  • You said "lock appears" - which suggests it's locking (hanging). So what does the stack trace look like if you break into the code when it's locked? It's also unclear to me how the two different code snippets are related to each other. – Jon Skeet Feb 05 '14 at 07:26
  • https://catelproject.atlassian.net/wiki/display/CTL/UIVisualizerService – bobah75 Feb 05 '14 at 07:41
  • The dialog box associated with the method described in Examples – bobah75 Feb 05 '14 at 07:41
  • When I delete UnitOfWork - method works – bobah75 Feb 05 '14 at 07:42
  • When I Change Method Code to This var d = TypeFactory.Default.CreateInstanceWithParameters(); return 5; Blocking is also reproduced – bobah75 Feb 05 '14 at 07:43
  • Depending on the services Catel are not allowed in the dialog box – bobah75 Feb 05 '14 at 07:44
  • None of that actually answers my question, I'm afraid. But I don't claim to have any experience of Catel. Maybe someone who's used it will come along to help you, but it would really make things easier if you could provide more information in the question (rather than a stream of comments.) – Jon Skeet Feb 05 '14 at 07:46
  • Thank you! Correct a little question. The point seems especially catel. – bobah75 Feb 05 '14 at 07:50

2 Answers2

3

Note: I edited this answer so it contains the answer to this question. The previous answer contained some hints for the topic starter to investigate the issue.

You invoke the command in the constructor of the MainViewModel. Note that we never recommend that you invoke anything in the constructor. We have the Initialize method for that.

The reason is that you construct the MainViewModel with the TypeFactory (Catel does that for you). Then in the same (async) command being execution in that thread, you want to instantiate a UnitOfWork which also wants to instantiate a type via the TypeFactory. This is on a different thread. The TypeFactory is still locked because you are still constructoring the MainViewModel.

Again, Catel provides the Initialize method on the ViewModelBase which is called outside of the creation so it is safe to do anything in there. Please use that instead.

Geert van Horrik
  • 5,689
  • 1
  • 18
  • 32
  • DbContext creation worked e.g. var a = new TSDbContext(); a.Dispose(); return 5; //Worked! But UnitOfWork => DeadLock Last Line output window [DEBUG] [Catel.IoC.ServiceLocator] Registering type 'Catel.Data.IConnectionStringManager' to type 'Catel.Data.ConnectionStringManager' – bobah75 Feb 05 '14 at 12:28
  • I tried both in the Initialize method (is called in the View.Loaded event) and as a command, both work fine with the following code: public async Task Test(string login, string password) { var a = await Task.Factory.StartNew(() => { using (var uow = new UnitOfWork()) { var userRep = uow.GetRepository(); userRep.GetAll(); return 5; } }); a++; return a; } – Geert van Horrik Feb 05 '14 at 13:49
  • Please create a **reproducable** application (as small as possible) in the official issue-tracker at http://www.catelproject.com/support/issue-tracker – Geert van Horrik Feb 05 '14 at 13:53
2

I think I know what might be the problem here. If my understanding of the problem is correct, the following code reproduces it:

public partial class MainWindow : Window
{
    class Model
    {
        Model() { }

        public Task<int> AsyncTask { get; private set; }

        public static Model Create()
        {
            var model = new Model();
            Func<Task<int>> doTaskAsync = async () =>
            {
                await Task.Delay(1);
                return 42;
            };
            model.AsyncTask = doTaskAsync();
            return model;
        }
    }

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        var textBlock = new TextBlock();
        var window = new Window { Content = textBlock };

        window.Loaded += (sIgnore, eIgnore) =>
        {
            // int result = ((Model)window.DataContext).AsyncTask.Result;
            // textBlock.Text = result.ToString();
        };

        window.DataContext = Model.Create();
        window.ShowDialog();

        MessageBox.Show("Result: " + 
            ((Model)window.DataContext).AsyncTask.Result);
    }
}

Uncomment the commented lines, and there will be a deadlock inside window.Loaded event handler, at ((Model)window.DataContext).AsyncTask.Result.

That happens because the window.Loaded is fired synchronously on the same iteration of the Dispatcher message loop which called ShowDialog. The AsyncTask did not have a chance to complete, because the continuation after await Task.Delay(1) is scheduled on the DispatcherSynchronizationContext of the UI thread.

The same AsyncTask.Result code works immediately after ShowDialog. That's because quite a few more iterations of the message loop have been executed (on the dialog's new Dispatcher frame), before the dialog has been closed.

The fix is simple:

window.Loaded += async (sIgnore, eIgnore) =>
{
    int result = await ((Model)window.DataContext).AsyncTask;
    textBlock.Text = result.ToString();
};

This will asynchronously complete the task on the dialog's Dispatcher frame.

I'm not sure how close this is to the OP's scenario, because placing await Task.Delay(1).ConfigureAwait(false) would solve the problem in the above case, as well. Nevertheless, this is as far as I can make guesses based upon the OP's code.

noseratio
  • 59,932
  • 34
  • 208
  • 486
  • The problem is he says he is doing this in a *Command*. I have tried to reproduce this using a DbContext and UnitOfWork / Repository, but cannot get it to hang (in both the Initialize (View.Loaded) and as a command). – Geert van Horrik Feb 05 '14 at 13:50
  • @GeertvanHorrik, the command is just another kind of event. If it's fired synchronously, somewhere before `ShowDialog` enters its modal message loop, the effect would be the same. – noseratio Feb 05 '14 at 13:54
  • 1
    As far as I understand, he shows the *Login* window, and inside the *Login* window he executes the Command which will invoke the *Login* call (which is asynchronous). But he should simply come up with a good reproducable case. – Geert van Horrik Feb 05 '14 at 14:22
  • @GeertvanHorrik, I agree. So far we can only make guesses. – noseratio Feb 05 '14 at 14:24
  • 1
    FYI: we found the issue. There were multithreading deadlocks on the TypeFactory. See other answer. – Geert van Horrik Feb 06 '14 at 14:36