【问题标题】:Storing System.Type with MongoDb使用 MongoDb 存储 System.Type
【发布时间】:2012-01-21 13:48:44
【问题描述】:

当我存储这个类时:

class MyClass{
    ...
    public Type SomeType {get;set;} 
    ...
}

SomeType 属性像这样被序列化:

"SomeType" : {
    "_t" : "RuntimeType"
}

随后的每个查询都失败了。

我正在使用官方的 C# 驱动程序。我如何让它存储实际的类型? 谢谢。

【问题讨论】:

    标签: mongodb mongodb-.net-driver


    【解决方案1】:

    这是 System.Type 的示例序列化程序,它将 Type 的名称序列化为 BSON 字符串。这有一些限制,如果类型名称不是系统类型或在同一程序集中,则 Deserialize 方法会失败,但您可以调整此示例序列化程序以改为编写 AssemblyQualifiedName。

    public class TypeSerializer : IBsonSerializer
    {
        public object Deserialize(BsonReader reader, Type nominalType, IBsonSerializationOptions options)
        {
            var actualType = nominalType;
            return Deserialize(reader, nominalType, actualType, options);
        }
    
        public object Deserialize(BsonReader reader, Type nominalType, Type actualType, IBsonSerializationOptions options)
        {
            if (reader.CurrentBsonType == BsonType.Null)
            {
                return null;
            }
            else
            {
                var fullName = reader.ReadString();
                return Type.GetType(fullName);
            }
        }
    
        public bool GetDocumentId(object document, out object id, out Type idNominalType, out IIdGenerator idGenerator)
        {
            throw new InvalidOperationException();
        }
    
        public void Serialize(BsonWriter writer, Type nominalType, object value, IBsonSerializationOptions options)
        {
            if (value == null)
            {
                writer.WriteNull();
            }
            else
            {
                writer.WriteString(((Type)value).FullName);
            }
        }
    
        public void SetDocumentId(object document, object id)
        {
            throw new InvalidOperationException();
        }
    }
    

    诀窍是让它正确注册。您需要为 System.Type 和 System.RuntimeType 注册它,但 System.RuntimeType 不是公共的,因此您无法在代码中引用它。但是您可以使用 Type.GetType 来解决它。这是注册序列化程序的代码:

    var typeSerializer = new TypeSerializer();
    BsonSerializer.RegisterSerializer(typeof(Type), typeSerializer);
    BsonSerializer.RegisterSerializer(Type.GetType("System.RuntimeType"), typeSerializer);
    

    我使用这个测试循环来验证它是否有效:

    var types = new Type[] { typeof(int), typeof(string), typeof(Guid), typeof(C) };
    foreach (var type in types)
    {
        var json = type.ToJson();
        Console.WriteLine(json);
        var rehydratedType = BsonSerializer.Deserialize<Type>(json);
        Console.WriteLine("{0} -> {1}", type.FullName, rehydratedType.FullName);
    }
    

    其中 C 只是一个空类:

    public static class C
    {
    }
    

    【讨论】:

      【解决方案2】:

      不支持序列化 System.Type(至少目前不支持)。您必须将类型名称存储为字符串。

      或者,您可以为 System.Type 编写一个序列化程序并注册它,但这可能比简单地将类型名称存储为字符串更有效。

      【讨论】:

      • 谢谢,罗伯特。我已经通过存储类型名称来做到这一点,但我宁愿使用自定义序列化程序来做到这一点。不幸的是,我找不到如何编写的示例。
      • 我尝试为 System.Type 编写一个示例序列化程序,但我认为这不可能。原来 System.Type 是一个抽象基类,但派生自它的具体类 System.RuntimeType 不是公开可见的。由于我们的代码看不到 System.RuntimeType,我们无法为 System.RuntimeType 注册序列化程序。
      • 刚刚想到了一个 hack 来为 System.RuntimeType 注册一个序列化程序。我正在添加一个新的答案来描述它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-20
      • 2012-04-26
      • 1970-01-01
      • 2018-01-01
      • 2011-07-07
      相关资源
      最近更新 更多