Bing Maps are great. You can geocode and correct your addresses. Depending on your level of queries and types of usage you may need to purchase credits to query Bing Maps, but I thought it was pretty straight forward. If you need to get a Bing Maps account, go here.
The thing that makes this great is it is workflow. It is not tied down to an entity. You can use it on your custom entities, you can geocode your addresses, and correct them. You can do address verification and geocoding. You can control what you do using workflow.
Ok, while this isn’t anything complicated, I thought it was a good example of a custom workflow that used an external webservice. Please don’t forget to create a signed key for your project.
Workflow Class:
using System;
using System.ServiceModel;
using System.Text;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using BingWorkflow.BingMapsGeo;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Workflow;
using Microsoft.Win32;
namespace BingWorkflow
{
[CrmWorkflowActivity("Bing Maps", "Geocode")]
public class VerifyAddressUsingBingMapsActivity :
SequenceActivity
{
// Activity code goes here.
public static DependencyProperty AddressCityProperty = DependencyProperty.Register(
"AddressCity", typeof (string), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty AddressCountryProperty = DependencyProperty.Register(
"AddressCountry", typeof (string), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty AddressLine1Property = DependencyProperty.Register(
"AddressLine1", typeof (string), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty AddressPostalCodeProperty = DependencyProperty.Register(
"AddressPostalCode", typeof (string), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty AddressStateProperty = DependencyProperty.Register(
"AddressState", typeof (string), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty BingKeyProperty = DependencyProperty.Register(
"BingKey", typeof (string), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty OutputCityProperty = DependencyProperty.Register(
"OutputCity", typeof (string), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty OutputCountryProperty = DependencyProperty.Register(
"OutputCountry", typeof (string), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty OutputLatitudeProperty = DependencyProperty.Register(
"OutputLatitude", typeof (CrmFloat), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty OutputLine1Property = DependencyProperty.Register(
"OutputLine1", typeof (string), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty OutputLongitudeProperty = DependencyProperty.Register(
"OutputLongitude", typeof (CrmFloat), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty OutputMultipleFoundProperty = DependencyProperty.Register(
"OutputMultipleFound", typeof (CrmBoolean), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty OutputPostalCodeProperty = DependencyProperty.Register(
"OutputPostalCode", typeof (string), typeof (VerifyAddressUsingBingMapsActivity));
public static DependencyProperty OutputStateProperty = DependencyProperty.Register(
"OutputState", typeof (string), typeof (VerifyAddressUsingBingMapsActivity));
[CrmInput("Address City")]
public string AddressCity
{
get { return (string)GetValue(AddressCityProperty); }
set { SetValue(AddressCityProperty, value); }
}
[CrmInput("Address Country")]
public string AddressCountry
{
get { return (string)GetValue(AddressCountryProperty); }
set { SetValue(AddressCountryProperty, value); }
}
[CrmInput("Address Line 1")]
public string AddressLine1
{
get { return (string)GetValue(AddressLine1Property); }
set { SetValue(AddressLine1Property, value); }
}
[CrmInput("Address Postal Code")]
public string AddressPostalCode
{
get { return (string)GetValue(AddressPostalCodeProperty); }
set { SetValue(AddressPostalCodeProperty, value); }
}
[CrmInput("Address State Or Province")]
public string AddressState
{
get { return (string)GetValue(AddressStateProperty); }
set { SetValue(AddressStateProperty, value); }
}
[CrmInput("Bing API Key")]
public string BingKey
{
get { return (string)GetValue(BingKeyProperty); }
set { SetValue(BingKeyProperty, value); }
}
[CrmOutput("City")]
public string OutputCity
{
get { return (string)GetValue(OutputCityProperty); }
set { SetValue(OutputCityProperty, value); }
}
[CrmOutput("Country Or Region")]
public string OutputCountry
{
get { return (string)GetValue(OutputCountryProperty); }
set { SetValue(OutputCountryProperty, value); }
}
[CrmOutput("Latitude")]
public CrmFloat OutputLatitude
{
get { return (CrmFloat)GetValue(OutputLatitudeProperty); }
set { SetValue(OutputLatitudeProperty, value); }
}
[CrmOutput("Line 1")]
public string OutputLine1
{
get { return (string)GetValue(OutputLine1Property); }
set { SetValue(OutputLine1Property, value); }
}
[CrmOutput("Longitude")]
public CrmFloat OutputLongitude
{
get { return (CrmFloat)GetValue(OutputLongitudeProperty); }
set { SetValue(OutputLongitudeProperty, value); }
}
[CrmOutput("Multiple Address Found")]
public CrmBoolean OutputMultipleFound
{
get { return (CrmBoolean)GetValue(OutputMultipleFoundProperty); }
set { SetValue(OutputMultipleFoundProperty, value); }
}
[CrmOutput("Postal Code")]
public string OutputPostalCode
{
get { return (string)GetValue(OutputPostalCodeProperty); }
set { SetValue(OutputPostalCodeProperty, value); }
}
[CrmOutput("State Or Province")]
public string OutputState
{
get { return (string)GetValue(OutputStateProperty); }
set { SetValue(OutputStateProperty, value); }
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
// Reset Outputs
OutputLatitude = new CrmFloat();
OutputLongitude = new CrmFloat();
OutputLine1 = String.Empty;
OutputCity = String.Empty;
OutputState = String.Empty;
OutputPostalCode = String.Empty;
OutputMultipleFound = new CrmBoolean {Value = false};
var key = BingKey;
if (String.IsNullOrEmpty(key) || key.ToLower() == "registry")
{
key = GetAPIKeyFromRegistry();
}
// Create Bing Maps Request
var bingRequest = new GeocodeRequest
{
Credentials = new Credentials {ApplicationId = key},
Query = AddressLine1 + ", " +
AddressCity + ", " +
AddressState + ", " +
AddressPostalCode + ", " +
AddressCountry
};
// Filter for High Confidence
var filters = new ConfidenceFilter[1];
filters[0] = new ConfidenceFilter {MinimumConfidence = Confidence.High};
bingRequest.Options = new GeocodeOptions {Filters = filters};
// Create Client
GeocodeServiceClient bingClient = null;
try
{
var geoBinding = new BasicHttpBinding(BasicHttpSecurityMode.None)
{
Name = "BasicHttpBinding_IGeocodeService",
CloseTimeout = new TimeSpan(0, 1, 0),
OpenTimeout = new TimeSpan(0, 1, 0),
ReceiveTimeout = new TimeSpan(0, 10, 0),
SendTimeout = new TimeSpan(0, 1, 0),
AllowCookies = false,
BypassProxyOnLocal = false,
HostNameComparisonMode = HostNameComparisonMode.StrongWildcard,
MaxBufferSize = 65536,
MaxBufferPoolSize = 524288,
MaxReceivedMessageSize = 65536,
MessageEncoding = WSMessageEncoding.Text,
TextEncoding = Encoding.UTF8,
TransferMode = TransferMode.Buffered,
UseDefaultWebProxy = true
};
var geoEA =
new EndpointAddress("http://dev.virtualearth.net/webservices/v1/geocodeservice/GeocodeService.svc");
bingClient = new GeocodeServiceClient(geoBinding, geoEA);
var bingResponse = bingClient.Geocode(bingRequest);
if (bingResponse.Results != null && bingResponse.Results.Length > 0)
{
if (bingResponse.Results.Length > 1)
{
OutputMultipleFound.Value = true;
}
var bingResult = bingResponse.Results[0];
if (bingResult.Locations != null && bingResult.Locations.Length > 0)
{
var bingLocation = bingResult.Locations[0];
OutputLatitude.Value = bingLocation.Latitude;
OutputLongitude.Value = bingLocation.Longitude;
}
var bingAddress = bingResult.Address;
OutputLine1 = bingAddress.AddressLine;
OutputPostalCode = bingAddress.PostalCode;
OutputCountry = bingAddress.CountryRegion;
OutputCity = String.IsNullOrEmpty(bingAddress.PostalTown)
? bingAddress.Locality
: bingAddress.PostalTown;
OutputState = String.IsNullOrEmpty(bingAddress.AdminDistrict)
? bingAddress.District
: bingAddress.AdminDistrict;
}
}
catch (Exception ex)
{
throw new WorkflowTerminatedException(
"There was an error openning the connection to Bing Maps.", ex);
}
finally
{
// Close the client
if (bingClient != null)
{
bingClient.Close();
}
}
return base.Execute(executionContext);
}
private static string GetAPIKeyFromRegistry()
{
// Opening the registry key
var key = Registry.LocalMachine;
if (key != null)
{
key = key.OpenSubKey("Software\\Microsoft Geocoder");
}
return key != null ? key.GetValue("APIKey").ToString() : null;
}
}
}


HI,
Could you include a picture of what you mean? I am one of many copy paste programmers, but would like to see the result of your programming in some type of image:)
Thank you..
PS.. I just recently discovered your blogg, and now I am sticking to it:)!!
I was wondering if you could help me?
We are using the Bing Spatial Data Services from the selections of Bing Map API’s. We are sending a group of addresses and say that there is an address in Brooklyn NY but the address is supposed to be New York, NY the service will not bring anything back. Now if you use the maps it will just automatically change it to New York, NY. Is there a parameter that can be set to automatically update this address if this happens?
I have been thrown into this project last minute so I am not that familiar with all of this. Is this someting you can help me with or point me in the right direction.
Roxy, What method are you using to handle the group of addresses?