C# WindowsPhone7中的异步XML读取
因此,我有一个Win手机应用程序,它可以找到出租车公司的名单,并成功地从Bing中提取他们的姓名和地址,然后填充一个列表框,显示给用户。现在我想做的是,在Bing上搜索每一个搜索词,找到每个搜索词返回的点击数,并对它们进行相应的排名(一种松散的流行度排名) 上面的截图可以找到bing上的搜索结果数量,但问题是因为它启动了async,所以无法将第2种方法中获得的结果与方法1中的正确公司关联起来。有没有办法: 1.)将name和phone变量传递到第二个方法中,在那里创建滑行对象C# WindowsPhone7中的异步XML读取,c#,asynchronous,windows-phone-7,C#,Asynchronous,Windows Phone 7,因此,我有一个Win手机应用程序,它可以找到出租车公司的名单,并成功地从Bing中提取他们的姓名和地址,然后填充一个列表框,显示给用户。现在我想做的是,在Bing上搜索每一个搜索词,找到每个搜索词返回的点击数,并对它们进行相应的排名(一种松散的流行度排名) 上面的截图可以找到bing上的搜索结果数量,但问题是因为它启动了async,所以无法将第2种方法中获得的结果与方法1中的正确公司关联起来。有没有办法: 1.)将name和phone变量传递到第二个方法中,在那里创建滑行对象 2.)传回gRes
2.)传回gResults变量,然后才创建相应的taxicompany对象?您可以将任何对象作为“UserState”传递,作为进行异步调用的一部分,异步调用将在异步回调中可用。因此,在您的第一段代码中,更改:
c.DownloadStringAsync(new Uri(baseURL));
c.DownloadStringCompleted += new DownloadStringCompletedEventHandler(findTotalResults);
致:
这将允许您执行以下操作:
void findTotalResults(object sender, DownloadStringCompletedEventArgs e)
{
lock (this)
{
TaxiCompany t = e.UserState;
string s = e.Result;
...
}
}
我还没有测试这段代码本身,但是使用eventag的UserState将对象传递给异步回调的一般想法应该是可行的
查看以获取更多信息。作为异步调用的一部分,您可以将任何对象作为“UserState”传递,异步调用将在异步回调中可用。因此,在您的第一段代码中,更改:
c.DownloadStringAsync(new Uri(baseURL));
c.DownloadStringCompleted += new DownloadStringCompletedEventHandler(findTotalResults);
致:
这将允许您执行以下操作:
void findTotalResults(object sender, DownloadStringCompletedEventArgs e)
{
lock (this)
{
TaxiCompany t = e.UserState;
string s = e.Result;
...
}
}
我还没有测试这段代码本身,但是使用eventag的UserState将对象传递给异步回调的一般想法应该是可行的
有关更多信息,请参阅。好的,这里有很多工作要做 获取一些小助手代码 首先,我想让你们看几篇名为和的博客文章。我不是建议你真的读它们(尽管你也很受欢迎,但我听说它们不容易读)。实际上,您需要的是从它们中提取几个代码块,以便将其放入应用程序中 从第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; }
}
对于带有completed列表的completed事件,我们还需要一个EventArgs
,以及一个Error
属性,该属性将返回可能发生的任何异常。看起来是这样的:-
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;
}
}
到目前为止,这是相当直截了当的。您将注意到在dispatcher上使用了BeginInvoke
,因为将涉及一系列异步操作,我们希望确保在实际引发事件时,它在UI线程上运行,从而更容易使用此类
分离XML解析
您的原始代码存在的一个问题是,它将枚举XML与尝试执行其他函数混为一谈,这完全有点多余。我识别的第一个函数是解析XML以获取姓名和电话号码。将此函数添加到类:-
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();
}
}
}
}
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));
}
我不知道初始Uri是什么,也不知道您是否需要以某种方式进行参数化,但您只需要调整这个函数。真正的神奇发生在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();
你也可以考虑使用< /P>
TaxiCompanyDisplayList.ItemsSource = args.Results.OrderByDescending(tc => tc.Total);
如果你想让总数最高的公司排在榜首。没错,这里有很多事情要做
获取一些小助手代码
首先,我想让你们看几篇名为和的博客文章。我不是建议你真的读它们(尽管你也很受欢迎,但我听说它们不容易读)。实际上,您需要的是从它们中提取几个代码块,以便将其放入应用程序中
从第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; }
}
对于带有completed列表的completed事件,我们还需要一个EventArgs
,以及一个Error
属性,该属性将返回可能发生的任何异常。看起来是这样的:-
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;
}
}
到目前为止,这是相当直截了当的。您将注意到在dispatcher上使用了BeginInvoke
,因为将有一系列异步操作
TaxiCompanyDisplayList.ItemsSource = args.Results.OrderByDescending(tc => tc.Total);