MongoDB C#驱动程序 - 将集合序列化为接口

5

由于工作环境的限制,我正在MongoDB中实现一个简单的事件溯源存储。我想从MongoDB中获取一个IClientEvents列表,代码如下:

 var events = await _db.GetCollection<IClientEvent>("ClientEvents").FindAsync(c => c.ClientId == clientId);

当我运行上述仓库方法时,会出现以下异常:
Message: System.InvalidOperationException : {document}.ClientId is not supported.

IClientEvent 接口定义如下:

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
}

一个集合中存储许多不同类型的事件,这些事件都实现了IClientEvent。我想在单个调用中获取所有以clientId为客户端发生的事件。

我已经注册了所有IClientEvent的具体实现,甚至添加了自定义辨别器:

        var clientEventsDiscriminator = new ClientEventsMongoDiscriminatorConvention();
        BsonSerializer.RegisterDiscriminatorConvention(typeof(IClientEvent),clientEventsDiscriminator);
        BsonClassMap.RegisterClassMap<ClientChangedEvent>();
        BsonSerializer.RegisterDiscriminatorConvention(typeof(ClientChangedEvent), clientEventsDiscriminator);

我甚至尝试注册一个ImpliedImplementationInterfaceSerializer,就像这篇SO帖子中提到的那样,但是当我为已经注册了IClientEvent序列化程序的第二个具体实现注册序列化程序时,它会抛出异常。

不确定接下来该怎么做。非常感谢任何帮助!

--编辑更多代码:

以下是完整的注册代码:

        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);

这是鉴别器(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));
    }
}

运行代码时,似乎从鉴别器的GetActualType方法中没有得到任何结果。

你确定不需要向那个仓库方法提供具体的类型吗?该仓库方法如何知道ClientChangedEvent是你想要的实现? - Robert Harvey
从你的问题中无法确定 ClientEventsMongoDiscriminatorConvention 是否提供所需的映射。你没有展示给我们那段代码。 - Robert Harvey
尝试将接口属性设为公共的。 - jdweng
嘿@RobertHarvey-我已经编辑了问题并加入了你要求的代码。如注所述-当运行代码时,似乎鉴别器的GetActualType方法从未被调用。 - LukeP
@jdweng - 在接口的属性/方法上使用public/private等修饰符是不合法的。它默认为公共访问权限。 - LukeP
@RobertHarvey - 就添加一个具体类型到方法中而言,那不是我想要的。我想获取客户端的所有事件,而不管类型如何。我希望鉴别器告诉序列化程序将其序列化为哪个具体类(您现在可以在我添加的代码中看到)。 - LukeP
1个回答

3

我通过将IClientEvent从接口更改为抽象类,使其正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接