Search code examples
c#mongodbasp.net-coreexceptionformatexception

FormatException: Cannot deserialize a 'Guid' from BsonType 'ObjectId'


I have been going crazy this last days.

I'm trying to use string Guids as _id's in my MongoDb, with the latest version of the driver. I stumbled with this tutorial, which shed me some light...

Storing GUIDs as strings in MongoDB with C#

But I am getting this error: FormatException: Cannot deserialize a 'Guid' from BsonType 'ObjectId'.

   MongoDB.Bson.Serialization.Serializers.GuidSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
MongoDB.Bson.Serialization.Serializers.SerializerBase<TValue>.MongoDB.Bson.Serialization.IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize(IBsonSerializer serializer, BsonDeserializationContext context)
MongoDB.Bson.Serialization.BsonClassMapSerializer<TClass>.DeserializeMemberValue(BsonDeserializationContext context, BsonMemberMap memberMap)

The stack trace:

 at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.DeserializeMemberValue(BsonDeserializationContext context, BsonMemberMap memberMap)
   at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.DeserializeClass(BsonDeserializationContext context)
   at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
   at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
   at MongoDB.Driver.Core.Operations.CursorBatchDeserializationHelper.DeserializeBatch[TDocument](RawBsonArray batch, IBsonSerializer`1 documentSerializer, MessageEncoderSettings messageEncoderSettings)
   at MongoDB.Driver.Core.Operations.FindCommandOperation`1.CreateFirstCursorBatch(BsonDocument cursorDocument)
   at MongoDB.Driver.Core.Operations.FindCommandOperation`1.CreateCursor(IChannelSourceHandle channelSource, BsonDocument commandResult)
   at MongoDB.Driver.Core.Operations.FindCommandOperation`1.Execute(RetryableReadContext context, CancellationToken cancellationToken)
   at MongoDB.Driver.Core.Operations.FindOperation`1.Execute(RetryableReadContext context, CancellationToken cancellationToken)
   at MongoDB.Driver.Core.Operations.FindOperation`1.Execute(IReadBinding binding, CancellationToken cancellationToken)
   at MongoDB.Driver.OperationExecutor.ExecuteReadOperation[TResult](IReadBinding binding, IReadOperation`1 operation, CancellationToken cancellationToken)
   at MongoDB.Driver.MongoCollectionImpl`1.ExecuteReadOperation[TResult](IClientSessionHandle session, IReadOperation`1 operation, ReadPreference readPreference, CancellationToken cancellationToken)
   at MongoDB.Driver.MongoCollectionImpl`1.ExecuteReadOperation[TResult](IClientSessionHandle session, IReadOperation`1 operation, CancellationToken cancellationToken)
   at MongoDB.Driver.MongoCollectionImpl`1.FindSync[TProjection](IClientSessionHandle session, FilterDefinition`1 filter, FindOptions`2 options, CancellationToken cancellationToken)
   at MongoDB.Driver.MongoCollectionImpl`1.<>c__DisplayClass45_0`1.<FindSync>b__0(IClientSessionHandle session)
   at MongoDB.Driver.MongoCollectionImpl`1.UsingImplicitSession[TResult](Func`2 func, CancellationToken cancellationToken)
   at MongoDB.Driver.MongoCollectionImpl`1.FindSync[TProjection](FilterDefinition`1 filter, FindOptions`2 options, CancellationToken cancellationToken)
   at MongoDB.Driver.FindFluent`2.ToCursor(CancellationToken cancellationToken)
   at MongoDB.Driver.Core.Operations.AsyncCursorSourceEnumerableAdapter`1.GetEnumerator()
   at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
   at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
   at System.Linq.SystemCore_EnumerableDebugView`1.get_Items()

I've scoured the Internet in search of an answer.

I DO NOT WANT TO USE POCO's DECORATION, OR RETURN BsonDocuments. Already went this route:

BsonDefaults.GuidRepresentationMode = GuidRepresentationMode.V3;
            BsonSerializer.RegisterSerializer<Guid>(new GuidSerializer(GuidRepresentation.Standard));

and didn't work. Also did the convention thing...

public class GuidAsStringRepresentationConvention : ConventionBase, IMemberMapConvention
    {
        public void Apply(BsonMemberMap memberMap)
        {
            if (memberMap.MemberType == typeof(Guid))
                memberMap.SetSerializer(new GuidSerializer(BsonType.String));
            
            else if (memberMap.MemberType == typeof(Guid?))
                memberMap.SetSerializer(new NullableSerializer<Guid>(new GuidSerializer(BsonType.String)));
        }
    }

Didn't work either...

This is my code:

public class TestClass
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public bool IsActive { get; set; }
    }

This gets called BEFORE the Db connection:

if (!BsonClassMap.IsClassMapRegistered(typeof(TestClass)))
        {
            BsonClassMap.RegisterClassMap<TestClass>(cm =>
            {
                cm.MapIdMember(m => m.Id).SetOrder(0);
                cm.MapMember(m => m.Name).SetOrder(1);
                cm.MapMember(m => m.IsActive).SetOrder(2);
            });
        }

var conn = configuration.GetConnectionString("MongoDb");
            var name = MongoUrl.Create(conn).DatabaseName;
            var client = new MongoClient(conn);
            _db = client.GetDatabase(name); 

after that, I try to get the records, after I initialize the collection in the constructor...

public async Task<IEnumerable<TestClass>> ReadAllAsync(CancellationToken cancellationToken = default)
        {
            var filter = Builders<TestClass>.Filter.Empty;
            var items = await _collection.Find<TestClass>(filter).ToListAsync(cancellationToken: cancellationToken);

            return items;
        }

And of course, I initialize the serializer on "ConfigureServices" in the Startup.cs

BsonSerializer.RegisterSerializer(new GuidSerializer(BsonType.String));

Last, the BSON document with the string GUID (or UUID):

{ 
    "_id" : "c2ea54fc-3942-4ad5-8315-9e96cc5de034", 
    "name" : "I'm Going Crazy", 
    "isActive" : true
}

If anyone can shed some light, I would appreciate it a lot. Thanks!!!!

[UPDATE]

I did this:

var col = _db.GetCollection<BsonDocument>("TestCollection");
var list = col.Find(FilterDefinition<BsonDocument>.Empty).ToList();

...and I can see the string GUIDs, everything loads perfectly. It's just NOT PARSING correctly. AND I DON'T KNOW WHY. And I refuse to parse this document manually!!!! That's what the driver is supposed to be doing!

[UPDATE 2]

Decided to decorate the property with:

[BsonId]
[BsonRepresentation(MongoDB.Bson.BsonType.String)]
public Guid Id { get; set; }

Didn't work either...


Solution

  • Apparently, the GUIDs I was using were not valid GUIDs. I generated them using an online generator.

    I generated new ones on another website, and voilá. Everyting worked.