除非我先写入数据库,否则无法创建抽象 class 的实例

Instances of abstract class cannot be created unless i write to database first

我有两个函数,一个用于写入,一个用于读取,当我首先尝试使用读取函数时出现此错误:

An error occurred while deserializing the Message property of class DDSRecorder.MessageContainer: Instances of abstract classes cannot be created

这是我没有得到的,如果我至少先使用写入一次,那么读取就可以正常工作。我不明白在后台发生了什么使得初始化抽象 class 如果我们用它一次来写。

为其添加地图未能解决问题:

if (BsonClassMap.IsClassMapRegistered(typeof(MessageContainer)))
{
    BsonClassMap.RegisterClassMap<MessageBase>(cm =>
    {
        cm.AutoMap();
        cm.SetIsRootClass(true);
    });
}

这是我用于 mongo collection 的 class。

[BsonIgnoreExtraElements(true)]
public class MessageContainer
{
    [BsonId]
    public ObjectId Id { get; set; }

    [BsonDateTimeOptions(Kind = DateTimeKind.Utc)]
    public DateTime TimeStamp { get; set; }

    [BsonElement]
    public string MessageType { get; set; }

    public MessageBase Message { get; set; }

    [BsonConstructor]
    public MessageContainer()
    {

    }

    [BsonConstructor]
    public MessageContainer(MessageBase message)
    {
        Message = message ?? throw new ArgumentNullException(nameof(message));
        TimeStamp = DateTime.UtcNow;
        MessageType = message.GetType().Name;
    }

    [BsonConstructor]
    public MessageContainer(DateTime timeStamp, string messageType, MessageBase message)
    {
        TimeStamp = timeStamp;
        MessageType = messageType ?? throw new ArgumentNullException(nameof(messageType));
        Message = message ?? throw new ArgumentNullException(nameof(message));
    }  
}

里面的摘要class:

public abstract class MessageBase
    {
        protected MessageBase();

        public MessageBase CreateCopy();
    }

写法示例:

public bool Write(MessageContainer message)
{
    if (message != null && _mongoCollection != null)
    {
        try
        {

            if (!BsonClassMap.IsClassMapRegistered(typeof(MessageContainer)))
            {
                BsonClassMap.RegisterClassMap<MessageContainer>();
                BsonClassMap.RegisterClassMap<MessageBase>(cm =>
                {
                    cm.AutoMap();
                    cm.SetIsRootClass(true);
                });
            }

            _mongoCollection.InsertOne(message);
           
            return true;                                      
        }
        catch (Exception Ex)
        {
            Console.WriteLine(Ex.Message);
        }
    }
    
    return false;
}

读取方法示例:

public bool GetFirstAndLastMessageTime(out DateTime firstMessageTime, out DateTime lastMessageTime)
{
  if (BsonClassMap.IsClassMapRegistered(typeof(MessageContainer)))
  {
      BsonClassMap.RegisterClassMap<MessageBase>(cm =>
      {
          cm.AutoMap();
          cm.SetIsRootClass(true);
      });
  }
  
  var filter = Builders<MessageContainer>.Filter.Empty;    
  var first = _mongoCollection.Find(filter).Sort(Builders<MessageContainer>.Sort.Ascending("TimeStamp")).Limit(5).ToList().First();
  var last = _mongoCollection.Find(filter).Sort(Builders<MessageContainer>.Sort.Descending("TimeStamp")).Limit(5).ToList().First();       
  firstMessageTime = first.TimeStamp; 
  lastMessageTime = last.TimeStamp;
  return true;
}

我缺少什么才能初始化摘要 class 而无需先写?

好吧,这里有点 anti-pattern(我不喜欢将基础 classes 的依赖项添加到它们的实现中),但是一个快速的解决方法是添加

[BsonKnownTypes(typeof(MyImplementation))]

其中 MyImplementation 是实现抽象 class 的类型。

在你的 MessageBase class 上。对我来说,这成功了——我能够很好地读取数据和反序列化。我也不必添加任何 class 地图。

虽然 amitla 提供的答案是正确的,但出于多种原因,它对我来说并不适用。

一个是我不想更改摘要 class 并将所有实现写为标签。

另一个注意事项是,我从 MongoDB 论坛中发现,显然它仅在写入后工作的原因是自动映射器,这就是为什么它在写入后对我有用。

所以我就是这样做的:


public void RegisterClassMapping(List<string> messagesType)
{
    // Map base class first
    BsonClassMap.RegisterClassMap<MessageBase>(cm =>
    {
        cm.AutoMap();
        cm.SetIsRootClass(true);
    });

    if (messagesType.Count > 0)
    {
        foreach (string message in messagesType) // Do look up for each message
        {
            Type type = GetType("NAMEOFTHENAMESPACE." + message);

            if (type == null)
            {
                type = GetType("NAMEOFTHENAMESPACE." + message);
            }

            if (type != null && !BsonClassMap.IsClassMapRegistered(type))
            {
                BsonClassMap.LookupClassMap(type);
            }
        }
    }
}

这是我使用的通用 GetType:

private Type GetType(string typeName)
{
    var type = Type.GetType(typeName);
    if (type != null) return type;

    foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
    {
        type = a.GetType(typeName);
        if (type != null)
            return type;
    }

    return null;
}

我只 运行 这一次,它会在接下来的 session 中发挥作用。