7

WPFMVVMアプリケーションがあります。ビューモデルには、ビューにバインドされたいくつかのプロパティがあり、これらのプロパティは、データベースから直接、またはビューモデルとデータベースの間にあるwcfサービスを介して取得されたデータによって入力されます。データ接続のモードの選択は、クライアントアプリケーションのApp.configファイルのアプリ設定によって異なります。サービスメソッドを非同期的に呼び出し、それらの戻り値を処理する独自の方法を実装したいと思います。タスクを使用して次の方法で実装した場合、スレッドの問題が発生する可能性があるかどうかを知りたいです。

サービス呼び出しフロー:ViewModel> ServiceAgent>(MyWCFServiceClientまたはMyBusinessClient)> MyBusinessClass>データベースサービス操作を使用するために、IMyWCFService(サービス参照の追加時に生成される)を実装するMyWCFServiceClientクラスがあります。

また、同じIMyWCFServiceインターフェイスから実装するMyBusinessClassClientクラスがあります。したがって、MyWCFServiceとMyBusinessClientの両方が同じメソッドシグネチャを持っています。サービスクライアントの生成中に非同期メソッドを生成しないことを選択しました。生成する場合、IMyWCFServiceによって生成された不要なものをMyBusinessClientにも実装する必要がある可能性があるためです。

IMyWCFServiceで定義されたEmployeeオブジェクトを返すメソッドGetEmployee(int id)があると仮定します。したがって、クラスMyWCFServiceClientとMyBusinessClientの両方に実装があります。

私のViewModelには、次のものがあります。

private void btnGetEmployee_Click()
        {
            ServiceAgent sa = new ServiceAgent (); 

            //this call/callback process the service call result

            sa.GetEmployee(1673, (IAsyncResult ar) =>
            {
                Task<Employee> t1 = (Task<Employee>)ar;
                Employee = t1.Result;
                //do some other operation using the result
                //do some UI updation also
            });
        }  


        //this property is bound a label in the view
      private Employee _employee;
        public Employee Employee
        {
            get
            {
                return _ employee;
            }
            set
            {
                _ employee = value;
                OnPropertyChanged(() => Employee);                    
            }
        }

ServiceAgentクラスは、次のように実装されます。

public class ServiceAgent
    {
        private IMyWcfService client;

        public ProxyAgent()
        {
            //The call can go to either MyWCFServiceClient or 
            //MyBusinessClient depending on this setting

            //client = new MyBusinessClient(); 
            //OR

            client = new MyWcfServiceClient();
        }

        public void GetEmployee(int id, AsyncCallback callback)
        {
          //My implementation to execute the service calls asynchronously using tasks
          //I don’t want to use the complex async mechanism generated by wcf service reference ;)

            Task<Employee> t = new Task<Employee>(()=>client.GetEmployee(id));    
            t.Start();

            try
            {
                t.Wait();
            }
            catch (AggregateException ex)
            {
                throw ex.Flatten();
            }

            t.ContinueWith(task=>callback(t));
        }
    }

これは私のUIをフリーズさせています。それは避けたい。また、これが私が達成したいことのための適切な方法であるかどうか疑問に思います。タスク/スレッドとコールバックの経験が少ないので、将来問題(スレッド/メモリ管理など)が発生するかどうかを知りたいです。

4

1 に答える 1

3

@Ananth heh、コードを読み間違えていると思ったので、コメントを削除しました。一般的に言えば、Web サービスに接続するときは、呼び出しを常に非同期として扱う必要があります。これは、スレッド (通常は GUI スレッド) をフリーズさせる過度の遅延に対処できるためです。これは、1 つの GUI アクションに対して複数の WCF 呼び出しを行う必要がある場合に複雑になります。これは、WCF インターフェイスが非同期呼び出しのように記述されているにもかかわらず、嘘をついて同期的に実行されるため、さらに悪化します。将来の混乱の明確な原因。

したがって、非同期モデルを扱うのが最善だと思いますが、少なくとも、WCF 呼び出し内で型チェック/キャスト/リターン処理を行うことができます。私は同様のことをしましたが、同期呼び出しを使用する代わりにコールバックを使用しますが、WCF 呼び出しで IAsyncResult を処理、それを予想される型にキャストしてユーザーに返します。

public void GetEmployee(int id, Action<Employee> getEmployeeCompletedHandler)
{
    Task<Employee> t = new Task<Employee>(()=>client.GetEmployee(id));    
    t.Start();
    t.ContinueWith(task=>
    {
        if (getEmployeeCompletedHandler != null)
            getEmployeeCompletedHandler(t1.Result);
    });
}

あなたの典型的な使い方は次のとおりです。

sa.GetEmployee(1673, result => this.Employee = result);

本当に同期モデルを維持したい場合は、作業をバックグラウンド スレッドに移動できます (ただし、GUI スレッドの観点からは「非同期」のままです)。この時点でも、GetEmployeeメソッドを同期にして値を返すようにすることもできます。このようにして、それを使用する API コンシューマーには、非同期操作がないことが明らかです。

public Employee GetEmployee(int id)
{
    Task<Employee> t = new Task<Employee>(()=>client.GetEmployee(id));    
    t.Start();

    try
    {
        t.Wait();
    }
    catch (AggregateException ex)
    {
        throw ex.Flatten();
    }

    return t.Result;
}

次に、呼び出しコードは次のようになります。

//spawn a background thread to prevent the GUI from freezing
BackgroundThread.Spawn(() =>
{
    this.Employee = sa.GetEmployee(1673);
});

は、バックグラウンドスレッドBackgroundThreadの作成/生成をラップするために作成できるカスタム クラスです。実装の詳細はお任せしますが、スレッド化用のマネージ ラッパーを用意するのが最善だと思います。これは、使用法が非常に簡単になり、実装の詳細が抽象化されるためです (スレッド プールを使用するか、新しいスレッドを使用するか、BackgroundWorker を使用するか、どうでもいいことです!)

先ほど投稿した WCF 呼び出しの同期使用法は試していないので (最初のコード サンプルのような完全な非同期モデルに固執しています)、うまくいくと思います。(ただし、この方法を行うことはまだお勧めしません!)

于 2012-10-05T10:53:51.920 に答える