MongoDB BsonSerializer反序列化时BaseEvent属性值为默认值问题
问题:MongoDB事件存储中子类事件属性反序列化失败
使用MongoDb.Driver/2.19.1与C# .NET 7实现MongoDB事件存储时,StreamRecord类能正常反序列化,但其中BaseEvent子类(如PersonCreatedEvent)的属性始终为默认值,数据库中明明存储了对应数据。
相关代码
StreamRecord类
public sealed class StreamRecord { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string Id { get; init; } public string StreamName { get; init; } public long Version { get; init; } public DateTime TimeStamp { get; init; } public string EventType { get; init; } public BaseEvent EventData { get; init; } public StreamRecord(string streamName, BaseEvent @event, DateTime timeStamp) { Version = @event.Version; // Versions of record in stream and Event should match. StreamName = streamName; EventType = @event.EventType; EventData = @event; TimeStamp = timeStamp; } }
BaseEvent类
public abstract class BaseEvent : Message, ITrackedMessage, INotification { protected BaseEvent() { // ReSharper disable once UseNameOfInsteadOfTypeOf EventType = GetType().Name; MessageType = MessageType.Event; TimeStamp = DateTime.Now; } /// <summary> /// Gets or initializes Version number of an event /// </summary> public long Version { get; set; } /// <summary> /// Gets type name of an event /// </summary> public string EventType { get; init; } public string? ApplicationId { get; init; } public string? CorrelationId { get; init; } public string? UserId { get; init; } public DateTime TimeStamp { get; init; } }
PersonCreatedEvent类
public class PersonCreatedEvent : BaseEvent { public PersonCreatedEvent(string name, int age) : base() { Name = name; Age = age; } public string Name { get; } public int Age { get; } }
初始化代码
public static IServiceCollection AddEventStore(this IServiceCollection services, Func<MongoDbEventRepositoryOptions> mongoDbOptions, params Assembly[] eventsHostAssemblies) { var options = mongoDbOptions(); InitializeBsonClassMaps(eventsHostAssemblies); services.AddScoped<IMongoDatabase>(sp => new MongoClient(options.ConnectionString).GetDatabase(options.DatabaseName)) .AddSingleton(options) .AddScoped<IEventRepository, MongoDbEventRepository>() .AddEventStore(); return services; } private static void InitializeBsonClassMaps(IEnumerable<Assembly> eventsHostAssemblies) { var eventTypes = eventsHostAssemblies .SelectMany(a => a.GetTypes()) .Where(x => x.Name.EndsWith("Event", StringComparison.InvariantCultureIgnoreCase) && x.BaseType == typeof(BaseEvent) && x.IsClass) .ToList(); BsonClassMap.RegisterClassMap<BaseEvent>(ce => { ce.AutoMap(); ce.SetIsRootClass(true); //ce.SetIgnoreExtraElements(true); }); foreach (var eventClassMap in eventTypes.Select(eventType => new BsonClassMap(eventType))) { //eventClassMap.SetIgnoreExtraElements(true); BsonClassMap.RegisterClassMap(eventClassMap); } }
MongoDB存储的Bson文档
{ "_id" : ObjectId("644a86173f5ac693ebd6813b"), "StreamName" : "PersonAggregate[0]", "Version" : NumberLong(0), "TimeStamp" : ISODate("2023-04-27T14:26:31.294+0000"), "EventType" : "PersonCreatedEvent", "EventData" : { "_t" : [ "EventType", "PersonCreatedEvent" ], "MessageId" : LUUID("52B9C7B4-8FFC-7E45-88A2-585B44B4443C"), "MessageType" : NumberInt(2), "Version" : NumberLong(0), "EventType" : "PersonCreatedEvent", "ApplicationId" : null, "CorrelationId" : null, "UserId" : null, "Name" : "Jondi", "Age" : NumberInt(50) } }
解决方案
1. 修复Bson类映射的多态配置问题
当前类映射初始化未正确处理子类属性映射与多态判别符配置,修改InitializeBsonClassMaps方法:
private static void InitializeBsonClassMaps(IEnumerable<Assembly> eventsHostAssemblies) { var eventTypes = eventsHostAssemblies .SelectMany(a => a.GetTypes()) .Where(x => x.Name.EndsWith("Event", StringComparison.InvariantCultureIgnoreCase) && typeof(BaseEvent).IsAssignableFrom(x) && x.IsClass && !x.IsAbstract) .ToList(); // 配置基类映射,指定多态判别符字段并注册子类 BsonClassMap.RegisterClassMap<BaseEvent>(cm => { cm.AutoMap(); cm.SetIsRootClass(true); cm.SetDiscriminator("_t"); cm.SetDiscriminatorIsRequired(true); // 注册所有子类 foreach (var type in eventTypes) { cm.AddKnownType(type); } }); // 为每个子类自动映射属性 foreach (var eventType in eventTypes) { BsonClassMap.LookupClassMap(eventType); } }
2. 标记子类构造函数以支持只读属性反序列化
PersonCreatedEvent的Name和Age是只读属性,MongoDB驱动默认无法反序列化,添加[BsonConstructor]特性标记构造函数:
public class PersonCreatedEvent : BaseEvent { [BsonConstructor] public PersonCreatedEvent(string name, int age) : base() { Name = name; Age = age; } public string Name { get; } public int Age { get; } }
3. 确保映射初始化时机正确
确认InitializeBsonClassMaps在创建MongoClient之前执行,当前AddEventStore方法的顺序已经符合要求,避免后续重复注册类映射。
内容的提问来源于stack exchange,提问作者ramaz bluashvili
相关产品推荐
相关产品推荐

