问题描述
所以我有一个发现出租车公司的名单,并从兵拉动其姓名和地址成功和填充,正在向用户显示一个列表框一个双赢的手机应用程序。现在我想要做的是,寻找每一个在Bing上这些条款,发现点击每个搜索长期回报的数量,并相应地对他们进行排名(一种人气排名的松)
So I have a Win Phone app that is finding a list of taxi companies and pulling their name and address from Bing successfully and populating a listbox that is being displayed to users. Now what I want to do is, to search for each of these terms on Bing, find the number of hits each search term returns and rank them accordingly (a loose sort of popularity ranking)
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("&","&");
}
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;
}
}
所以,code的该位找到出租车公司,并启动一个异步任务找到搜索结果(gResults)的数量,以创建每个teaxicompany对象。
So that bit of code finds the taxi company and launches an asynchronous task to find the number of search results ( gResults ) to create each teaxicompany object.
//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搜索结果的数量,但问题是,因为它启动异步没有办法在第二个方法,合适的公司在方法1中获得的gResults关联有什么办法要么
The above snipped finds the number of search results on bing, but the problem is since it launches async there is no way to correlate the gResults obtained in the 2nd method with the right company in method 1. Is there any way to either:
1)通过姓名和电话变量进入第二个方法来创建有出租车对象
1.) Pass the name and phone variables into the 2nd method to create the taxi object there
2)传回gResults变量,然后才创建相应taxicompany对象?
2.) Pass back the gResults variable and only then create the corresponding taxicompany object?
推荐答案
右键也有很多在这里做。
Right well there is a lot do here.
获得一些小帮手code
首先,我要你点一对夫妇的博客文章名为Simple异步操作亚军第1部分并Part 2 。我不建议你实际阅读(虽然欢迎你太多,但我已经告诉他们不是方便阅读)。你真正需要的是一对夫妇从他们身上code块把你的应用程序。
First off I want to point you to a couple of blog posts called Simple Asynchronous Operation Runner Part 1 and Part 2. I'm not suggesting you actually read them (although you're welcome too but I've been told they're not easy reading). What you actually need is a couple of code blocks from them to put in your application.
首先,从第1部分副本从AsyncOperationService框中code,将其放置在新的类文件在您的项目称为AsyncOperationService.cs。
First from Part 1 copy the code from the "AsyncOperationService" box, place it in new class file in your project called "AsyncOperationService.cs".
其次,您需要从第2部分的DownloadString功能,你可以把那个地方,但我建议你创建一个名为WebClientUtils的静态公共类,并把它放在那里。
Second you'll need the "DownloadString" function from Part 2. You could put that anywhere but I recommend you create a static public class called "WebClientUtils" and put it in there.
解决方案概述
我们要创建一个类( TaxiCompanyFinder
),它具有触发关闭异步工作得到你之后的结果只有一个方法,然后有一个事件当任务完成时引发。
We're going to create a class (TaxiCompanyFinder
) that has a single method which fires off the asynchronous job to get the results you are after and then has an event that is raised when the job is done.
所以,让我们开始吧。你有一个 TaxiCompany
类,我会在这里,这样的例子尽可能完整的创造我自己的: -
So lets get started. You have a TaxiCompany
class, I'll invent my own here so that the example is as complete as possible:-
public class TaxiCompany
{
public string Name { get; set; }
public string Phone { get; set; }
public int Total { get; set; }
}
我们还需要一个 EventArgs的
为承载完成的名单,LT完成事件; TaxiCompany&GT;
,并在错误
属性,将返回可能发生的任何异常。这看起来是这样的: -
We also need an EventArgs
for the completed event that carries the completed List<TaxiCompany>
and also an Error
property that will return any exception that may have occured. That looks like this:-
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
类: -
Now we can make a start with some bare bones for the TaxiCompanyFinder
class:-
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
}
}
这是pretty向前伸直为止。你会注意到在调度使用的BeginInvoke
,因为那里将是一系列的参与,我们希望确保当事件实际募集它的异步操作运行在UI线程使它更容易消耗此类
This is pretty straight forward so far. You'll note the use of BeginInvoke
on the dispatcher, since there are going to be a series of async actions involved we want to make sure that when the event is actually raised it runs on the UI thread making it easier to consume this class.
分离XML解析
你的一个原来的code的问题是,它列举混合XML与尝试做其它功能,它都有点spagetti。我中标识第一功能是XML的解析得到的名称和电话号码。此功能添加到类: -
One of the problems your original code has is that it mixes enumerating XML with trying to do other functions as well, its all a bit spagetti. First function that I indentified is the parsing of the XML to get the name and phone number. Add this function to the class:-
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();
}
}
}
}
请注意,此函数产生一组 TaxiCompany
从XML实例,但不尝试做别的。另外,使用 ReadElementContentAsString
的这使得整洁阅读。此外,XML字符串的消费是非常平滑的。
Note that this function yields a set of TaxiCompany
instances from the xml without trying to do anything else. Also the use of ReadElementContentAsString
which makes for tidier reading. In addition the consuming of the xml string is much smoother.
出于类似的原因,此功能添加到类: -
For similar reasons add this function to the class:-
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;
}
的核心功能
下面的函数添加到类,这是完成所有的实际工作异步功能: -
Add the following function to the class, this is the function that does all the real async work:-
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));
}
这实际上看起来pretty向前伸直,简直就像synchonous code这是一点。它包含去抓取你所需要的设定初始XML,创建了集 TaxiCompany
对象。它通过一套foreaches添加总
的每个值。最后完成的事件被触发拥有整套的公司。
It actually looks pretty straight forward, almost like synchonous code which is the point. It fetchs the initial xml containing the set you need, creates the set of TaxiCompany
objects. It the foreaches through the set adding the Total
value of each. Finally the completed event is fired with the full set of companies.
我们只需要填写 FindCompaniesAsync
方法: -
We just need to fill in the FindCompaniesAsync
method:-
public void FindCompaniesAsync()
{
Uri initialUri = new Uri("ConstructUriHere", UriKind.Absolute);
FindCompanies(initialUri).Run((e) =>
{
if (e != null)
OnFindCompaniesCompleted(new FindCompaniesCompletedEventArgs(e));
});
}
我不知道最初的URI是什么,或者你是否需要以某种方式paramatise但你只需要调整这个功能。真正的魔力发生在运行
扩展方法,这种跑步通过所有异步操作,如果有返回异常则完成事件触发与错误
属性集。
I don't know what the initial Uri is or whether you need to paramatise in some way but you would just need to tweak this function. The real magic happens in the Run
extension method, this jogs through all the async operations, if any return an exception then the completed event fires with Error
property set.
使用类
现在你可以消耗这个类是这样的:
Now in you can consume this class like this:
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);
如果你想获得公司最高总在列表的顶端。
if you want to get the company with the highest total at the top of the list.
这篇关于在Windows Phone 7的异步读取XML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!