2010-11-30 11 views
1

私はタクシー会社のリストを見つけてBingから名前と住所を引き出し、ユーザーに表示されているリストボックスに入力するWin Phoneアプリを持っています。今、私は何をしたい(人気順位の緩い一種)だから、Windows Phoneで非同期XMLを読む7

void findBestResult(object sender, DownloadStringCompletedEventArgs e) 
    { 
      string s = e.Result; 
      XmlReader reader = XmlReader.Create(new MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(s))); 
      String name = ""; 
      String rName = ""; 
      String phone = ""; 
      List<TaxiCompany> taxiCoList = new List<TaxiCompany>(); 

      while (reader.Read()) 
      { 
       if (reader.NodeType == XmlNodeType.Element) 
       { 
        if (reader.Name.Equals("pho:Title")) 
        { 
         name = reader.ReadInnerXml(); 
         rName = name.Replace("&amp;","&"); 
        } 

        if (reader.Name.Equals("pho:PhoneNumber")) 
        { 
         phone = reader.ReadInnerXml(); 
        } 

        if (phone != "") 
        { 
         string baseURL = "http://api.search.live.net/xml.aspx?Appid=<MyAppID>&query=%22" + name + "%22&sources=web"; 
         WebClient c = new WebClient(); 
         c.DownloadStringAsync(new Uri(baseURL)); 
         c.DownloadStringCompleted += new DownloadStringCompletedEventHandler(findTotalResults); 
         taxiCoList.Add (new TaxiCompany(rName, phone, gResults)); 
        } 
        phone = ""; 
        gResults =""; 
       } 
      TaxiCompanyDisplayList.ItemsSource = taxiCoList; 
     } 
    } 

そのビットのを、ビンビンにこれらの用語のそれぞれの検索各検索語リターンヒット数を見つけ、それに応じてランク付けすること、ですコードはタクシー会社を見つけ、各teaxicompanyオブジェクトを作成するための検索結果(gResults)の数を見つける非同期タスクを起動します。

//Parses search XML result to find number of results 
    void findTotalResults(object sender, DownloadStringCompletedEventArgs e) 
    { 
     lock (this) 
     { 
      string s = e.Result; 
      XmlReader reader = XmlReader.Create(new MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(s))); 
      while (reader.Read()) 
      { 
       if (reader.NodeType == XmlNodeType.Element) 
       { 
        if (reader.Name.Equals("web:Total")) 
        { 
         gResults = reader.ReadInnerXml(); 
        } 

       } 
      } 
     } 
    } 

は、上記のスニップは、ビングの検索結果の数を見つけたが、それは方法1で右の会社と第二の方法で得られたgResultsを相関させる方法はありありません非同期(async)起動するので、問題がありますいずれかのいずれかの方法:

1)がタクシーオブジェクト

2を作成するために、第二の方法に名前と電話変数を渡す)変数gResultsバックパスし、だけにして、対応するtaxicompanyオブジェクトを作成しますか?

答えて

8

ここにはよくあることがたくさんあります。

いくつかの小さなヘルパーコードを取得

まず第一に、私はSimple Asynchronous Operation Runner Part 1Part 2と呼ばれるブログ記事のカップルにあなたを指すようにしたいです。私はあなたが実際にそれらを読むことを示唆していませんが(あなたも歓迎ですが、私は彼らが読みにくいと言われています)。あなたが実際に必要とするのは、あなたのアプリケーションに入れるための2つのコードブロックです。

「AsyncOperationService」ボックスのコードをコピーして、プロジェクト内の新しいクラスファイル「AsyncOperationService.cs」に配置します。

第2部から「DownloadString」関数が必要です。どこにでも置くことができますが、「WebClientUtils」という静的パブリッククラスを作成してそこに配置することをお勧めします。私たちは、あなたが後にある結果を得るために、非同期の仕事をオフ発射し、その後に発生するイベントがあり、単一のメソッドを持つクラス(TaxiCompanyFinder)を作成しようとしているソリューション

概要仕事が終わったとき。

だから始めることができます。あなたは例ができるだけ完全であるように、私はここに私自身を考案よ、TaxiCompanyクラスを持っている: -

public class TaxiCompany 
{ 
    public string Name { get; set; } 
    public string Phone { get; set; } 
    public int Total { get; set; } 
} 

我々はまた、またErrorプロパティを完成List<TaxiCompany>を運び、完成イベントのEventArgsが必要発生した可能性のある例外が返されます。

public class FindCompaniesCompletedEventArgs : EventArgs 
{ 
    private List<TaxiCompany> _results; 
    public List<TaxiCompany> Results 
    { 
     get 
     { 
      if (Error != null) 
       throw Error; 

      return _results; 
     } 
    } 

    public Exception Error { get; private set; } 

    public FindCompaniesCompletedEventArgs(List<TaxiCompany> results) 
    { 
     _results = results; 
    } 

    public FindCompaniesCompletedEventArgs(Exception error) 
    { 
     Error = error; 
    } 
} 

今、私たちはTaxiCompanyFinderクラスのためのいくつかの裸の骨でスタートを行うことができます - - :これは、これまでかなり単純です

public class TaxiCompanyFinder 
{ 
    protected void OnFindCompaniesCompleted(FindCompaniesCompletedEventArgs e) 
    { 
     Deployment.Current.Dispatcher.BeginInvoke(() => FindCompaniesCompleted(this, e)); 
    } 

    public event EventHandler<FindCompaniesCompletedEventArgs> FindCompaniesCompleted = delegate {}; 

    public void FindCompaniesAsync() 
    { 
     // The real work here 
    } 
} 

それはこのようになります。一連の非同期アクションが発生するため、ディスパッチャーにBeginInvokeの使用が記録されます。イベントが実際に発生したときにUIスレッドで実行され、このクラスを簡単に使用できるようにする必要があります。あなたのオリジナルのコードが持っている問題の

