Dynamic in .NET 4.0 and the FedEx Tracking Service Response Problem

Imagine you are writing an ASP.NET MVC3 application which requires FedEx shipping integration. The tracking service that FedEx provides has some useful properties and classes, but let's say the issue involves a property called ActualDeliveryTime, of type DateTime. As the service reference class, Reference.cs, has been created with this DateTime property, the underlying SOAP serialization logic will convert the incoming date and time string to a DateTime object, respecting any GMT offset presented with a UTC date/time string.

What if that GMT offset was wrong? Further, what if the date and time the package was delivered is not, in fact, what the FedEx tracking service returns in the ActualDeliveryTime property. What if a simple web track for the package in question, on fedex.com, confirms this? There are perfectly reasonable ways to control the deserialization of SOAP XML in a WCF client, most of which involve changing the svcutil (or slsvcutil) output class to derive from a new base class you have created which derives from ClientBase<T>.

This particular solution (not the ClientBase<T> inheritance one) involves the use of the dynamic keyword to have the incoming value be represented by one type, and the outgoing value a be represented by a different type.

 

// Consumer classes of dynamic properties require this using:
//
using Microsoft.CSharp.RuntimeBinder;



// NOTE: The ActualDeliveryTimestamp property was made dynamic to resolve a FedEx GMT offset issue
// with this UTC DateTime value. Should you choose to regenerate this class, this property must be
// added back as it appears in the existing TrackService class today.
//
private dynamic actualDeliveryTimestampDttmField;



/// <summary>
/// Sets the actual delivery date and time as a string, but returns a DateTime irrespective of the GMT offset, like what we have observed was the behavior of the fedex.com tracking page.
/// This dynamic property is custom to this class and must be recreated if the reference is updated.
/// </summary>

public dynamic ActualDeliveryTimestamp {

    get
    {
        DateTime dtmActualDeliveryDttm = DateTime.MinValue;
        string strActualDeliveryDttm = this.actualDeliveryTimestampDttmField as String;

        if (!String.IsNullOrEmpty(strActualDeliveryDttm))
        {

            System.Text.RegularExpressions.Regex gmtOffsetRegex = new System.Text.RegularExpressions.Regex(@"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[\-\+]\d{2}:\d{2}");

            if (gmtOffsetRegex.IsMatch(strActualDeliveryDttm))
            {

            string parsedActualDeliveryDttm = strActualDeliveryDttm.Substring(0, (strActualDeliveryDttm.Length - 6));

            DateTime.TryParse(parsedActualDeliveryDttm, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.RoundtripKind, out dtmActualDeliveryDttm);

            }
        }
        else
        {
            DateTime.TryParse(strActualDeliveryDttm, out dtmActualDeliveryDttm);
        }

        return dtmActualDeliveryDttm;

    }

    set
    {
        System.Xml.XmlNode[] xActualDeliveryTimestampField = value as System.Xml.XmlNode[];
        this.actualDeliveryTimestampDttmField = xActualDeliveryTimestampField[0].InnerText;
    }

}