Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Crashing WCF 3.5 JSON Services with DateTime.MinValue


:P
On this page:

Ah, here's another fun one. I've been working on cleaning up some of the DevConnections Demos from last year with jQuery 1.2.6 and the release version of jQuery.ui 1.5 as I had previously been using beta versions and in between versions of jQuery that came with the UI beta.

One of my demos has been an interop Stock Portfolio example that shows stock portfolios as well as stock quote lookups. These demos actually worked fine before when I used them for the conference and testing in various scenarios, but when I ran through them again today I noticed that quite frequently the application would lock up. One of the things the sample does is do stock symbol lookups so if you type in a letter it tries to immediately retrieve the symbol's company and display it - requests can be rapid and so it can get very difficult to debug.

After a lot of trial and error I traced this back to the case when an invalid stock quote was requested and either a null reference or an empty object was returned.  Whenever this happened the server simply hangs up and is not returning a response. No error, no message, but requests just hanging. Playing around with this a little more I found two issues which apparently have their root in WCF 3.5 JSON HTTP behaviors that are, uhm less than optimal.

Null Results

The first issue I ran into had to do with returning null objects from method calls. I was getting stock quotes and if a quote couldn't be found returning null values. However, I figured out that WCF returned the returned null value to my jQuery client as an empty string or no value at all (ie. 0 bytes).

This turns out to be a behavior problem in WCF when using Bare messages when communicating with the server:

[OperationContract]          
[WebInvoke(Method="POST",
           BodyStyle=WebMessageBodyStyle.Bare,
           ResponseFormat=WebMessageFormat.Json
 )]
StockQuote GetStockQuote(string symbol);

If you use WebMessageBodyStyle.Bare and return null apparently WCF doesn't serialize the value but instead returns no data. It doesn't throw or cause an error as it does on hard errors, but it also returns no data. This issue is isolated to WebMessageBodyStyle.Bare, so if you're returning data with the default WebMessageBodyStyle.Wrapped (MS AJAX style) null is properly returned as aJSON encoded null string, but you also get the extra object wrapping on output and requirements to pass parameters as a named object map for each parameter.

The empty value bonked my client side JSON parser because no data is not a valid JSON value and so the parser (a slightly modified JSON2.js) failed. This is no big deal and easy to fix, but it still sucks royally - if I return null I'd expect WCF to serialize a null properly back as JSON regardless of whether it thinks a REST style result for null should be no data. That's what I specify the data type for and JSON obviously has a null value representation.

This was  minor annoyance and easily fixed, although also a little tricky to catch.

As I've mentioned in various posts and lectures  using Bare JSON messaging just has too many inconsistent behaviors like this null representation, the inability to capture errors on the client effectively (they  basically throw HTML Yellow screen of Death errors) and the difficulty in dialing in the messaging for multiple format options just make them a bad choice. The REST based JSON interfaces need serious fixes before this stuff is ready for production usage, IMHO.

For AJAX scenarios it's simply easier to go with the MS AJAX style messaging that wraps messages regardless whether you use the Microsoft Service Proxies or another tool like jQuery. If you use the latter you can find more info on doing so in a previous post. Using that mechanism it's easy to switch between bare and wrapped modes from the client code, but in my own apps I've just gone down the route of the slightly more wordy 'wrapped' syntax because Microsoft properly handles the RPC method call scenarios and error handling which is otherwise impossibly lame. Bare sounds interesting and easier but in the end without consistency it's not the right call for me.

Date Serialization Problems

The next problem is much more insidious because it hangs ASP.NET solid. After fixing the null issue  I realized that requests were still locking up the ASP.NET application when hitting the GetStockQuote() method above, but again only in situations when an invalid quote was returned and even then not every time. GetStockQuote actually would never return null, but rather return an 'empty' StockQuote   object that includes a message that can be displayed (ie. symbol not found with the symbol name).

However, whenever this happened no result went back to the client and the request would hang.

It turns out that WCF doesn't deal very well with small dates while serializing service method result data. Specifically, I've run into a nasty problem with returning DateTime.MinValue. If you want to lock up an ASP.NET application that services WCF data data via JSON create a method like the following:

[ServiceContract(Name="StockService",Namespace="JsonStockService")]    
 public interface IJsonStockService
 {
 
    [OperationContract]
    [WebInvoke(Method = "POST",
                BodyStyle = WebMessageBodyStyle.Wrapped,
                ResponseFormat = WebMessageFormat.Json
      )]
    DateTime GetDate();
 }

and implement the method like this in the implementation class:

public DateTime GetDate()
{
    return DateTime.MinValue;
}

When you do this you'll find that the service call doesn't just fail, it actually locks up the entire application if the failures occur a few times in succession. The following trace in FireBug demonstrates what this looks like:

