2

使用库С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 我有命令(也可以尝试 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 不允许出现在对话框中

4

2 回答 2

3

注意:我编辑了这个答案,所以它包含了这个问题的答案。上一个答案包含一些提示,供主题启动者调查该问题。

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

原因是您使用 TypeFactory 构建MainViewModel(Catel 为您做这件事)。然后在该线程中执行的同一个(异步)命令中,您想要实例化一个 UnitOfWork,它也想要通过 TypeFactory 实例化一个类型。这是在不同的线程上。TypeFactory 仍处于锁定状态,因为您仍在构造 MainViewModel。

同样,Catel 在 ViewModelBase 上提供了Initialize方法,该方法在创建之外被调用,因此在那里做任何事情都是安全的。请改用它。

于 2014-02-05T10:15:23.290 回答
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取消注释注释行,事件处理程序内部将出现死锁, at ((Model)window.DataContext).AsyncTask.Result

发生这种情况是因为在 Dispatcher 消息循环的同一迭代window.Loaded中同步触发. 没有机会完成,因为在UI 线程上安排了之后的继续。ShowDialogAsyncTaskawait Task.Delay(1)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 的代码做出的猜测。

于 2014-02-05T11:18:26.233 回答