Windows Phone 7中的异步XML读取

var*_*rin 1 c# asynchronous windows-phone-7

所以我有一个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<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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

因此,该位代码找到出租车公司并启动异步任务以查找创建每个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();
                    }

                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

上面的剪辑找到了bing上搜索结果的数量,但问题是因为它启动异步,没有办法将第二种方法中获得的gResults与方法1中的正确公司相关联.有没有办法:

1.)将名称和电话变量传递给第二种方法,在那里创建出租车对象

2.)传回gResults变量,然后才创建相应的taxicompany对象?

Ant*_*nes 8

好吧,这里有很多事情要做.

获取一些小帮手代码

首先,我想指出几个名为Simple Asynchronous Operation Runner Part 1Part 2的博客文章.我并不是建议你真正阅读它们(虽然你们也受欢迎,但我被告知他们阅读起来并不容易).你真正需要的是它们中的几个代码块放在你的应用程序中.

首先从第1部分复制"AsyncOperationService"框中的代码,将其放在项目中名为"AsyncOperationService.cs"的新类文件中.

其次,您需要第2部分中的"DownloadString"函数.您可以将其放在任何位置,但我建议您创建一个名为"WebClientUtils"的静态公共类并将其放在那里.

解决方案概要

我们将创建一个class(TaxiCompanyFinder),它有一个方法可以触发异步作业以获得您所获得的结果,然后在作业完成时引发一个事件.

让我们开始吧.你有一个TaxiCompany班级,我会在这里发明我自己的,以便示例尽可能完整: -

public class TaxiCompany
{
    public string Name { get; set; }
    public string Phone { get; set; }
    public int Total { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我们还需要一个EventArgs完成的事件,其中包含已完成的事件List<TaxiCompany>以及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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以为这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
    }
}
Run Code Online (Sandbox Code Playgroud)

到目前为止,这非常简单.您将注意到在BeginInvoke调度程序上的使用,因为将涉及一系列异步操作,我们希望确保在实际引发事件时它在UI线程上运行,从而更容易使用此类.

分离XML解析

您的原始代码存在的一个问题是,它将枚举XML与尝试执行其他功能混合在一起,它有点spagetti.我识别的第一个函数是解析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();
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

请注意,此函数TaxiCompany从xml 生成一组实例,而不尝试执行任何其他操作.使用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;
    }
Run Code Online (Sandbox Code Playgroud)

核心功能

将以下函数添加到类中,这是执行所有真正异步工作的函数: -

    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));
    }
Run Code Online (Sandbox Code Playgroud)

它实际上看起来很直接,几乎像同步代码,这是重点.它获取包含所需集合的初始xml,创建TaxiCompany对象集.它通过集合添加Total每个值的foreaches .最后,完成的活动将由全套公司解雇.

我们只需要填写FindCompaniesAsync方法: -

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

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

我不知道最初的Uri是什么,或者你是否需要以某种方式使用paramatise,但你只需要调整这个函数.真正的魔法发生在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();
Run Code Online (Sandbox Code Playgroud)

您也可以考虑使用

        TaxiCompanyDisplayList.ItemsSource = args.Results.OrderByDescending(tc => tc.Total);
Run Code Online (Sandbox Code Playgroud)

如果你想让公司总排名最高的公司.