Catel async等待ShowDialog中的命令 - 死锁

时间:2014-02-05 06:51:36

标签: c# mvvm task-parallel-library async-await catel

使用库Сatel最新版本(3.8.1 beta)。

如何在对话框窗口中使用TAP方法?

实施例。 在主ViewModel中调用方法

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

在LoginWindowViewModel中我有Command(也尝试AsynchronousCommand),这叫做方法

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;
}

只有在关闭对话框窗口时才从等待方法获得结果。  锁出现在行

var uow = new UnitOfWork()

ConfigureAwait(false) - 无助于解决问题

当我删除UnitOfWork时 - 方法有效

当我将方法代码更改为此时 var d = TypeFactory.Default.CreateInstanceWithParameters(); 返回5;

阻止也会在TypeFactory ......

行上重现

根据服务,对话框中不允许使用Catel

2 个答案:

答案 0 :(得分:3)

注意:我编辑了这个答案,因此它包含了这个问题的答案。上一个答案包含一些关于主题入门者调查问题的提示。

您在MainViewModel的构造函数中调用该命令。请注意,我们从不建议您在构造函数中调用任何内容。我们有 Initialize 方法。

原因是您使用TypeFactory构建 MainViewModel (Catel为您完成此操作)。然后在该线程中执行相同(异步)命令时,您希望实例化一个UnitOfWork,它也希望通过TypeFactory实例化一个类型。这是一个不同的主题。 TypeFactory仍处于锁定状态,因为您仍在构造MainViewModel。

同样,Catel在ViewModelBase上提供 Initialize 方法,该方法在创建时称为 outside ,因此可以安全地在那里做任何事情。请改用它。

答案 1 :(得分:2)

我想我知道可能是什么问题。如果我对问题的理解是正确的,下面的代码将重现它:

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);
    }
}

取消注释注释行,window.Loaded内的((Model)window.DataContext).AsyncTask.Result事件处理程序中将出现死锁。

这是因为window.Loaded在调度ShowDialog的Dispatcher消息循环的同一次迭代上同步被触发。 AsyncTask没有机会完成,因为await Task.Delay(1)之后的继续安排在UI线程的DispatcherSynchronizationContext上。

相同的AsyncTask.Result代码会在ShowDialog之后立即生效。这是因为在对话框关闭之前,已经执行了更多的消息循环迭代(在对话框的新Dispatcher框架上)。

修复很简单:

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

这将在对话框的Dispatcher框架上异步完成任务。

我不确定这与OP的情况有多接近,因为在上述情况下放置await Task.Delay(1).ConfigureAwait(false)也可以解决问题。然而,根据OP的代码,这是我可以做出的猜测。