Due to environment restrictions at work I'm implementing a rough Event Sourcing store in MongoDB. I'm trying to get a list of IClientEvents
from Mongo like so:
var events = await _db.GetCollection<IClientEvent>("ClientEvents").FindAsync(c => c.ClientId == clientId);
I get the following exception when I run the above mentioned repository method:
Message: System.InvalidOperationException : {document}.ClientId is not supported.
The IClientEvent
interface is defined as:
public interface IClientEvent
{
Guid Id { get; set; }
long TimeStamp { get; set; }
Guid ClientId { get; set; }
}
public class ClientChangedEvent : IClientEvent
{
public Guid Id { get; set; }
public long TimeStamp { get; set; }
public Guid ClientId { get; set; }
public IEnumerable<Change> Changes;
// ... other properties for the event
}
There will be many different event types stored into a single collection, all of which will implement IClientEvent
. I want to just get, in a single call, all events that have occurred to a Client by clientId
.
I have registered all of the concrete implementations of IClientEvent
and even added a custom discriminator:
var clientEventsDiscriminator = new ClientEventsMongoDiscriminatorConvention();
BsonSerializer.RegisterDiscriminatorConvention(typeof(IClientEvent),clientEventsDiscriminator);
BsonClassMap.RegisterClassMap<ClientChangedEvent>();
BsonSerializer.RegisterDiscriminatorConvention(typeof(ClientChangedEvent), clientEventsDiscriminator);
I have even tried registering an ImpliedImplementationInterfaceSerializer
as mentioned in this SO post but it throws an exception when I register the 2nd concrete implementation that I have already registered a serializer for IClientEvent
.
Not sure where to go from here. Any help is greatly appreciated!
-- EDIT for more code:
Here is the full registration code:
var clientEventsDiscriminator = new ClientEventsMongoDiscriminatorConvention();
BsonSerializer.RegisterDiscriminatorConvention(typeof(IClientEvent),clientEventsDiscriminator);
clientEventsDiscriminator.AddEventType<ClientChangedEvent>();
BsonClassMap.RegisterClassMap<ClientChangedEvent>();
BsonSerializer.RegisterDiscriminatorConvention(typeof(ClientChangedEvent), clientEventsDiscriminator);
clientEventsDiscriminator.AddEventType<ClientAddedEvent>();
BsonClassMap.RegisterClassMap<ClientAddedEvent>();
BsonSerializer.RegisterDiscriminatorConvention(typeof(ClientAddedEvent), clientEventsDiscriminator);
Here is the Discriminator:
public class ClientEventsMongoDiscriminatorConvention : IDiscriminatorConvention
{
private Dictionary<string, Type> _eventTypes = new Dictionary<string, Type>();
public string ElementName => "_eventType";
public BsonValue GetDiscriminator(Type nominalType, Type actualType)
{
return GetDiscriminatorValueForEventType(actualType);
}
public Type GetActualType(IBsonReader bsonReader, Type nominalType)
{
var bookmark = bsonReader.GetBookmark();
bsonReader.ReadStartDocument();
if (!bsonReader.FindElement(ElementName))
{
throw new InvalidCastException($"Unable to find property '{ElementName}' in document. Cannot map to an EventType.");
}
var value = bsonReader.ReadString();
bsonReader.ReturnToBookmark(bookmark);
if (_eventTypes.TryGetValue(value, out var type))
{
return type;
}
throw new InvalidCastException($"The type '{value}' has not been registered with the '{nameof(ClientEventsMongoDiscriminatorConvention)}'.");
}
private string GetDiscriminatorValueForEventType(Type type)
{
var indexOfEventWord = type.Name.IndexOf("Event");
if (indexOfEventWord == -1)
{
return type.Name;
}
return type.Name.Substring(0, indexOfEventWord);
}
public void AddEventType<T>()
{
var discriminatorName = GetDiscriminatorValueForEventType(typeof(T));
_eventTypes.TryAdd(discriminatorName, typeof(T));
}
}
When running the code it doesn't appear to ever hit the GetActualType
method of the discriminator.
I managed to get it to work by simply changing IClientEvent
from an interface to an abstract class.