سؤال

I am trying to use NodaTime in my app. The app persists data in a mongodb database. Consider the following class

public class SomeType
{
    public ObjectId Id { get; set; }
    public Instant Instant { get; set; }
    [BsonDateTimeOptions(Kind = DateTimeKind.Local)]
    public DateTime DateTime { get; set; }
    [BsonDateTimeOptions(Kind = DateTimeKind.Utc)]
    public DateTime DateTimeUtc { get; set; }
//    public ZonedDateTime ZonedDateTime { get; set; }
//    public LocalDateTime LocalDateTime { get; set; }
}

Without adding a custom serializer, the Instant property of the class doesn't get stored in the db. Reading the document from the db also fails.

public class InstantBsonSerializer : BsonBaseSerializer
{
    public override object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options)
    {
        var ticks = bsonReader.ReadInt64();
        return new Instant(ticks);
    }

    public override object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options)
    {
        var ticks = bsonReader.ReadInt64();
        return new Instant(ticks);
    }

    public override void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
    {
        var instant = (Instant) value;
        bsonWriter.WriteInt64(instant.Ticks);
    }
}

I created the above serializer and registered it properly. I am now able to save and retrieve instances of my class with the proper value set for Instant.

My question is how does the C# driver handle searching using linq?

var list = mongoDatabase.GetCollection<SomeType>("SomeType")
                     .AsQueryable<SomeType>()
                     .Where(x => x.Instant < Instant.FromDateTimeUtc(dateTime.ToUniversalTime()))
                     .ToList();
Console.WriteLine(list.Count);

I get the correct list of documents. I am trying to understand HOW mongodb gets this data. Does it first load ALL data, deserialize it properly and then filter? Or does it serialize the Instant value of the where clause and use the serialized value to filter all documents, load matching ones and then deserialize?

I tried seeing the query logged by the mongodb profiler, but its doesnt seem to log anything. I have set the profiling level to 2.

In the case of Instant the serialized value is a long. But in case of ZonedDateTime, the serialized value will be a document with two properties. How will searching work in that case?

Edit:

I was able to get the profiling to work. The following c# query,

mongoDatabase.GetCollection<SomeType>("SomeTypeCollection")
             .AsQueryable<SomeType>()
             .Where(x => x.Instant < Instant.FromDateTimeUtc(DateTime.UtcNow))
             .ToList();

results in the following mongodb query

{ "Instant": { $lt: 13781017828460782 }} 

Meaning, the c# driver serializes my Instant object and uses the serialized value to filter the results in the db itself.

هل كانت مفيدة؟

المحلول

The driver will convert you linq query into a mongodb query and filter results before deserialising them. The query will not run until you call ToList()

Here are the operators that are supported: http://docs.mongodb.org/ecosystem/tutorial/use-linq-queries-with-csharp-driver/

The custom deserialiser will not come into play until the driver starts building the object graph from the data returned.

You wouldn't normally need a custom serialiser for classes with simple types or where you're not doing extra work when reading/writing the docs.

If you wanted to control what was serialised, ie if essentially your class holds a single datetime, why not make it so that the UTC datetime stamp is stored and use BsonIgnore for the other properties.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top