FailedRequests

The first to XHR hits are successful hits against another request, then the two test hits against the GetDate() method with the MinValue date result. Note that the result byte count is ? - it's basically a request that's not returning and instead timing out. Note also that the final AJAX call - calling the GetStockQuote method again - also fails. Opening another browser and trying to access any ASP.NET page also fails although this doesn't immediately occur. Bonk - the ASP.NET application is locked.

My first thought here was that WCF fails serialization of a date smaller than 1-1-1970, because this is the base date that JavaScript uses for any date math. That date is the 0 milliseconds and all other dates are calculated by adding milliseconds to it. I tried the following return just for kicks:

public DateTime GetDate()
{
    return new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    //return DateTime.MinValue;
}

and that actually works, but switching back to returning DateTime.MinValue again fails immediately and hangs the app.

What's odd is that if leave the app sitting for a while, eventually - after 3 minutes or so - the ASP.NET app starts responding again and any stacked up requests complete.

[updated]

It looks like the problem lies with the DataContractJsonSerializer which also bombs on DateTime.MinValue as the following example demonstrates:

protected void Page_Load(object sender, EventArgs e)
{
    DateTime value = new DateTime(2, 1, 1);
    string json = this.ToJson(value);
    Response.Write("<pre>" + Server.HtmlEncode(json) + "</pre>");
}
 
 
public string ToJson(object value)
{
    DataContractJsonSerializer ser = new DataContractJsonSerializer(value.GetType());
    MemoryStream ms = new MemoryStream();
    ser.WriteObject(ms, value);
    return = Encoding.Default.GetString(ms.ToArray());
}

which also fails with:

Specified argument was out of the range of valid values.
Parameter name: value

in the call to WriteObject. It's interesting to experiment with the dates here. MinValue fails, but:

DateTime value = new DateTime(1, 1, 1, 0, 0, 0,DateTimeKind.Utc); 

works. Remove the UTC flag and it also fails.

Nasty little serialization bug that.

Workaround?

The workaround is ugly - basically set the date and time to an explicit value on any defaulted values or members that make it to the client. I tend to use a static date value that represents the JavaScript 0 date:

public static DateTime DAT_BASEDATE = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

which I can use as a sort of custom MinValue date:

[DataMember]
public DateTime LastQuoteTime
{
    get { return _LastQuoteTime; }
    set { _LastQuoteTime = value; }
}
private DateTime _LastQuoteTime = DAT_BASEDATE; // DateTime.MinValue;

and that works just fine. Alternately if you run into this issue and you don't want to muck around with the base type you can also do this right before you return a result from a service implementation method:

public StockQuote GetQuote(string symbol)
{
    StockQuote quote = new StockQuote();
    quote.LastQuoteTime = StockQuote.DAT_BASEDATE;
    return quote;
}

Ugly I know but it works. Fun ain't it?

Posted in WCF  JavaScript  jQuery  

The Voices of Reason


 

Travis Illig
July 11, 2008

# re: Crashing WCF 3.5 JSON Services with DateTime.MinValue

Is this just a problem in ASP.NET? I tried your code out in a console app and it doesn't fail.

This code...

try
{
  DateTime value = new DateTime(2, 1, 1);
  DataContractJsonSerializer ser = new DataContractJsonSerializer(value.GetType());
  MemoryStream ms = new MemoryStream();
  ser.WriteObject(ms, value);
  Console.WriteLine("SUCCESS: {0}", System.Text.Encoding.Default.GetString(ms.ToArray()));
}
catch(Exception err)
{
  Console.WriteLine("ERROR: {0}", err);
}


...presents this on the console:

SUCCESS: "\/Date(-62104032000000-0800)\/"


It also seems to succeed on DateTime.MinValue:

SUCCESS: "\/Date(-62135568000000-0800)\/"

Paul S
April 02, 2009

# re: Crashing WCF 3.5 JSON Services with DateTime.MinValue

I have only just had to build a service using WCF, and with a short deadline - this almost broke me! I could not for the life of me figure out why my POCO was not serializing.

Wrote a couple of methods using reflection that might be useful to someone - have not gone the extra step of cached dynamic methods to save cycles but as follows:

/// <summary>
/// Prepares list of objects for json serialization
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <returns></returns>
private void PrepareListForSerialization<T>(ref List<T> list)
{
    if (list.Count > 0)
    {
    for (int i = 0; i < list.Count; i++)
    {
        T obj = list[i];
        PrepareObjectForSerialization<T>(ref obj);
    }
    }
}
/// <summary>
/// Prepares object for json serialization
/// </summary>
/// <param name="obj"></param>
private void PrepareObjectForSerialization<T>(ref T obj)
{
    Type type = typeof(T);
    PropertyInfo[] props = type.GetProperties();
    foreach (PropertyInfo prop in props)
    {
    if (prop.PropertyType == typeof(DateTime))
        prop.SetValue(obj, Convert.ToDateTime(prop.GetValue(obj, null)).ToUniversalTime(), null);
    }
    FieldInfo[] fields = type.GetFields();
    foreach (FieldInfo field in fields)
    {
    if (field.FieldType == typeof(DateTime))
        field.SetValue(obj, Convert.ToDateTime(field.GetValue(obj)).ToUniversalTime());
    }
}

