2016-08-03 32 views
2

Ich verwende LINQ to DocumentDB und ich bin derzeit bemüht, die zurückgegebenen Datensätze durch ein Datetime-Feld zu filtern. Meine Frage lautet wie folgt:Linq to DocumentDB: Filtern nach Datetime-Feld

 var mycollection = Client.CreateDocumentQuery<Test>(MyCollection.DocumentsLink) 
         .Where(d => d.Time >= DateTime.Now) 
         .AsEnumerable(); 

jedoch diese Abfrage stürzt immer mit der Meldung:

ExceptionMessage=The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'. 
ExceptionType=System.InvalidOperationException 
ExceptionMessage=Constant of type 'System.DateTime' is not supported. 
ExceptionType=Microsoft.Azure.Documents.Linq.DocumentQueryException 

Dieser Fehler tritt auf, weil ich das Filtern eines Datetime-Feld verwendet wird. Wenn ich nach einem beliebigen String-Feld filtern sollte, funktioniert das perfekt. Wie kann ich diesen Fehler verhindern?

+1

hast du das gelesen? https://azure.microsoft.com/en-us/blog/working-with-dates-in-azure-documentdb-4/ –

+0

+1 zu diesem writeup. Es verwendet ISO-8601-Zeichenfolgen, was meine Empfehlung zum Speichern von Datum-Uhrzeit-Daten in DocumentDB ist. –

Antwort

1

Die DateTime ist keine unterstützte Zeit, müssen Sie die DateTime in einen int konvertieren, so dass Sie JSON sieht wie folgt aus (zum Beispiel):

{ 
    "Time": 1408318702 
} 

Sie benötigen einen JsonConverter zu verwenden und behandeln Ihre DateTime Eigenschaften als Epoche. Dieser Code ist von this source here geliehen.

public class FooBar 
{ 
    [JsonConverter(typeof(EpochDateTimeConverter))] 
    public DateTime Time { get; set; } 
} 


public class EpochDateTimeConverter : JsonConverter 
{ 
    public override void WriteJson(JsonWriter writer, 
            object value, 
            JsonSerializer serializer) 
    { 
     int seconds; 
     if (value is DateTime) 
     { 
      DateTime dt = (DateTime)value; 
      if (!dt.Equals(DateTime.MinValue)) 
       seconds = dt.ToEpoch(); 
      else 
       seconds = int.MinValue; 
     } 
     else 
     { 
      throw new Exception("Expected date object value."); 
     } 

     writer.WriteValue(seconds); 
    } 

    public override object ReadJson(JsonReader reader, 
            Type type, 
            object value, 
            JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.None || reader.TokenType == JsonToken.Null) 
      return null; 

     if (reader.TokenType != JsonToken.Integer) 
     { 
      throw new Exception(
       string.Format("Unexpected token parsing date. Expected Integer, got {0}.", 
           reader.TokenType)); 
     } 

     int seconds = (int)reader.Value; 
     return new DateTime(1970, 1, 1).AddSeconds(seconds); 
    } 
} 

Und die .ToEpoch Erweiterungsmethode ist wie folgt definiert:

public static class Extensions 
{ 
    public static int ToEpoch(this DateTime date) 
    { 
     if (date == null) return int.MinValue; 
     DateTime epoch = new DateTime(1970, 1, 1); 
     TimeSpan epochTimeSpan = date - epoch; 
     return (int)epochTimeSpan.TotalSeconds; 
    } 
} 
+0

Danke für den Vorschlag, aber ich bekomme immer noch den gleichen Fehler. – Crista23

+0

Ich sehe das Problem, ich habe meine Antwort aktualisiert. –

+0

Alternativ können Sie ISO-8601-Strings verwenden. Ich denke, dass DateTime-Objekte standardmäßig auf diese Weise serialisiert werden (obwohl ich nicht glaube, dass sie korrekt de-serialisiert sind). Sie müssten dann einen String-Bereichsindex auf das Feld mit -1 Genauigkeit setzen. Das Schöne an diesem Ansatz ist, dass sie in der Datenbank lesbar sind. Einige argumentieren, dass es weniger effizient ist, aber ich habe in meinen Experimenten keinen Leistungseinfluss gesehen. –

0

ein DateTime- Objekt in DocumentDB ISO 8601 String-Format standardmäßig zum Beispiel serialisiert wird: „2016-08-08T22: 10: 50.000000Z "

Sie können in diesem Fall die SQL-Syntax anstelle von LINQ verwenden, da das DateTime-Objekt nicht unterstützt wird.

Beachten Sie, dass Sie entweder EnableScanInQuery in der Abfrage als "faceOptions" auf "true" setzen können oder einen Bereichsindex im Feld "Time" mit der Genauigkeit -1 festlegen können.

Sie können das Datum als Epoche speichern, wie David erwähnt, aber Sie müssen nicht. Hier

ist, was Ihre Abfrage aussehen würde:

String current = DateTime.UtcNow.ToString ("o");

var myQuery = Client.CreateDocumentQuery (MyCollection.DocumentsLink, "SELECT * FROM C WHERE c.time> = '" + + currenttime "'", New FeedOptions {EnableScanInQuery = true}) .AsEnumerable();