使用 Rx 在 Bing Maps 中对地址进行地理编码

3

我正在学习如何在我正在开发的Silverlight 4应用程序中使用Rx扩展。我创建了一个示例应用程序来确定这个过程,但是我无法得到任何返回结果。

以下是主要代码:

    private IObservable<Location> GetGPSCoordinates(string Address1)
    {
        var gsc = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService") as IGeocodeService;

        Location returnLocation = new Location();
        GeocodeResponse gcResp = new GeocodeResponse();

        GeocodeRequest gcr = new GeocodeRequest();
        gcr.Credentials = new Credentials();
        gcr.Credentials.ApplicationId = APP_ID2;
        gcr.Query = Address1;

        var myFunc = Observable.FromAsyncPattern<GeocodeRequest, GeocodeResponse>(gsc.BeginGeocode, gsc.EndGeocode);
        gcResp = myFunc(gcr) as GeocodeResponse;

        if (gcResp.Results.Count > 0 && gcResp.Results[0].Locations.Count > 0)
        {
            returnLocation = gcResp.Results[0].Locations[0];
        }
        return returnLocation as IObservable<Location>;
    }

gcResp返回为空。有什么想法或建议将不胜感激。

3个回答

1
你正在订阅的可观察源是异步的,因此您不能立即在订阅后访问结果。您需要在订阅中访问结果。
更好的方法是根本不订阅,而是组合响应:
private IObservable<Location> GetGPSCoordinates(string Address1)
{
    IGeocodeService gsc = 
        new GeocodeServiceClient("BasicHttpBinding_IGeocodeService");

    Location returnLocation = new Location();
    GeocodeResponse gcResp  = new GeocodeResponse();

    GeocodeRequest gcr = new GeocodeRequest();
    gcr.Credentials = new Credentials();
    gcr.Credentials.ApplicationId = APP_ID2;
    gcr.Query = Address1;

    var factory = Observable.FromAsyncPattern<GeocodeRequest, GeocodeResponse>(
        gsc.BeginGeocode, gsc.EndGeocode);

    return factory(gcr)
        .Where(response => response.Results.Count > 0 && 
                           response.Results[0].Locations.Count > 0)
        .Select(response => response.Results[0].Locations[0]);
}

如果你只需要第一个有效值(地址的位置不太可能改变),那么在 WhereSelect 之间添加 .Take(1)

编辑:如果你想特别处理找不到地址的情况,你可以返回结果然后由消费者处理,或者你可以返回异常并在订阅时提供一个 OnError 处理程序。如果你想使用后者,你将使用 SelectMany:

return factory(gcr)
    .SelectMany(response => (response.Results.Count > 0 && 
        response.Results[0].Locations.Count > 0)
        ? Observable.Return(response.Results[0].Locations[0])
        : Observable.Throw<Location>(new AddressNotFoundException())
    );

Richard,那个可行——非常感谢。还有一个问题——如果地址无效,则根据 where 子句,该函数不返回任何内容。因此,似乎最好让我的函数返回 IObservable<GeocodeResponse>,然后能够在 Subscribe 中测试结果计数。你有任何其他建议吗?非常感谢您的帮助。 - AussieAtHeart
@AussieAtHeart - 嗨,澳洲人,任何对个别答案的回复都应该作为评论添加,而不是单独的答案。你应该考虑将这个移到Richard的答案评论中。 - James Hay
@James Hay,我只能在这篇帖子下面添加评论,而不能在其他帖子下面添加评论,即使我已经登录了。我不确定为什么会发生这种情况。 - AussieAtHeart
@AussieAtHeart - 你应该能够对自己问题的答案进行评论。问题在于你创建了多个账户。你用来添加这个答案的账户(663939)与你原始问题的账户(663488)不同,而且与你其他(已删除)答案的账户(663683)也不同。你应该使用原始账户重新登录。 - Richard Szalay
@AussieAtHeart:顺便说一下,如果您不小心创建了多个帐户,可以向管理员请求将它们合并。请参见如何链接/合并/组合/关联两个帐户/用户?(匿名/未注册/cookie或OpenID/已注册) - Helen

0

如果你展开myFunc的类型,你会发现它是Func<GeocodeRequest, IObservable<GeocodeResponse>>

Func<GeocodeRequest, IObservable<GeocodeResponse>> myFunc =
    Observable.FromAsyncPattern<GeocodeRequest, GeocodeResponse>
        (gsc.BeginGeocode, gsc.EndGeocode);

当你调用myFunc(gcr)时,返回的是一个IObservable<GeocodeResponse>而不是一个GeocodeResponse。你的代码myFunc(gcr) as GeocodeResponse返回null因为类型转换是无效的。
你需要做的是要么获取observable的最后一个值,要么执行订阅操作。调用.Last()会阻塞程序。如果你调用.Subscribe(...),你的响应将通过回调线程传递。
试试这个:
gcResp = myFunc(gcr).Last();

请告诉我你的进展。


0

Richard(和其他人),

所以我已经让代码返回了位置,并且调用代码进行了订阅。这是(希望)最后的问题。当我调用GetGPSCoordinates时,下一条语句会立即执行,而不等待订阅完成。以下是在按钮的OnClick事件处理程序中的示例。

Location newLoc = new Location();

GetGPSCoordinates(this.Input.Text).ObserveOnDispatcher().Subscribe(x =>
            {
             if (x.Results.Count > 0 && x.Results[0].Locations.Count > 0)
                  {
                     newLoc = x.Results[0].Locations[0];
                     Output.Text = "Latitude: " + newLoc.Latitude.ToString() +
  ", Longtude: " + newLoc.Longitude.ToString();
                  }
             else
                  {
                    Output.Text = "Invalid address";
                  }
});
            Output.Text = " Outside of subscribe --- Latitude: " + newLoc.Latitude.ToString() +
  ", Longtude: " + newLoc.Longitude.ToString();

Output.Text的赋值在Subscribe之外执行,因此会在Subscribe完成之前显示零,然后Subscribe内部的“1”会显示新的位置信息。

这个过程的目的是获取位置信息,然后将其保存在数据库记录中,并且我正在使用Foreach循环逐个处理多个地址。我选择Rx扩展作为解决方案,以避免异步回调成为编码陷阱的问题。但似乎我已经用另一个陷阱来交换了它。

有什么想法、评论或建议吗?


(这应该是对您原始问题的编辑,但我意识到在多个账户问题得到管理员解决之前,您不能这样做) - Richard Szalay
Subscribe 后的代码会立即执行,因为 Subscribe 是非阻塞的。你无法避免异步工作的事实,你所能做的就是通过将代码移动到订阅处理程序中来处理它。 - Richard Szalay
但是,你如何在概念上处理这种情况呢?如果有人有一份地址列表并想将它们转换为GPS坐标或一份他们想要翻译的单词列表,最好的处理方式是什么?我发现唯一的其他选择是Async CTP,但它还没有准备好。有任何示例吗?我已经在这里和其他网站上搜索了,但没有找到任何东西。 - AussieAtHeart
我进行了更多的研究,看起来在 Rx 中使用 SelectMany 进行链式操作是最好的选择。欢迎提出其他建议。 - AussieAtHeart
使用 SelectMany 进行链式调用确实是一个常见的选择。您还可以使用 ForkJoin 来处理多个并行调用。 - Richard Szalay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接