Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting default MongoDb/Bson representation for all decimals to double

I am helping to write a C# application to analyse financial data. Internally all the numbers are stored as decimal, however when we persist them to our mongodb database, we would like them to be stored as doubles, rather than strings. I know I can do this for individual fields by applying the [BsonRepresentation(BsonType.Double)] attribute to each decimal field, but I keep forgetting when adding new decimal values.

Is there any way of making this the default representation for all decimal numbers?

like image 652
Nikki Locke Avatar asked Feb 25 '14 15:02

Nikki Locke


4 Answers

You can see my pull request for a sample of a convention to do this

https://github.com/mongodb/mongo-csharp-driver/pull/175

Here's how you would register it:

var conventions = new ConventionPack
{
    new DecimalRepresentationConvention(BsonType.Double)
};
ConventionRegistry.Register("decimalAsDouble", conventions, t => condition);

For they type condition, people usually check the namespace, if it's just your own types you are serializing, you could just return true to apply it to every type.

See more about conventions here:

http://docs.mongodb.org/ecosystem/tutorial/serialize-documents-with-the-csharp-driver/#conventions

like image 102
Wes Avatar answered Nov 16 '22 15:11

Wes


For anyone coming to this question now, it's worth mentioning that since v3.4, Mongo now natively supports storing Decimal values, which is the best way to serialize C# decimal values since using Double can result in loss of precision, which can be particularly problematic with financial data.

You can find information on how to do this here: How to use decimal type in MongoDB

like image 23
adrian Avatar answered Nov 16 '22 14:11

adrian


Looks like I'm talking to myself, but it might help someone else...

Here is an easier way:

void Main()
{
    BsonSerializer.RegisterSerializationProvider(new MyDecimalSerializer());
    Console.WriteLine(new Test().ToJson(new JsonWriterSettings() { Indent = true }));
}

class MyDecimalSerializer : DecimalSerializer, IBsonSerializationProvider {
    private IBsonSerializationOptions _defaultSerializationOptions = new RepresentationSerializationOptions(BsonType.Double);

    public override void Serialize(
        BsonWriter bsonWriter,
        Type nominalType,
        object value,
        IBsonSerializationOptions options) {
        if(options == null) options = _defaultSerializationOptions;
        base.Serialize(bsonWriter, nominalType, value, options);
    }

    public IBsonSerializer GetSerializer(Type type) {
        return type == typeof(Decimal) ? this : null;
    }
}

Using the same Test class as my other answer above, this works.

For simplicity, I have made my decimal serializer an IBsonSerialisationProvider itself - in the Bson source code, that role is usually taken by a class that maintains a list of types and serializers that can handle them.

like image 1
Nikki Locke Avatar answered Nov 16 '22 14:11

Nikki Locke


Based on @NikkiLocke answer, but working on newer versions of mongo c# driver:

public class BsonDecimalSerializer : DecimalSerializer, IBsonSerializationProvider
{
    public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, decimal value)
    {
        context.Writer.WriteDouble((double)value);
    }
    public override decimal Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
    {
        return (decimal)context.Reader.ReadDouble();
    }
    public IBsonSerializer GetSerializer(Type type)
    {
        return type == typeof(decimal) ? this : null;
    }
}

And the serializer registration:

BsonSerializer.RegisterSerializationProvider(new BsonDecimalSerializer());
like image 1
thepirat000 Avatar answered Nov 16 '22 14:11

thepirat000