Search code examples
c#.netmongodbmongodb-.net-driver

Reading null into a nullable int with MongoDB C# driver


I have data in my MongoDB where I am storing a nullable int, most often as null. My model on the C# side has the variable as int? MaterialID.

When I run .Query.FirstOrDefault(), I get an error "Input string was not in a correct format." whenever it tries to read the null value from Mongo (ints work fine).

I managed to get around this by changing my int? to a string and then later casting that back to an int?, but that's a horrible hack that I really don't like. Anyone have any ideas as to how I can read null from Mongo into my int? on the C# side?

Thanks.

My JSON:

 "Collection" : {
        "List" : [ 
            {
                "ID" : "40",
                "StartDate" : ISODate("2013-01-01T00:00:00.000Z"),
                "EndDate" : ISODate("2013-12-31T00:00:00.000Z"),
                "MaterialID" : "3"
            }, 
            {
                "ID" : "40",
                "StartDate" : ISODate("2014-01-01T00:00:00.000Z"),
                "EndDate" : ISODate("2014-09-30T00:00:00.000Z"),
                "MaterialID" : "null"
            }
        ]


Solution

  • You are storing MaterialID field as string in database (type 2). Of course when you try to assign string value to nullable integer, you have error. Note: even null is stored as string with text "null". I recommend you to start from fixing data in your database. You can do it from mongo shell:

    > db.collection.find({MaterialID:{$type:2}}).forEach(
        function(doc){ 
           var materialId = (doc.MaterialID == "null") ? null : new NumberInt(doc.MaterialID);
           db.collection.update({_id:doc._id},{$set:{MaterialID: materialId}});
        })
    

    Note: in your json documents don't have _id field, but I assume it's there instead of ID field. Anyway, you can adjust script to your needs.

    After changing field type, your mapping to nullable int will work correctly.


    BTW there is another option in case for some reason you cannot fix data in database. You can use custom BSON serializer which will convert int? to string and vice versa:

    public class NullableIntAsStringSerializer : SerializerBase<int?>
    {
        public override void Serialize(
            BsonSerializationContext context, BsonSerializationArgs args, int? value)
        {
            context.Writer.WriteString(value.HasValue ? value.ToString() : "null");
        }
    
        public override int? Deserialize(
            BsonDeserializationContext context, BsonDeserializationArgs args)
        {
            var str = context.Reader.ReadString();
            return str == "null" ? (int?)null : Int32.Parse(str);
        }
    }
    

    And assign this serializer to your property

    [BsonSerializer(typeof(NullableIntAsStringSerializer))]
    public int? MaterialID { get; set; }