C# WindowsPhone7中的异步XML读取

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

因此,我有一个Win手机应用程序,它可以找到出租车公司的名单,并成功地从Bing中提取他们的姓名和地址,然后填充一个列表框,显示给用户。现在我想做的是,在Bing上搜索每一个搜索词,找到每个搜索词返回的点击数,并对它们进行相应的排名(一种松散的流行度排名)

上面的截图可以找到bing上的搜索结果数量,但问题是因为它启动了async,所以无法将第2种方法中获得的结果与方法1中的正确公司关联起来。有没有办法:

1.)将name和phone变量传递到第二个方法中,在那里创建滑行对象


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);