FormatException:无法从 BsonType 'ObjectId' 反序列化 'Guid'

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

这几天我都快疯了

我正在尝试在我的 MongoDb 中使用字符串 Guid 作为 _id,并使用最新版本的 driver。我偶然发现了这个教程,它让我明白了......

Storing GUIDs as strings in MongoDB with C#

但我收到此错误:FormatException:无法从 BsonType 'ObjectId'.

反序列化 'Guid'
   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)

堆栈跟踪:

 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()

我已经在 Internet 上搜索以寻找答案。

我不想使用 POCO 的装饰,或者 RETURN BsonDocuments。已经走过这条路:

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

并没有奏效。也做了 公约 事情...

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)));
        }
    }

也没用...

这是我的代码:

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

这在 Db 连接之前被调用:

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); 

之后,在构造函数中初始化 collection 之后,我尝试获取记录...

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;
        }

当然,我在 Startup.cs

中的“ConfigureServices”上初始化序列化程序
BsonSerializer.RegisterSerializer(new GuidSerializer(BsonType.String));

最后,带有字符串 GUID(或 UUID)的 BSON 文档:

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

如果有人能提供一些帮助,我将不胜感激。谢谢!!!

[更新]

我这样做了:

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

...我可以看到字符串 GUID,一切都完美加载。它只是没有正确解析。我不知道为什么。而且我拒绝手动解析这个文档!!!!这就是 driver 应该做的!

[更新 2]

决定用属性装饰属性:

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

也没用...

如果您想继续在本机使用 Guid(而不是将它们视为字符串),请更改您正在注册的序列化程序。

删除:

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

并将其替换为:

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

是的,第二行(在撰写本文时)是一种过时的方法,但目前这是唯一的方法。这是一个 open bug with the c# driver.

There does seem to be an issue here that needs to be fixed.

In the long run we encourage everyone to use the new V3 GuidRepresentationMode, and in fact when using V3 mode this bug does not appear to happen. I added the following line of code as the first line of main:

BsonDefaults.GuidRepresentationMode = GuidRepresentationMode.V3;

We still need to figure out how to get your code to work in V2 mode, but if you are willing to use V3 mode you could have an immediate workaround.

显然,我使用的 GUID 不是有效的 GUID。我使用在线生成器生成它们。

我在另一个网站上生成了新的,瞧瞧。一切顺利。