Paul S
April 02, 2009

# re: Crashing WCF 3.5 JSON Services with DateTime.MinValue

I have only just had to build a service using WCF, and with a short deadline - this almost broke me! I could not for the life of me figure out why my POCO was not serializing.

Wrote a couple of methods using reflection that might be useful to someone - have not gone the extra step of cached dynamic methods to save cycles but as follows:

/// <summary>
/// Prepares list of objects for json serialization
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <returns></returns>
private void PrepareListForSerialization<T>(ref List<T> list)
{
    if (list.Count > 0)
    {
    for (int i = 0; i < list.Count; i++)
    {
        T obj = list[i];
        PrepareObjectForSerialization<T>(ref obj);
    }
    }
}
/// <summary>
/// Prepares object for json serialization
/// </summary>
/// <param name="obj"></param>
private void PrepareObjectForSerialization<T>(ref T obj)
{
    Type type = typeof(T);
    PropertyInfo[] props = type.GetProperties();
    foreach (PropertyInfo prop in props)
    {
    if (prop.PropertyType == typeof(DateTime))
        prop.SetValue(obj, Convert.ToDateTime(prop.GetValue(obj, null)).ToUniversalTime(), null);
    }
    FieldInfo[] fields = type.GetFields();
    foreach (FieldInfo field in fields)
    {
    if (field.FieldType == typeof(DateTime))
        field.SetValue(obj, Convert.ToDateTime(field.GetValue(obj)).ToUniversalTime());
    }
}

Arun Kayamkulam
November 24, 2009

# re: Crashing WCF 3.5 JSON Services with DateTime.MinValue

Thanks

Convert DateTime.MinValue to utc format and try again

eg.
DateTime dateMinValue = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);

This is working fine.

Matt Sugden
May 21, 2010

# re: Crashing WCF 3.5 JSON Services with DateTime.MinValue

Another solution is to use a regular expression to convert the serialized date field into a readable format.

private string FixJsonDateFields(string jsonStr)
{
Regex regEx = new Regex(":(\"\\\\/)Date(.*?)(\\\\/\")", RegexOptions.Multiline);
if (regEx.IsMatch(jsonStr))
jsonStr = regEx.Replace(jsonStr, ":new Date$2");

return jsonStr;
}

This will convert a date string "DateField":"\/Date(-62135596800000+0000)\/" to "DateField":new Date(-59011459200000).

I've had major issues getting my web front end to read dates using the .net serializer, after moving from the newtonsoft one, and this works fine.

Hope you find it useful.

George Tang
February 17, 2011

# re: Crashing WCF 3.5 JSON Services with DateTime.MinValue

This doesn't only happen for MinValue, it also happens on MaxValue. What happens is that DataContractJsonSerializer will first convert all DateTime to UTC before serializing. So it depends on what time zone you're in. If you're in the UTC - x hours timezones, MinValue will be just fine, but MaxValue will bomb as the serializer tries to add x hours to MaxValue. If you're in the UTC + x hours timezones, then MaxValue will be fine and MinValue will bomb while it tries to subtract x hours from MinValue. Of course the solutions above by converting the MinValue to UTC, then whenever you need to test if the datetime has MinValue, you'll need to test for MinValue.ToUniversalTime() as well since you won't know if it's set for UTC or not. Thus when you need to check for MinValue, you'll need to have _myDatetime == DateTime.MinValue || _myDatetime == DateTime.MinValue.ToUniversalTime(). This is a major pain for me, since most of my code don't have to deal with timezone/UTC since the clients will always only care about the local date/time, and not for another timezone.

Andrea Mazzarri
September 05, 2012

# re: Crashing WCF 3.5 JSON Services with DateTime.MinValue

