8

The following linq to entities query gives the result below:

public class UserCountResult
{
    public DateTime? date { get; set; } // **should this be string instead?**
    public int users { get; set; }
    public int visits { get; set; }
}

public JsonResult getActiveUserCount2(string from = "", string to = "")
{

    var query = from s in db.UserActions
                    group s by EntityFunctions.TruncateTime(s.Date) into g
                    select new UserCountResult
                    {
                        date = g.Key, // can't use .toString("dd.MM.yyyy") here
                        users = g.Select(x => x.User).Distinct().Count(),
                        visits = g.Where(x => x.Category == "online").Select(x => x.Category).Count()
                    };

    return Json(query, JsonRequestBehavior.AllowGet);

}

Result:

[{"date":"\/Date(1383433200000)\/","users":21,"visits":47},{"date":"\/Date(1383519600000)\/","users":91,"visits":236}]

Instead of something like /Date(1383433200000)/, I need the date in format "dd.MM.yyyy", e.g.

[{"date":"29.11.2013","users":21,"visits":47},{"date":"30.11.2013","users":91,"visits":236}]

I found no way as to how to change the format in the query and I'm not sure what to do.. I don't even understand why g.Key is a nullable .. Thanks for any input!

peter
  • 2,103
  • 7
  • 25
  • 51
  • I think you should be able to use `g.Key.GetValueOrDefault()`. – Rob Lyndon Nov 04 '13 at 22:46
  • 1
    you can always use some `SqlFunctions` like `DatePart` to extract each part of the date, then concatenate them together to get the format you want. – King King Nov 04 '13 at 23:00
  • After rescanning your question, I recognized a bit strangeness in your code, the `date` property is `DateTime?`, but you still want to assign it to some string? – King King Nov 04 '13 at 23:09

4 Answers4

12

g.Key is nullable because that's the signature of EntityFunctions.TruncateTime. http://msdn.microsoft.com/en-us/library/dd395596.aspx.

To exit from Linq to Entities, you can leave the query as is, and project it after the fact:

return Json(query.AsEnumerable().Select(r => new 
    {
        date = r.date.GetValueOrDefault().ToString("dd.MM.yyyy"),
        users = r.users,
        visits = r.visits
    }), JsonRequestBehavior.AllowGet);

It's not pretty, but that's Linq to Entities for you.

Rob Lyndon
  • 12,089
  • 5
  • 49
  • 74
  • 1
    Thanks for your help. This gives me "'LINQ to Entities' does not recognize the method 'System.String ToString(System.String)'" in the response text. – peter Nov 04 '13 at 22:54
  • @peter well, `LINQ to Entity` always complains much :), not surprising. – King King Nov 04 '13 at 22:55
  • As King King says, Linq to Entities is temperamental, but there is a way around it. Edit pending. – Rob Lyndon Nov 04 '13 at 23:01
  • 1
    This solution implies changing the type of the date property for the sole purpose of serialization... Not very convenient if you need the class for other purposes. The cleanest way is to change the serializer behavior. – Thomas Levesque Nov 04 '13 at 23:09
  • I adjusted the projection so as to not return a new UserCountResult but just an anonymous type instead ( (r=> new { .. } ), otherwise I'd had to, as Thomas wrote, change the type of the date property. – peter Nov 05 '13 at 09:25
4

Assuming you're using JSON.NET as the JSON serializer, you can apply the JsonConverterAttribute to the date property to specify a custom converter.

[JsonConverter(typeof(MyDateConverter))]
public DateTime? date { get; set; }

You can use the DateTimeConverterBase class as a base class for your converter.

Here's a possible implementation for MyDateConverter:

class CustomDateTimeConverter : DateTimeConverterBase
{
    private const string Format = "dd.MM.yyyy";

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        DateTime d = (DateTime)value;
        string s = d.ToString(Format);
        writer.WriteValue(s);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (s == null)
            return null;
        string s = (string)reader.Value;
        return DateTime.ParseExact(s, Format, null);
    }
}

Another option is to exclude the date property from the serialization (using the JsonIgnoreAttribute), and add another property of type String that converts to and from the desired format. Here's an implementation of this solution:

public class UserCountResult
{
    [JsonIgnore]
    public DateTime? date { get; set; }
    [JsonProperty("date")]
    public string DateAsString
    {
        get
        {
            return date != null ? date.Value.ToString("dd.MM.yyyy") : null;
        }
        set
        {
            date = string.IsNullOrEmpty(value) ? default(DateTime?) : DateTime.ParseExact(value, "dd.MM.yyyy", null);
        }
    }

    public int users { get; set; }
    public int visits { get; set; }
}
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • @elgonzo, no, it's an ASP.NET MVC type. But IIRC, ASP.NET MVC uses JSON.NET as its serializer (or at least, it can be configured to use it) – Thomas Levesque Nov 04 '13 at 22:52
  • Thank you very much for your detailed answer, for now this seems overpowered for my little purpose but I'll read into the topics – peter Nov 04 '13 at 23:45
3

If this is using SQL Server, you can concat the result of SQL functions

using System.Data.Entity.SqlServer;    

...

date = SqlFunctions.DatePart("year",g.Key)
    +"-"+SqlFunctions.DatePart("month",g.Key)
    +"-"+SqlFunctions.DatePart("day",g.Key)
Carter Medlin
  • 11,857
  • 5
  • 62
  • 68
2

Something like this should work:

date = new Date(parseInt(g.Key.substr(6)));

The substr will pull off the "/Date(" string, parseInt will pull just the integer and Date will give you a new date object.

EDIT:

Just found this SO question that supports this answer.

How do I format a Microsoft JSON date?

Community
  • 1
  • 1
crthompson
  • 15,653
  • 6
  • 58
  • 80
  • Thanks for the link, I'd rather have the data returned from the controller in the format I desire, though. – peter Nov 04 '13 at 23:00
  • @peter A whole lot of good information over at [Scott Hanselman's blog](http://www.hanselman.com/blog/OnTheNightmareThatIsJSONDatesPlusJSONNETAndASPNETWebAPI.aspx) – crthompson Nov 04 '13 at 23:03