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

时间:2011-03-17 00:37:15

标签: silverlight-4.0 bing-maps system.reactive geocode

我正在学习使用我正在开发的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返回null。任何想法或建议将不胜感激。

3 个答案:

答案 0 :(得分: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]);
}

如果您只需要第一个有效值(地址位置不太可能更改),请在.Take(1)Where之间添加Select

编辑:如果您想要专门处理未找到的地址,您可以返回结果并让消费者处理它,或者您可以返回例外并提供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())
    );

答案 1 :(得分: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();

让我知道你怎么走。

答案 2 :(得分:0)

理查德(和其他人),

所以我有代码返回位置,我订阅了调用代码。这是(希望)最后一期。当我调用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();

在Subscribe之外执行的Output.Text赋值在Subscribe完成之前执行并显示零,然后subscribe中的那个显示新的位置信息。

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

思考,评论和建议?