一つを解析するXMLを分離

は、それは、同様にそのすべてのビットスパゲッティを他の機能を実行しようとしてXMLを列挙ミックスしていることです。私が確認した最初の機能は、名前と電話番号を取得するためにXMLを解析することです。クラスにこの機能を追加します - この関数は、他の何かをしようとせずに、XMLからTaxiCompanyインスタンスのセットをもたらすこと

IEnumerable<TaxiCompany> CreateCompaniesFromXml(string xml) 
    { 
     XmlReader reader = XmlReader.Create(new StringReader(xml)); 
     TaxiCompany result = new TaxiCompany(); 

     while (reader.Read()) 
     { 
      if (reader.NodeType == XmlNodeType.Element) 
      { 
       if (reader.Name.Equals("pho:Title")) 
       { 
        result.Name = reader.ReadElementContentAsString(); 
       } 

       if (reader.Name.Equals("pho:PhoneNumber")) 
       { 
        result.Phone = reader.ReadElementContentAsString(); 
       } 

       if (result.Phone != null) 
       { 
        yield return result; 
        result = new TaxiCompany(); 
       } 
      } 
     } 
    } 

注意を。また、より巧妙な読書を行うReadElementContentAsStringの使用。さらに、xml文字列の消費ははるかにスムーズです。同様の理由がクラスにこの機能を追加するために

: -

private int GetTotalFromXml(string xml) 
    { 
     XmlReader reader = XmlReader.Create(new StringReader(xml)); 
     while (reader.Read()) 
     { 
      if (reader.NodeType == XmlNodeType.Element) 
      { 
       if (reader.Name.Equals("web:Total")) 
       { 
        return reader.ReadElementContentAsInt(); 
       } 
      } 
     } 
     return 0; 
    } 

コア機能

はこれがすべて本当の非同期作業を行う機能である、クラスに次の関数を追加します。 : -

private IEnumerable<AsyncOperation> FindCompanies(Uri initialUri) 
    { 
     var results = new List<TaxiCompany>(); 

     string baseURL = "http://api.search.live.net/xml.aspx?Appid=<MyAppID>&query=%22{0}%22&sources=web"; 

     string xml = null; 
     yield return WebClientUtils.DownloadString(initialUri, (r) => xml = r); 

     foreach(var result in CreateCompaniesFromXml(xml)) 
     { 
      Uri uri = new Uri(String.Format(baseURL, result.Name), UriKind.Absolute); 
      yield return WebClientUtils.DownloadString(uri, r => result.Total = GetTotalFromXml(r)); 
      results.Add(result); 
     } 

     OnFindCompaniesCompleted(new FindCompaniesCompletedEventArgs(results)); 
    } 

それは実際にほとんどのポイントであるsynchonousコードのように、かなりまっすぐ進むと見えます。必要なセットを含む最初のxmlをフェッチし、TaxiCompanyオブジェクトのセットを作成します。それはそれぞれのTotal値を追加してセットを通しforeaches。最後に、完了したイベントは企業の完全なセットと解雇されます。私は最初のウリが何であるかを知らないか、あなたには、いくつかの方法でparamatiseする必要がありますが、あなたはこの機能を微調整する必要があるかどうか

public void FindCompaniesAsync() 
    { 
     Uri initialUri = new Uri("ConstructUriHere", UriKind.Absolute); 

     FindCompanies(initialUri).Run((e) => 
     { 
      if (e != null) 
       OnFindCompaniesCompleted(new FindCompaniesCompletedEventArgs(e)); 
     }); 
    } 

- :私たちはちょうどFindCompaniesAsync方法で入力する必要があり

。実際の魔法はRun拡張メソッドで発生します。これはすべての非同期操作をジョグし、例外が返された場合は完了したイベントがErrorプロパティセットで発生します。

クラスを使用して

は今、あなたはこのように、このクラスを消費することができますに:あなたが会社を取得したい場合

var finder = new TaxiCompanyFinder(); 
finder.FindCompaniesCompleted += (s, args) => 
{ 
    if (args.Error == null) 
    { 
     TaxiCompanyDisplayList.ItemsSource = args.Results; 
    } 
    else 
    { 
     // Do something sensible with args.Error 
    } 
} 
finder.FindCompaniesAsync(); 

をまた

 TaxiCompanyDisplayList.ItemsSource = args.Results.OrderByDescending(tc => tc.Total); 

を使用して検討するかもしれませんリストの最上位にある合計。

1

任意のオブジェクトを非同期呼び出しの一部として「UserState」として渡すことができ、非同期コールバックで使用できるようになります。だから、コード、変更のあなたの最初のブロックに:

c.DownloadStringAsync(new Uri(baseURL)); 
c.DownloadStringCompleted += new DownloadStringCompletedEventHandler(findTotalResults); 

へ:

TaxiCompany t = new TaxiCompany(rName, phone); 
c.DownloadStringAsync(new Uri(baseURL), t); 
c.DownloadStringCompleted += new DownloadStringCompletedEventHandler(findTotalResults); 

、あなたはこれを行うことができるようにする必要があります

void findTotalResults(object sender, DownloadStringCompletedEventArgs e) 
{ 
    lock (this) 
    { 
     TaxiCompany t = e.UserState; 
     string s = e.Result; 

     ... 
    } 
} 

私はこのコードごとをテストしていませんしかし、eventargのUserStateを使ってオブジェクトを非同期コールバックに渡すという一般的な考え方は、関係なく動作するはずです。

さらに詳しい情報はAsyncCompletedEventArgs.UserState definition on MSDNをご覧ください。

+0

ありがとう、UserStateは私が探していたものです – varunsrin

関連する問題