Sorry...previous version flawed by a bug....this should work better:
 using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.Diagnostics;
    using System.Runtime.Serialization.Json;
    using System.IO;
 
    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public static class JsonSerializerUtil
    {
        /// <summary>
        /// Prepares list of objects for json serialization
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <returns></returns>
        public static void PrepareListForSerialization<T>(ref List<T> list)
        {
            if (list.Count > 0)
            {
                for (int i = 0; i < list.Count; i++)
                {
                    T obj = list[i];
                    PrepareObjectForSerialization<T>(ref obj);
                }
            }
        }
 
        /// <summary>
        /// Prepares object for json serialization
        /// </summary>
        /// <param name="obj"></param>
        public static void PrepareObjectForSerialization<T>(ref T obj)
        {
            List<Tuple<object, object>> dt = new System.Collections.Generic.List<System.Tuple<object, object>>();
 
            GetDateTimeProps(ref dt, obj, obj.GetType().AssemblyQualifiedName);
 
            GetDateTimeFieldInfos(ref dt, obj, obj.GetType().AssemblyQualifiedName);
 
            foreach (var d in dt)
            {
                var prop = d.Item1 as PropertyInfo;
 
                if (null != prop)
                {
                    DateTime val = Convert.ToDateTime(prop.GetValue(d.Item2, null));
 
                    if (prop.CanWrite)
                    {
                        prop.SetValue(d.Item2, val.ToUniversalTime(), null);
                    }
                }
 
                var field = d.Item1 as FieldInfo;
 
                if (null != field)
                {
                    DateTime val = Convert.ToDateTime(field.GetValue(d.Item2));
 
                    field.SetValue(d.Item2, val.ToUniversalTime());                    
                }
            }            
        }
 
        private static void GetDateTimeFieldInfos<T>(ref List<Tuple<object, object>> dt, T obj, string typeName)
        {
            var type = Type.GetType(typeName);
 
            FieldInfo[] fields = type.GetFields();
 
            foreach (FieldInfo field in fields)
            {
                Debug.WriteLine(field.Name);
 
                if (field.FieldType == typeof(DateTime))
                {
                    dt.Add(new System.Tuple<object, object>(field, obj));
 
                    Debug.WriteLine(field.Name + " is DateTime");
                }
 
                object myPropertyAVal = field.GetValue(obj);
 
                if (null != myPropertyAVal)
                {
                    Type t = myPropertyAVal.GetType();
                    //Type t = prop.PropertyType;
 
                    bool isPrimitiveType = t.IsPrimitive || t.IsValueType || (t == typeof(string));
 
                    if (isPrimitiveType)
                    {
                    }
                    else
                    {
                        //Recurse on subproperties
                        GetDateTimeFieldInfos(ref dt, field.GetValue(obj), t.AssemblyQualifiedName);
                    }
                }
            }
        }
 
        public static void GetDateTimeProps<T>(ref List<Tuple<object, object>> dt, T obj, string typeName)
        {
            var type = Type.GetType(typeName);
 
            PropertyInfo[] props = type.GetProperties();
 
            foreach (PropertyInfo prop in props)
            {
                Debug.WriteLine(prop.Name);
 
                if (prop.PropertyType == typeof(DateTime))
                {
                    dt.Add(new System.Tuple<object, object>(prop, obj));
 
                    Debug.WriteLine(prop.Name + " is DateTime");
                }
 
                if (prop.GetIndexParameters().Length == 0)
                {
                    object myPropertyAVal = prop.GetValue(obj, null);
 
                    if (null != myPropertyAVal)
                    {
                        Type t = myPropertyAVal.GetType();
                        //Type t = prop.PropertyType;
 
                        bool isPrimitiveType = t.IsPrimitive || t.IsValueType || (t == typeof(string));
 
                        if (isPrimitiveType)
                        {
                        }
                        else
                        {
                            //Recurse on subproperties
                            GetDateTimeProps(ref dt, prop.GetValue(obj, null), t.AssemblyQualifiedName);
                        }
                    }
                }
                else
                {
                    // get an integer count value, by incrementing a counter until the exception is thrown
                    int count = 0;
                    while (true)
                    {
                        try
                        {
                            prop.GetValue(obj, new object[] { count });
                            count++;
                        }
                        catch (TargetInvocationException) { break; }
                        catch (ArgumentOutOfRangeException) { break; }
                    }
 
                    for (int i = 0; i < count; i++)
                    {
                        object myPropertyAVal = prop.GetValue(obj, new object[] { i });
                        Type t = myPropertyAVal.GetType();
                        //Type t = prop.PropertyType;
 
                        //recurse on inner items of the property
                        GetDateTimeProps(ref dt, prop.GetValue(obj, new object[] { i }), t.AssemblyQualifiedName);
                    }
                }
            }
        }
    }

Naveen
April 18, 2013

# re: Crashing WCF 3.5 JSON Services with DateTime.MinValue

Thanks for the article. It helped me a lot. My application was locking itself as you said in the article. I couldn't realize that, it is DateTime data member which caused the issue unless I read this article.

Alexander
May 31, 2014

# re: Crashing WCF 3.5 JSON Services with DateTime.MinValue

for DateTime try to use
[DataMember(EmitDefaultValue = false)]

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024