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

时间:2016-05-11 13:34:46

标签: c# .net mongodb mongodb-.net-driver

我有两个通过Mongo传递“消息”的.Net(C#)应用程序。一个是 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 Driver不要为动态类型添加_t类型鉴别器? 我也对其他解决方案/解决方案感兴趣,但只是排除 _t 将是我的首选方法。

.Net Framework 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"
}

3 个答案:

答案 0 :(得分:0)

您可以使用Project在提取时排除项目,如下所示:

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

答案 1 :(得分:0)

派对有点晚了,但这是一个禁止歧视者写作的惯例:

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

您可以使用以下方式在应用启动时注册:

MyMessage

如果您已经映射了Id类,例如要将_id属性用作MongoDB的BsonClassMap.RegisterClassMap<MyMessage>(cm => { cm.MapIdMember(m => m.Id).SetSerializer(new StringSerializer(BsonType.ObjectId)); cm.AutoMap(); cm.SetDiscriminatorIsRequired(false); }); 元素,您可以在那里执行:

import React, { Component } from 'react'
import './Todo.css'

class Todo extends Component {
  constructor (props) {
    super(props)
    this.state = { deleted: false }
  }

  clickFunction (event) {
    this.setState({ deleted: true })
  }

  render () {
    const classes = ['todo', this.props.className]

    return (
      {
        !this.state.deleted
        ? <li>
          {this.props.todoName}
          <button
            className={classes.join(' ')}
            onClick={this.clickFunction.bind(this)}
            value={this.props.value}
          >delete</button>
        </li>
        : null
    )
  }
}

export default Todo

答案 2 :(得分:0)

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

但是我能够使用 UndiscriminatedActualTypeSerializer 阻止添加_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);