Windows Store App のViewModelにおいてAsyncAwaitの落とし穴

AsyncAwaitは非常に便利で、お手軽に非同期処理を実装でき
バックグランドの処理とUIの処理を分離することなく実装できます。
ですが、UIの処理可能なスレッドは決まっています。

#問題
これは、実装によって発生しなかったりします。
// かなり簡略してます。

INotifyPropertyChangedを実装するViewModelを作って、
PropertyChangeイベントを実装しました。

Xamlに`prop1`や`prop2`をデータバインドします。

XamlのイベントからAsyncを利用して、
ViewModelで外部リソースにアクセスするためのAsyncAwaitなモデル処理を実装しました。
その中で、ViewModelのpropertyが変更されたときにViewに変更を通知するための
`PropertyChanged`を呼びだすまでは良かったのですが
例外が発生してしまいました。


```csharp:ViewModel.cs
    /// <summary>
    /// ViewModel
    /// </summary>
    public class ViewModel: INotifyPropertyChanged
    {

        public string Prop1 { get; private set; }

        public string Prop2 { get; private set; }

        /// <summary>
        /// プロパティの変更通知
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// プロパティの変更通知イベントを発生させる
        /// </summary>
        /// <param name="name"></param>
        protected async virtual void OnPropertyChanged(string name)
        {
             PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

        public async void ProceedAsync()
        {
             await Proceed();
        }

        protected async void Proceed()
        {
            Func<Task<{戻り値}>> method = async () =>
            {
                var result = await {外部リソースアクセス！};
　　　　　　　　　return result;
            };
             await Operation(method);
        }

        protected async Task Operation(Func<Task<{戻り値}>> method)
        {
            var result = await Task.Run(method);
            this.prop1 = "プロパティの更新" + result.ToString();
            this.OnPropertyChanged("prop1");
        }

    }
```

#エラー
なぜか、ComExceptionが発生している。

```
型 'System.Runtime.InteropServices.COMException' の初回例外が System.dll で発生しました
アプリケーションは、別のスレッドにマーシャリングされたインターフェイスを呼び出しました。
```

#原因
UIを処理できるスレッドは決まっている。
それ以外でアクセスするとエラーとなる。

#対応策
AsyncAwaitをやめてしまえば大丈夫です。
いや、それは回避策ですね。

この問題は、非同期処理が起因ですが、実行しているスレッドが問題。
なので、適切なスレッドで処理させればよいんです。

それを解決してくれるのが

[windows.ui.core.coredispatcher]("https://msdn.microsoft.com/library/windows/apps/windows.ui.core.coredispatcher\(v=vs.10\).aspx/)

ところが、データバインドは対象を指定するのみで
実際、Viewのcsで実装するところはありません。

じゃあどうするかというと。



```csharp:ViewModel2.cs
    /// <summary>
    /// ViewModel
    /// </summary>
    public class ViewModel: IDisposable, INotifyPropertyChanged
    {
        public Windows.UI.Core.CoreDispatcher Dispatcher;

        /// <summary>
        /// プロパティの変更通知イベントを発生させる
        /// </summary>
        /// <param name="name"></param>
        protected async virtual void OnPropertyChanged(string name)
        {
            if (Dispatcher != null)
            {
                await Dispatcher.RunAsync(  Windows.UI.Core.CoreDispatcherPriority.Normal,
                    () =>  PropertyChanged(this, new PropertyChangedEventArgs(name)));
            }
            else
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
    }
```

こんな風にしちゃいます。
あとは、View側でViewModelをバインドするついでに

```csharp:View.cs
    public class View
    {
        public void Init()
        {
            this.ViewModel = new ViewModel();
            this.DefaultViewModel = this.ViewModel;
            this.ViewModel.Dispatcher = this.Dispatcher;
        }
    }
```

こんな風にしてDispatherを渡してしまいます。
これで、スレッドがなんであるかを意識する必要がなくなりました。


#まとめ
AsyncAwaitは、非同期のわずらわしさを軽減してくれますが、
それでは解決できないUIスレッドが足を引っ張ります。
そこで、AsyncAwaitがそもそもやろうとしていること、
[タスクベースの非同期](https://msdn.microsoft.com/ja-jp/library/hh873175\(v=vs.110\).aspx)にフォーカスをあてて考えてみます。
そうしたとき、プログラムを書く側は
実行しているスレッドがなんであるかを意識することよりも
スレッドが適切なものになっていることのほうが都合が良いわけです。
もし意識してしまえば、AsyncAwaitの中見やライブラリの中身を知る必要が出てきてしまうでしょう。それは、仕様ではない実装に依存関係を生み出す結果となり
潜在的な不具合となってしまいます。
なので、一見遠回しのようでわかりにくいですが、ViewModelにDispatcherを
渡しました。