如何在C#与MongoDB中实现TimeOnly的序列化与反序列化
问题场景
- 环境:.NET 6 + C#,MongoDB 4.4
- 模型类使用
TimeOnly?类型,存储到MongoDB后生成的文档结构如下:
"defaultTime" : { "hour" : 15, "minute" : 15, "second" : 0, "millisecond" : 0, "ticks" : NumberLong(549000000000) }
- 从MongoDB读取数据时触发反序列化异常:
System.FormatException: 反序列化Models.Customer类的DefaultTime属性时出错:无法反序列化值类型System.TimeOnly。
---> MongoDB.Bson.BsonSerializationException: 无法反序列化值类型System.TimeOnly。
在 MongoDB.Bson.Serialization.BsonClassMapSerializer`1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
在 MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
在 MongoDB.Bson.Serialization.Serializers.NullableSerializer`1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
在 MongoDB.Bson.Serialization.Serializers.SerializerBase`1.MongoDB.Bson.Serialization.IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
在 MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize(IBsonSerializer serializer, BsonDeserializationContext context)
在 MongoDB.Bson.Serialization.BsonClassMapSerializer`1.DeserializeMemberValue(BsonDeserializationContext context, BsonMemberMap memberMap)
解决方案(已验证)
通过在MongoDB中直接存储TimeOnly对应的ticks值规避序列化问题,存储结构简化为:
"defaultTime" : NumberLong(549000000000)
需要自定义MongoDB序列化器实现ticks与TimeOnly?的双向转换:
自定义序列化器代码
public class TimeOnlyNullableSerializer : IBsonSerializer<TimeOnly?> { public Type ValueType => typeof(TimeOnly?); public TimeOnly? Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var bsonType = context.Reader.GetCurrentBsonType(); if (bsonType == BsonType.Null) { context.Reader.ReadNull(); return null; } if (bsonType == BsonType.Int64) { var ticks = context.Reader.ReadInt64(); return TimeOnly.FromTicks(ticks); } throw new NotSupportedException($"无法从类型{bsonType}反序列化为TimeOnly?"); } public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TimeOnly? value) { if (!value.HasValue) { context.Writer.WriteNull(); return; } context.Writer.WriteInt64(value.Value.Ticks); } object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { return Deserialize(context, args); } void IBsonSerializer.Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) { Serialize(context, args, (TimeOnly?)value); } }
注册序列化器
在MongoDB客户端初始化时绑定序列化器:
var clientSettings = MongoClientSettings.FromConnectionString("你的MongoDB连接字符串"); var classMap = BsonClassMap.LookupClassMap(typeof(Customer)); classMap.GetMemberMap(c => c.DefaultTime) .SetSerializer(new TimeOnlyNullableSerializer()); BsonSerializer.RegisterClassMap(classMap); var client = new MongoClient(clientSettings);
通过上述配置,即可实现TimeOnly?类型与MongoDB中ticks值的正常序列化、反序列化,解决原异常问题。
内容的提问来源于stack exchange,提问作者Bhanu

