问题描述
这几天我快疯了。
我正在尝试使用字符串 Guid 作为 _id 在我的 MongoDb 中使用最新版本的驱动程序。我偶然发现了这个教程,它让我明白了一些......
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,ReadPreference readPreference,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,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()
我在互联网上搜索了答案。
我不想使用 POCO 的装饰,也不想返回 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);
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,一切都完美加载。它只是没有正确解析。我不知道为什么。我拒绝手动解析这个文件!!!!这才是司机应该做的!
[更新 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。
这里似乎确实存在需要修复的问题。
从长远来看,我们鼓励大家使用新的 V3 GuidRepresentationMode,实际上在使用 V3 模式时,这个错误似乎不会发生。我在main的第一行添加了以下代码行:
BsonDefaults.GuidRepresentationMode = GuidRepresentationMode.V3;
我们仍然需要弄清楚如何让您的代码在 V2 模式下工作,但如果您愿意使用 V3 模式,您可以立即找到解决方法。
,显然,我使用的 GUID 不是有效的 GUID。我使用在线生成器生成它们。
我在另一个网站上生成了新的,瞧。一切都奏效了。