MongoDB 驱动程序 2.2 的动态类型 - 如何不序列化 _t 类型鉴别器?

问题描述 投票:0回答:4

我有两个 .Net (C#) 应用程序通过 Mongo 传递“消息”。一个是Writer,另一个是Reader_t 类型鉴别器导致我出现反序列化问题。

这是他们用来通信的公共类的简化版本:

public class MyMessage {
    public long Id {get; set;}
    public string MessageText {get; set;}
    public dynamic OriginalObject{get; set;}
}

Writer应用程序实际上是通过映射Reader应用程序未知的其他类型的值来创建MyMessage的实例。它映射出已知的公共元素,然后将整个原始对象分配给动态 OriginalObject 属性。

public MyMessage CreateMessage(SomeOtherType originalMsg) {
    return new MyMessage {
        MessageText = originalMsg.SomeField,
        OriginalObject = originalMsg            
    };
}

当 .Net Mongo 驱动程序序列化 MyMessage 时,它将 _t 类型鉴别器添加到 OriginalObject 子文档中。

但是,Reader 应用程序并未引用 Writer 所引用的所有类型。当 Reader 应用程序尝试反序列化 MyMessage 时,只要

OriginalObject
是它不具有的类型,它就会抛出 Unknown discriminator value 错误。

我读到,只要实际类型与名义类型不匹配,就会添加 _t 类型鉴别器。这在真正的多态、强类型场景中是有意义的。但现在 C# 有了动态类型,我想使用它。

此外,如果 MyMessage.OriginalMessage 的实例是纯匿名类型,则它可以正常工作。没有写入 _t 类型鉴别器。 Reader 应用程序愉快地将其反序列化为动态(expando)对象,并且一切正常。只有当 OriginalMessage 的实例是某种强类型时,我才会遇到此问题。

如何告诉 Mongo 驱动程序不要为动态类型添加 _t 类型鉴别符? 我也对其他解决方案/解决方法感兴趣,但简单地排除 _t 将是我的首选方法。

.Net框架4.6.1,MongoDB.Driver 2.2.3

更多示例代码:

public void SaveMessages()
{
    var message1 = new MyMessage { 
        Id=1, 
        MessageText="Anonymous Message", 
        OriginalMessage=new{ Field1 = "f1", Field2 = "f2" }
    };
    _collection.InsertOne(message1);  // no _t since OriginalMessage is purely anonymous

    var message2 = new MyMessage { 
        Id=2, 
        MessageText="Strong Message", 
        OriginalMessage=new SomeOtherType{Prop1="p1", Prop2="p2"}
    };
    _collection.InsertOne(message2); // has _t since SomeOtherType is not "dynamic"
}
c# .net mongodb mongodb-.net-driver
4个回答
0
投票

您可以使用

Project
在获取时排除某个项目,如下所示:

return await this.GetCollection()
            .Find(filter: new BsonDocument("_id", handle))
            .Project(Builders<BsonDocument>.Projection.Exclude("_t"))
            .FirstAsync();

0
投票

聚会有点晚了,但这里有一个禁止写入鉴别器的约定:

internal class NoDiscriminatorConvention : ConventionBase, IClassMapConvention
{
    public void Apply(BsonClassMap classMap)
    {
        classMap.SetDiscriminatorIsRequired(false);
    }
}

您可以使用以下方法在应用程序启动时注册它:

ConventionRegistry.Register("No discriminators for message types",
        new ConventionPack { new NoDiscriminatorConvention() },
        type => typeof(MyMessage).IsAssignableFrom(type));

如果您已经映射了

MyMessage
类,例如要使用
Id
属性作为 MongoDB 的
_id
元素,您可以在此处执行此操作:

BsonClassMap.RegisterClassMap<MyMessage>(cm => {
    cm.MapIdMember(m => m.Id).SetSerializer(new StringSerializer(BsonType.ObjectId));
    cm.AutoMap();
    cm.SetDiscriminatorIsRequired(false); });

0
投票

使用 mongodriver 2.10 时,mnibic 的解决方案对我不起作用。

但是我能够使用 UndiscriminatorActualTypeSerializer 来防止添加 _t 和 _v:

public class UndiscriminatedActualTypeConvention : ConventionBase, IClassMapConvention
{
    public void Apply(BsonClassMap cm)
    {
        Type type = cm.ClassType;
        if (type.IsClass
            && type != typeof(string)
            && type != typeof(object)
            && !type.IsAbstract)
        {
            foreach (var memberMap in cm.DeclaredMemberMaps)
            {
                var genericBase = typeof(UndiscriminatedActualTypeSerializer<>);
                var genericType = genericBase.MakeGenericType(new[] { memberMap.MemberType });
                var serializer = genericType
                    .GetProperty("Instance", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)
                    .GetValue(null, null) as IBsonSerializer;

                cm.MapMember(memberMap.MemberInfo).SetSerializer(serializer);
            }
        }
    }
}

程序开始时的用法:

ConventionRegistry.Register("No discriminators",
    new ConventionPack { new UndiscriminatedActualTypeConvention() },
    type => true);

0
投票

https://stackoverflow.com/a/63686596/1536933(由Kevin Smith)有一个对我有用的答案。对于首先发现这个问题的人来说,其要点是:

public class NullDiscriminatorConvention : IDiscriminatorConvention
{
    public static NullDiscriminatorConvention Instance { get; }
        = new NullDiscriminatorConvention();

    public Type GetActualType(IBsonReader bsonReader, Type nominalType)
        => nominalType;

    public BsonValue GetDiscriminator(Type nominalType, Type actualType)
        => null;

    public string ElementName { get; } = null;
}


BsonSerializer.RegisterDiscriminatorConvention(YourType, NullDiscriminatorConvention.Instance);
© www.soinside.com 2019 - 2024. All rights reserved.