[CSHARP-252] unsigned integer values support Created: 15/Jun/11  Updated: 02/Apr/15  Resolved: 16/Jun/11

Status: Closed
Project: C# Driver
Component/s: None
Affects Version/s: 1.0
Fix Version/s: 1.0

Type: New Feature Priority: Major - P3
Reporter: Jean-Sebastien Bourdon Assignee: Robert Stam
Resolution: Done Votes: 3
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

.NET



 Description   

Support for unsigned integer values would be very important in many cases. As far as I can see, this is currently not supported.



 Comments   
Comment by Yves Merlicco [X] [ 28/Mar/14 ]

Based on Robert Sam solution, here is an extended solution :

  • Compatible with the latest version of the C# driver
  • Targeting both UInt32 and UInt64 types (UInt16 don't need to have overflow activated)
  • Able to target Nullable types (Nullable<UInt32> and Nullable<UInt64>)

The convention class :

public class UnsignedNumericOverflowConvention : IMemberMapConvention
{
  public string Name
  {
    get { return "Overflow for UInt32 and UInt64"; }
  }
 
  public void Apply(BsonMemberMap memberMap)
  {
    var memberType = memberMap.MemberType;
 
    var type = Nullable.GetUnderlyingType(memberType) ?? memberType;
    if (type == typeof (UInt32))
    {
      memberMap.SetSerializationOptions(
        GetOverflowSerializationOptions(BsonType.Int32));
    }
    else if (type == typeof (UInt64))
    {
      memberMap.SetSerializationOptions(
        GetOverflowSerializationOptions(BsonType.Int64));
    }
  }
 
  private static RepresentationSerializationOptions GetOverflowSerializationOptions(BsonType bsonType)
  {
    return new RepresentationSerializationOptions(bsonType)
    {
      AllowOverflow = true
    };
  }
}

And to register your convention (within a convention pack) :

ConventionRegistry.Register(
  "Name",
  new ConventionPack {new UnsignedNumericOverflowConvention()},
  t => true);

Possibly using a more selective filter than t => true if necessary.

Hovewer I found a limitation with the overflow solution : If you set an UInt64 to UInt64.MaxValue (9,223,372,036,854,775,807), the value in MongoDB will be -1. MongoDB have a signed 64-bit integer and cannot handle integers > Int64.MaxValue (and UInt64.MaxValue is > Int64.MaxValue).

I ran some tests and this is how the Driver (or MongoDB?) handle the 64-bit integer overflow:

  • UInt64.MaxValue (9223372036854775807) become NumberLong(-1)
  • 9223372036854775808 become NumberLong("-9223372036854775808")
  • 9223372036854775809 become NumberLong("-9223372036854775807")
  • etc.
Comment by Robert Stam [ 22/Mar/12 ]

There didn't used to be a way to do this globally, but now there is, using the new SerializationOptionsConvention. You could write a convention to always allow overflow on uint members like this:

public class AlwaysAllowUInt32OverflowConvention : ISerializationOptionsConvention
{
    public IBsonSerializationOptions GetSerializationOptions(MemberInfo memberInfo)
    {
        Type type = null;
        var fieldInfo = memberInfo as FieldInfo;
        if (fieldInfo != null)
        {
            type = fieldInfo.FieldType;
        }
        var propertyInfo = memberInfo as PropertyInfo;
        if (propertyInfo != null)
        {
            type = propertyInfo.PropertyType;
        }
                
 
        if (type == typeof(uint))
        {
            return new RepresentationSerializationOptions(BsonType.Int32) { AllowOverflow = true };
        }
        else
        {
            return null;
        }
    }
}

and you would register the convention like this:

var conventions = new ConventionProfile();
conventions.SetSerializationOptionsConvention(new AlwaysAllowUInt32OverflowConvention());
BsonClassMap.RegisterConventions(conventions, t => true);

possibly using a more selective filter than t => true if necessary.

Comment by Wade Kaple [ 22/Mar/12 ]

Is there a way to configure this globally (i.e., all uint32 members should allow overflow)? Maybe a convention of some sort?

Comment by Robert Stam [ 15/Jun/11 ]

This is a normal exception. The serializer is telling you that the UInt32 value you are trying to serialize is outside the range of valid Int32 values. The exception will only be thrown if the value exceeds the maximum allowable Int32 value.

You can tell the serializer that you want to allow overflow by annotating the class member like this:

public class C

{ public ObjectId Id; [BsonRepresentation(BsonType.Int32, AllowOverflow = true)] public uint U; }

If you do this, numbers that overflow will still round trip without error, but when you look at the document in the mongo shell you will see a different negative number.

You can also tell the serializer that you want to store a UInt32 field as a Int64, in which case there will be no overflow:

public class C

{ public ObjectId Id; [BsonRepresentation(BsonType.Int64)] public uint U; }

But this trick only works for UInt32, not for UInt64.

Comment by Robert Stam [ 15/Jun/11 ]

Thanks for the extra information. That will help in reproducing this.

It will not be possible to add unsigned types to BsonType unless the server is changed to support the new types as well.

What we could do client side is provide some constructors that allow creating BsonInt32 and BsonInt64 values from unsigned values and accessor properties to get the values back out as unsigned integers.

Comment by Dan Le Van [ 15/Jun/11 ]

version 1.0.0.4098

There is an exception when calling Insert to a MongoCollection of an object containing a uint property.
And there are no uint in MongoDB.Bson.BsonType

Arithmetic operation resulted in an overflow.
"System.OverflowException: Arithmetic operation resulted in an overflow.\r\n at MongoDB.Bson.Serialization.Options.RepresentationSerializationOptions.ToInt32(UInt32 value) in C:\\work\\10gen\\mongodb\\mongo-csharp-driver\\Bson\\Serialization\\Options
RepresentationSerializationOptions.cs:line 404\r\n at MongoDB.Bson.Serialization.Serializers.UInt32Serializer.Serialize(BsonWriter bsonWriter, Type nominalType, Object value, IBsonSerializationOptions options) in C:\\work\\10gen\\mongodb\\mongo-csharp-driver\\Bson\\Serialization\\Serializers
NetPrimitiveSerializers.cs:line 1345\r\n at MongoDB.Bson.Serialization.BsonClassMapSerializer.SerializeMember(BsonWriter bsonWriter, Object obj, BsonMemberMap memberMap) in C:\\work\\10gen\\mongodb\\mongo-csharp-driver\\Bson\\Serialization
BsonClassMapSerializer.cs:line 324\r\n at MongoDB.Bson.Serialization.BsonClassMapSerializer.Serialize(BsonWriter bsonWriter, Type nominalType, Object value, IBsonSerializationOptions options) in C:\\work\\10gen\\mongodb\\mongo-csharp-driver\\Bson\\Serialization
BsonClassMapSerializer.cs:line 232\r\n at MongoDB.Bson.Serialization.BsonSerializer.Serialize(BsonWriter bsonWriter, Type nominalType, Object value, IBsonSerializationOptions options) in C:\\work\\10gen\\mongodb\\mongo-csharp-driver\\Bson\\Serialization
BsonSerializer.cs:line 448\r\n at MongoDB.Bson.Serialization.BsonSerializer.Serialize[TNominalType](BsonWriter bsonWriter, TNominalType value, IBsonSerializationOptions options) in C:\\work\\10gen\\mongodb\\mongo-csharp-driver\\Bson\\Serialization
BsonSerializer.cs:line 410\r\n at MongoDB.Driver.Internal.MongoInsertMessage.AddDocument[TDocument](TDocument document) in
C:\\work\\10gen\\mongodb\\mongo-csharp-driver\\Driver\\Internal
MongoInsertMessage.cs:line 51\r\n at MongoDB.Driver.MongoCollection.InsertBatch[TDocument](IEnumerable`1 documents, SafeMode safeMode) in C:\\work\\10gen\\mongodb\\mongo-csharp-driver\\Driver\\Core
MongoCollection.cs:line 742\r\n at MongoDB.Driver.MongoCollection.Insert[TDocument](TDocument document, SafeMode safeMode) in C:\\work\\10gen\\mongodb\\mongo-csharp-driver\\Driver\\Core
MongoCollection.cs:line 696\r\n

Comment by Robert Stam [ 15/Jun/11 ]

Where is this not supported?

Look for UInt16Serializer, UInt32Serializer, UInt64Serializer.

Generated at Wed Feb 07 21:36:16 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.