Supply Chain Management in Dynamics AX

This blog contains information and feedback on the Supply Chain Management area in Microsoft Dynamics AX

Transportation management (TMS) mileage engine based on Bing Maps

Transportation management (TMS) mileage engine based on Bing Maps

Rate This
  • Comments 32

Transportation management module (TMS) includes a number of extension points that allow for custom algorithms for tasks such as calculations for rates and transit time or mileage on a specific route. Dynamics AX 2013 R3 comes with a number of engines, including a point-to-point mileage engine that is defined as a Microsoft.Dynamics.Ax.Tms.Bll.P2PMileageEngine engine type. The point-to-point mileage engine requires that data is defined in AX in order to determine mileage between two addresses. This engine is good for smaller size distribution networks, but can become expensive in maintenance when working with a large number of destinations (for example, for use with an online retail store). For large distribution networks it makes sense to use external services, such as Bing Maps.

This blog post describes how to build a custom engine that can retrieve mileage data by using the Bing Maps SOAP services. There are other services that can be used for this purpose, for example the Bing Maps REST services, but these are not described in this document.

The following article provides more information about the Bing Maps SOAP services:

The following article provides more information about the Bing Maps REST services:

For more information about how to build a custom TMS engine, read following whitepaper:

The zipped source code of the discussed C# code is available for download as this blog file attachment


Despite installing Visual Studio tools for AX, we will need to create access keys in order to consume Bing Maps services. For commercial purposes we would need to create an enterprise key, but for testing a free trial key is enough. You can create a new key on following portal:

Note that this requires a valid Live ID account.

The new key will be displayed in your profile. The following illustration shows an example.

Implement a custom engine

To implement a custom engine, follow these steps:

1.       Open the following project in the AOT:

\Visual Studio Projects\C Sharp Projects\Microsoft.Dynamics.AX.Tms
in Visual Studio.

