I have two functions one for write and one for read when i try to use the read function first i get this error:
An error occurred while deserializing the Message property of class DDSRecorder.MessageContainer: Instances of abstract classes cannot be created
Here is what i dont get, if i use the write first at least once then the read works fine. i dont understand what happens in the background that makes it ok to initialize abstract class if we used it once to write.
Adding the map for it didn't resolve the problem:
if (BsonClassMap.IsClassMapRegistered(typeof(MessageContainer)))
{
BsonClassMap.RegisterClassMap<MessageBase>(cm =>
{
cm.AutoMap();
cm.SetIsRootClass(true);
});
}
Here is the class i am using for the mongo collection.
[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));
}
}
And the abstract class inside:
public abstract class MessageBase
{
protected MessageBase();
public MessageBase CreateCopy();
}
Example of write method:
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;
}
Example of read method:
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;
}
What am i missing for it to be able to initialize the abstract class without the need of writing first?
Well, kind of an anti-pattern here (I don't like adding dependencies from base classes to their implementations), but a quick fix would be to add
[BsonKnownTypes(typeof(MyImplementation))]
where MyImplementation
is the type that implements your abstract class.
on your MessageBase
class. For me this did the trick - I was able to read the data and deserialize just fine. I didn't have to add any class maps either.