Windows Phone 7中的异步XML读取
所以我有一个Win Phone应用程序,它正在寻找出租车公司的列表,并从Bing成功地提取他们的免费精选名字大全和地址,并填充正在向用户显示的列表框。 现在我要做的是,在Bing上搜索每个术语,找到每个搜索词返回的命中数并相应地对它们进行排名(一种松散的流行度排名)
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 taxiCoList = new List(); while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element) { if (reader.Name.Equals("pho:Title")) { name = reader.ReadInnerXml(); rName = name.Replace("&","&"); } if (reader.Name.Equals("pho:PhoneNumber")) { phone = reader.ReadInnerXml(); } if (phone != "") { string baseURL = "https://api.search.live.net/xml.aspx?Appid=&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(); } } } } }
上面的剪辑找到了bing上搜索结果的数量,但问题是因为它启动异步,没有办法将第二种方法中获得的gResults与方法1中的正确公司相关联。有没有办法:
1.)将名称和电话变量传递给第二种方法,在那里创建出租车对象
2.)传回gResults变量,然后才创建相应的taxicompany对象?
好吧,这里有很多事情要做。
获取一些小帮手代码
首先,我想指出几个名为Simple Asynchronous Operation Runner Part 1和Part 2的博客文章。 我并不是建议你真正阅读它们(尽管你们也受到了欢迎,但我被告知它们并不容易阅读)。 你真正需要的是它们中的几个代码块放在你的应用程序中。
首先从第1部分复制“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; } }
我们还需要一个EventArgs
用于已完成的事件,该事件包含已完成的List
以及一个Error
属性,该属性将返回可能发生的任何exception。 看起来像这样: –
public class FindCompaniesCompletedEventArgs : EventArgs { private List _results; public List Results { get { if (Error != null) throw Error; return _results; } } public Exception Error { get; private set; } public FindCompaniesCompletedEventArgs(List results) { _results = results; } public FindCompaniesCompletedEventArgs(Exception error) { Error = error; } }
现在我们可以为TaxiCompanyFinder
类开始一些简单的TaxiCompanyFinder
: –
public class TaxiCompanyFinder { protected void OnFindCompaniesCompleted(FindCompaniesCompletedEventArgs e) { Deployment.Current.Dispatcher.BeginInvoke(() => FindCompaniesCompleted(this, e)); } public event EventHandler FindCompaniesCompleted = delegate {}; public void FindCompaniesAsync() { // The real work here } }
到目前为止,这是非常直截了当的。 您将注意到在调度程序上使用BeginInvoke
,因为将涉及一系列异步操作,我们希望确保在实际引发事件时它在UI线程上运行,从而更容易使用此类。
分离XML解析
您的原始代码存在的一个问题是它将枚举XML与尝试执行其他function混合在一起,它有点spagetti。 我识别的第一个函数是解析XML以获取名称和电话号码。 将此函数添加到类中: –
IEnumerable 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(); } } } }
请注意,此函数从xml生成一组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; }
核心function
将以下函数添加到类中,这是执行所有真正异步工作的函数: –
private IEnumerable FindCompanies(Uri initialUri) { var results = new List(); string baseURL = "https://api.search.live.net/xml.aspx?Appid=&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)); }
它实际上看起来很直接,几乎像同步代码,这是重点。 它获取包含所需集合的初始xml,创建TaxiCompany
对象集。 它通过集合添加每个的Total
值。 最后,完整的活动将由全套公司解雇。
我们只需要填写FindCompaniesAsync
方法: –
public void FindCompaniesAsync() { Uri initialUri = new Uri("ConstructUriHere", UriKind.Absolute); FindCompanies(initialUri).Run((e) => { if (e != null) OnFindCompaniesCompleted(new FindCompaniesCompletedEventArgs(e)); }); }
我不知道最初的Uri是什么,或者你是否需要以某种方式使用paramatise,但你只需要调整这个函数。 真正的魔法发生在Run
扩展方法中,这将通过所有异步操作慢跑,如果有任何返回exception,则完成的事件将触发Error
属性设置。
使用class级
现在你可以像这样使用这个类:
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);
如果你想让公司总排名最高的公司。
您可以将任何对象作为“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将对象传递给异步回调的一般想法应该可以正常工作。
有关详细信息,请查看MSDN上的AsyncCompletedEventArgs.UserState定义 。
上述就是C#学习教程:Windows Phone 7中的异步XML读取分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/957033.html