2

ライブラリСatel最新バージョン(3.8.1ベータ版)を使用。

ダイアログ ウィンドウから 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 = 新しい UnitOfWork()

ConfigureAwait(false) - 問題の解決には役立たない

UnitOfWork を削除すると、メソッドが機能します

メソッドコードをこれに変更すると var d = TypeFactory.Default.CreateInstanceWithParameters(); 5 を返します。

ブロッキングは TypeFactory の行でも再現されます...

サービスによっては、ダイアログ ボックスで Catel を使用できない場合があります

4

2 に答える 2

3

注:この回答を編集して、この質問に対する回答が含まれるようにしました。前の回答には、トピックの開始者が問題を調査するためのヒントが含まれていました。

MainViewModel のコンストラクターでコマンドを呼び出します。コンストラクターで何かを呼び出すことは決してお勧めしません。そのためのInitializeメソッドがあります。

その理由は、TypeFactory を使用して MainViewModel を構築するためです( Catel がそれを行います)。次に、そのスレッドで実行されている同じ(非同期)コマンドで、TypeFactory を介してタイプをインスタンス化する UnitOfWork をインスタンス化します。これは別のスレッドにあります。MainViewModel をまだ構築しているため、TypeFactory はまだロックされています。

繰り返しになりますが、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 フレームでタスクが非同期に完了します。

await Task.Delay(1).ConfigureAwait(false)上記の場合も配置すると問題が解決するため、これがOPのシナリオにどれだけ近いかはわかりません。それにもかかわらず、これはOPのコードに基づいて推測できる限りです。

于 2014-02-05T11:18:26.233 に答える