2.       Add a basic TMS engine implementation project to your solution (C# class library). Let’s call the project CustomTMSEngines. Add reference to Microsoft.Dynamics.AX.Tms and add the project to AOT. Configure it to be deployed on AOS.

For step-by-step instructions for creating such a project, see the whitepaper titled Implementing and Deploying Transportation Management Engines for Microsoft Dynamics AX 2012 R3. The paper describes how to build simple TMS engines.

3.       Add service references to Bing Maps geocode service and Bing Maps route service.

To do that, follow these steps:

a.       Select the project node in Visual Studio

b.      Context menu>Add Service Reference

c.       Put this URL to “Address” and select “Go”:

d.      Name it BingMapsGeocodeService

e.      Click OK

f.        Repeat steps A-E for the following WSDL, and name it BingMapsRouteService:

4.       Your Solution Explorer should now look like this:

 5.       Create a new class called BingMapsServicesClient.

The purpose of this class is to retrieve the distance from address A to address B by processing requests and responses from the Bing Maps service. The process consists of two steps:

a.       Retrieve geo-locations for address A and address B

b.      Retrieve distance from geo-location of A to B

For now, let’s just implement the constructor with one parameter “key” that will be part of the class state. The key parameter holds the value of the authentication key that we created earlier using our Live ID account.

/// Provides access to Bing Maps services.
internal class BingMapsServicesClient
    private string key;

    /// Creates a new instance of <c>BingMapsServicesClient</c> class.
///<param name="key">Bing Maps services access key.</param>
    public BingMapsServicesClient(string key)
        this.key = key;

 6.       The next step is to add a method to BindMapsServicesClient, which will allow us to retrieve geo-location, based on address string.



private BingMapsRouteService.Location GeolocationAddress(string address)
    BingMapsRouteService.Location result = null;
    GeocodeRequest geocodeRequest = new GeocodeRequest(); 

    // Set the credentials using a valid Bing Maps key
    geocodeRequest.Credentials = new BingMapsGeocodeService.Credentials();
    geocodeRequest.Credentials.ApplicationId = this.key; 

    // Set the full address query
    geocodeRequest.Query = address;
    // Set the options to only return high confidence results 
    ConfidenceFilter[] filters = new ConfidenceFilter[1];
    filters[0] = new ConfidenceFilter();
    filters[0].MinimumConfidence = BingMapsGeocodeService.Confidence.High;

    // Add the filters to the options
    GeocodeOptions geocodeOptions = new GeocodeOptions();
    geocodeOptions.Filters = filters;
    geocodeRequest.Options = geocodeOptions;

    // Make the geocode request
    Binding binding = new BasicHttpBinding();
    EndpointAddress endpointAddress = new EndpointAddress(@"");

    GeocodeServiceClient geocodeService = new GeocodeServiceClient(binding, endpointAddress);
    GeocodeResponse geocodeResponse = geocodeService.Geocode(geocodeRequest); 

    if (geocodeResponse.Results.Length > 0)
        result = new BingMapsRouteService.Location()
            Latitude = geocodeResponse.Results[0].Locations[0].Latitude,
            Longitude = geocodeResponse.Results[0].Locations[0].Longitude
    return result;


There are a few interesting things in this method that are worth mentioning. First, the method signature contains only one string parameter for address, which is treated as query for the Bing Maps service. The format of this address must enable Bing Maps to identify the address. The best way to test whether your query fits this format is to test it through your web browser at

Second, the endpoint address URI is hardcoded in the method. Typically endpoint and bindings are defined in the application configuration file. This may not be the most convenient solution for AX because configuration changes (for example, switching between production and test service URI) would require that the AOS is restarted. The best approach here would probably be to query the URI from AX database using AX Managed Interop. A convenient way to do this is to use the proxy classes for AX tables and LINQ to AX for building queries. The following article provides information about working with LINQ to AX:

If we decide to source the URI from AX, we need to keep security in mind and treat the URI as a protected resource and do the proper threat modeling.

7.       The next step is to add the method for retrieving mileage between two geographical locations. Add the following method to the BingMapsServicesClient class.


private double RetrieveDistance(
    BingMapsRouteService.Location locationFrom,
    BingMapsRouteService.Location locationTo)
    BingMapsRouteService.RouteRequest routeRequest = new BingMapsRouteService.RouteRequest();
    routeRequest.Credentials = new BingMapsRouteService.Credentials();
    routeRequest.Credentials.ApplicationId = this.key;
    routeRequest.Waypoints = new Waypoint[2]
        new Waypoint() { Description = "Start", Location = locationFrom },
        new Waypoint() { Description = "End", Location = locationTo }

    Binding binding = new BasicHttpBinding();
    EndpointAddress endpointAddress = new EndpointAddress(@""); 

    RouteServiceClient routeService = new RouteServiceClient(binding, endpointAddress);
RouteResponse routeResponse = routeService.CalculateRoute(routeRequest);
    return routeResponse.Result.Summary.Distance / 1.609344;


 Again, this method uses a hardcoded URI for the route service, but it can be changed in a similar way as the GeolocationAddress method. Note that the distance that is retrieved from the Bing Maps service uses metric units. To convert kilometers to miles we divided it by the fixed factor.

 8.       To ensure that our code can compile we need proper imports. Add the following imports to BingMapsMileageEngine.cs:

    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using CustomTMSEngines.BingMapsGeocodeService;
    using CustomTMSEngines.BingMapsRouteService;


 9.       The last part that is missing in the BingMapsServicesClient class is a public method that will allow us to call the retrieved distance between two addresses without having to couple the consumer with the Bing Maps service proxy object model. The following method does this. 

/// Retrieves address between two fully specificed addresses.
///<param name="addressFrom">Origin address.</param>
///<param name="addressTo">Destination address/</param>

///<returns>Distance specified in miles.</returns>

public double RetrieveDistance(string addressFrom, string addressTo)


    BingMapsRouteService.Location locationStart = this.GeolocationAddress(addressFrom);
BingMapsRouteService.Location locationEnd = this.GeolocationAddress(addressTo);

return RetrieveDistance(locationStart, locationEnd);

 10.   The next part is to implement is the actual mileage TMS engine that will consume the functionality provided by the BingMapsServiceClient class. The following code block contains the full implementation of this class. 

namespace CustomTMSEngines
    using System;
    using System.ServiceModel;
    using System.Xml.Linq;
    using Microsoft.Dynamics.Ax.Tms.Bll;
    using Microsoft.Dynamics.Ax.Tms.Data;
    using Microsoft.Dynamics.Ax.Tms.Utility;
    using Microsoft.Dynamics.Ax.Tms;

    /// The <c>BingMapsMileageEngine</c> class retrieves mileage data using Bing Maps services. 
    class BingMapsMileageEngine : IMileageEngine
        private string mileageEngineCode;

        /// Initializes the engine instance.
        ///<param name="mileageEngine"></param>
        public void Initialize(TMSMileageEngine mileageEngine)
            mileageEngineCode = mileageEngine.MileageEngineCode;

        /// Retrieves mileage.
        ///<param name="transactionFacade">TMS transaction facade.</param>
        ///<param name="currentElement">XML element to for which to apply the mileage result.</param>
        ///<returns>The mileage response.</returns>
        public MileageHelperResponse RetrieveMiles(TransactionFacade transactionFacade, XElement currentElement)
            MileageHelperResponse result = new MileageHelperResponse(this.mileageEngineCode);
            string key = "YOUR BING MAPS ACCESS KEY IS ASSIGNED HERE";

            XElement pickUp = currentElement.GetAddress(AddTypeXmlConstants.Pickup);
            XElement dropOff = currentElement.GetAddress(AddTypeXmlConstants.DropOff);
            BingMapsServicesClient bingMapsServicesClient = new BingMapsServicesClient(key);

                string addressFrom = this.retrieveAddress(pickUp);
                string addressTo = this.retrieveAddress(dropOff);
            result.Miles = Convert.ToDecimal(bingMapsServicesClient.RetrieveDistance(addressFrom, addressTo));
            catch (FaultException)
                result.ErrorDto = new ErrorDto()
                    Code = "100",
                    Description = "Failure when retrieving mileage data from Bing Maps service."
            catch (Exception e)
                throw TMSException.Create(string.Format("Unexpected failure happened when calling {0} mileage engine.", mileageEngineCode), e);
            return result;

        private string retrieveAddress(XElement addressElement)
            return formatAddress(

        private string formatAddress(string address, string city, string stateProvince, string postalCode, string country)
            string result = string.Format("{0}, {1}, {2} {3}, {4}", address, city, stateProvince, postalCode, country);

            return result;



To make this class work we must ensure that the correct Bing Maps service access key is assigned to the key variable in the RetrieveMiles method. For testing, we can hardcode the value, but this approach should not be used for the production environment. The access key should be treated as a protected resource and should be stored and sourced securely.

Enablement and testing

After deploying the solution to the server and restarting the AOS, the CustomTMSEngines is available under [server bin]\VSAssemblies. We can now create a mileage engine that uses our new engine code and enable it for rating. To do that, follow these steps:

1.       Click Transportation management > Setup > Engines > Mileage engines.

2.       Create a new engine using the following fields values:

a.       Mileage engine=BingMaps

b.      Name=Bing Maps Mileage Engine

c.       Engine assembly=CustomTMSEngines.dll

d.      CustomTMSEngines.BingMapsMileageEngine


We do not need to define engine metadata and data because our engine uses external sources.

The following screenshot shows the BingMaps engine with the settings we’ve just entered.

 We don’t have to define engine parameters because the current implementation does not require them.

Yes, the form should be empty, as shown in the screenshot above.

3.       Now it’s time to make this engine consumed by a rate engine that uses mileage when calculating rates. Click Transportation management > Setup > Engines > Rate engine.

4.       For this exercise we will modify a preconfigured rate engine so that it uses BingMaps mileage engine (we could also define a new rate engine based on the already defined engine type, associate it with our new mileage engine, and use them side-by-side). If we initialized our system using the Initialize base engine data button in Transportation management parameters, we find a record that uses following rate engine type:


Select the engine, and then click the Parameters button. 

5.       We can now associate the MileageRateEngine with the BingMaps engine. To do this, set BingMaps as the value of the MileageEngineCode parameter, as shown in the following screenshot.


6.       Now let’s open the Rate route workbench and perform an ad-hoc rating, as shown in the following as on the screenshot.


7.       If we compare the results of the ad-hoc rating with the routing result from the Bing Maps web page, the result of 6.1 miles is the same as on the mileage response from TMS system.


It is fairly easy to build TMS engines that consume external services. This blog post has described a sample case in which we built a mileage engine that consumes mileage data from Bing Maps. Similar steps would be used to integrate with other mileage data providers. There are some products on the market that are specifically designed for transportation mileage calculation.

To wrap this up, I’d like to share a few tips with you if you decide to build your own engines that communicate with external systems:

·        Make sure to threat model your solutions, and ensure that service endpoint data and authentication data is secured.

·        Visual Studio unit tests are a good approach for testing the engine in isolation. If you go for X++ unit tests, restarting the AOS is required for every production code change.

·        The advantage of relying on external routing systems is that you don’t need to maintain address and routing data. Unfortunately, the disadvantage is that it can happen that some addresses recorded in AX may not be available in your external mileage system (for example, street numbers may be missing). You may consider writing a specific test or a job that iterates through all the transportation destinations and verifies that the mileage data can be retrieved. Otherwise you will discover those during operation.


  • Hi Pawel,

    I tried to create same engine Bing Map Mileage engine, i got a temporary key from bing maps.

    But i after Enabling the engine and clicking on Rate Route workbench --> Rate Shop

    i was not able to get the miles as shown above in your blog with the same address from and to, after processing i was able to get lines with the exceptions in the exception column, i am not able to add snapshot shots for you to view. But can you help if you understood my issue ?

  • Hi Pooja,

    I assume that you first tried smoke-test your implementation with the same addresses as in this tutorial, right? I cannot guess what's happening on your environment. I would suggest to put a break point on following line in RetrieveDistance method:

    RouteResponse routeResponse = routeService.CalculateRoute(routeRequest);

    And see what happens. I would guess that this the likely place that is throwing exception. Please inspect the exception message.


  • Hi Pawel,

    Firstly --

    I tried to do some changes in the setup and i was able to get 6.12 Miles in the miles column. But as you have only one shipping Carrier as "TestMileage" i have 6 Shipping Carriers and if you can see there is an Exception tab on this form when i click on that exception it has different exceptions for each of the 6 shipping carrier

    Point 2 point  truck --  Has Rate Base Detail could not be found          

    Truck Carrier --           Carrier fuel index not found

    Parcel Carrier --         Could not find transit time

    Rail Carrier  --            Could not find transit time

    Flower moving --        Could not find transit time

    Zone 2 Zone truck --  Could not find transit time

    I need to do some setups to get rid of these exceptions ??

    Secondly --

    If i am putting some other addresses in From and To except the Microsoft ones, i am not getting any result in Miles it is 0.00

    and If i click on the exception Tab

    Error shows as "Failure when retrieving mileage data from Bing Maps Service" for all the Shipping Carriers

    This could be the key issue, that i need to have a paid subscription ??

  • Hi Pooja,

    For your first part of the question - this is totally not related to the mileage system. You seem to be working with demo data, which is quite limited. My guess is that in your Rate route workbench you are trying to execute rate shopping based on origin and destination addresses that miss setup data in TMS system. For each of the carriers that didn't return result, find the "Rating profile" and verify that any "address" specific filter and date effectivity is correct on associated "transit time engine", rate master ("rate base assignment" and then related "rate base") and "carrier fuel index". If you are just interested in testing mileage, perhaps you want to disable the other carriers, transit time calculation and so on, not to pollute the picture.

    As for the second part of the question, you do not need paid subscription. The Bing maps trial key limits the number of requests and the lifetime of the key to 90 days. That should not limit the availability addresses that you use for mileage calculation.

    If you look at one of the last sentences in this blog post:

    "Unfortunately, the disadvantage is that it can happen that some addresses recorded in AX may not be available in your external mileage system"

    you may consider one of the two possibilities:

    1. You are trying to find address that cannot be found by Bing Maps (e.g. building and apartment numbers seem to be missing in some georgraphical locations).

    2. You are working in a geographical location, that uses different address formatting that we used in the code sample described in this tutorial (USA).

    One way or another, please use following steps to find out what's going on:

    1. Put a breakpoint in first line of following method

    public double RetrieveDistance(string addressFrom, string addressTo)

    2. Once you hit the breakpoint, take the addressFrom value and addressTo value, and try to find the addresses and the direction using BingMaps portal:

    3. If you fail to find the address, try to start from removing the building number. If it helps, you will start getting more results, but less accurate (I guess in most of the cases it will be a matter of a couple of hundred meters). The you can try to remove stateProvince from your request.

    If you find out what works for you, then you will need to apply correction of address formatting method, which is this one:

    private string formatAddress(string address, string city, string stateProvince, string postalCode, string country)

    A question: Can you tell me what country data you are using? Can you provide a sample address you are trying to work with?


  • I am using USMF company and i tested my addresses were not actual addresses and i checked with the new addresses, it is working now. Thank you for your help.

  • Hi Pawel,

    May i know how did you get the links for Bing map SOAP and REST service ?

  • Hi Pooja,

    At the beginning of this blog post I put some links that explain details about the services. These are available on msdn:

    The following article provides more information about the Bing Maps SOAP services:

    The following article provides more information about the Bing Maps REST services:

    Please follow these for more info about the wsdl references and so on.


  • Hi; I'm trying to follow the tutorial by in the steep 5 it's not appearing the BingMapsEngine, I've tried it on the record with the Microsoft.Dynamics.Ax.Tms.Bll.MileageRateEngine. value...... What can I do?

  • Simply put you are wasting your time, Bing Maps is NOT an accepted transportation industry standard because it primarily geared for retail consumer use. Users who are engaged in commercial transportation and pay transport charges based on established truck routes, use one of the providers such as PC Miler 27. Tier I TMS software do not use Bing Maps.

  • Hello,

    I've been trying to run the engine .. the code in Visual Studio apparently works well but when I enter the address data in the Tmsraterouteworkbench form as shown in the picture, if I debug and see the variable distance gives the value correctly, but when it is displayed in dynamics does not show anything, don't return any value in the grid ..

    On the other hand only works project code if I click the button rates in the form .. Show rates on the grid but the field of mileage with value 0 , however if I click the button route does nothing get fields to 0, the debugger does not start at the breakpoint of visual studio .. most likely I have something that might not have set well but have followed all the steps as the tutorial says but does not work

  • This is very good article about TMS engines, I tried to follow the whole process but when I clicked on the Rate Shop I was getting this error message below

    " Error RateShopBroker, TMSException"

    Please help me.

  • I tried to follow the whole process but when I clicked on the Rate Shop I was getting this error message below

    " Error RateShopBroker, TMSException"

    Please help me.

  • Hi PaweI,

    I hope u will understand my issue . i checked transportation error log and find the following error log,

    Exception at level 0

    Message: Error RateShopBroker, TMSException

    Source: Microsoft.Dynamics.Ax.Tms

    Stack trace:   at Microsoft.Dynamics.Ax.Tms.API.TmsService.ExecuteTransaction(String requestXml)

    Exception at level 1

    Message: Unable to cast object of type 'CustomTMSEngines.BingMapsMileageEngine' to type 'Microsoft.Dynamics.Ax.Tms.Bll.IRateEngine'.

    Thanks in advance.

  • Hi,

    I have the same error.

    Error RateShopBroker, TMSException

  • Hi Prabhu,

    Your message:

    Unable to cast object of type 'CustomTMSEngines.BingMapsMileageEngine' to type 'Microsoft.Dynamics.Ax.Tms.Bll.IRateEngine'.

    suggests that you have done a wrong setup in AX. It looks like you registered a Rating Engine based on 'CustomTMSEngines.BingMapsMileageEngine'.

    'CustomTMSEngines.BingMapsMileageEngine' is a mileage engine, which you set up in:

    Transportation management -> Setup -> Engines -> Mileage engine

    The responsibility of mileage engine is to answer: "how many miles there is from point A to point B?".

    Your actual Rate engine, which you setup here:

    Transportation management -> Setup -> Engines -> Rate engine

    Should remain Microsoft.Dynamics.Ax.Tms.Bll.MileageRateEngine. You should only change the Parameters of this engine (as on the step 5 of ""Enablement and testing" in blog above) to assign the BingMaps engine to MileageEngineCode. This change will only ensure that your mileage-based rating engine will use a Bing mileage engine for retrieving rates.

    I hope it helps.


Page 1 of 3 (32 items) 123
Leave a Comment
  • Please add 3 and 7 and type the answer here:
  • Post