如何在Bson阵列序列化器上实现TryGetItemSerializationInfo?

时间:2016-03-04 10:56:33

标签: mongodb mongodb-.net-driver

我们使用MongoDb来存储产品属性,并创建了一个自定义数组序列化程序来获取字典并以自定义格式对其进行序列化。格式获取每个字典项的键和值,并将其存储为以冒号分隔的单个数组项。

示例产品类

public class Product
{
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; }

    public string Name { get; set; }

    [BsonSerializer(typeof(ProductAttributeSerializer))]
    public Dictionary<string, string> Attributes { get; set; } = new Dictionary<string, string>();
}

自定义数组格式的示例数据

{ 
    "_id" : ObjectId("56d94ee01992d148b4c10d59"), 
    "Name" : "My Product", 
    "Attributes" : [
        "Colour:Blue", 
        "Gender:Mens"
    ]
}

这是我们的序列化程序

[BsonSerializer(typeof(ProductAttributeSerializer))]
    public class ProductAttributeSerializer : IBsonSerializer, IBsonArraySerializer
    {
        public Type ValueType { get { return typeof(Dictionary<string, string>); } }

        public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
        {
            var type = context.Reader.GetCurrentBsonType();
            var attributes = new Dictionary<string, string>();

            switch (type)
            {
                case BsonType.Array:

                    context.Reader.ReadStartArray();

                    while (context.Reader.ReadBsonType() != BsonType.EndOfDocument)
                    {
                        string[] splitValues = context.Reader.ReadString().Split(':');
                        attributes.Add(splitValues[0], splitValues[1]);
                    }

                    context.Reader.ReadEndArray();

                    return attributes;

                default:
                    throw new NotImplementedException($"No implementation to deserialize {type}");
            }
        }

        public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
        {
            var attributes = value as Dictionary<string, string>;

            if (attributes != null)
            {
                context.Writer.WriteStartArray();

                foreach (KeyValuePair<string, string> attr in attributes)
                {
                    context.Writer.WriteString($"{attr.Key}:{attr.Value}");
                }

                context.Writer.WriteEndArray();
            }
        }

        public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo)
        {
            string elementName = null;
            var serializer = BsonSerializer.LookupSerializer(typeof(string));
            var nominalType = typeof(string);
            serializationInfo = new BsonSerializationInfo(elementName, serializer, nominalType);
            return true;
        }
    }

项目的所有序列化都有效,但是当尝试通过驱动程序查询数据并尝试执行与此db.Product.find({ Attributes : { $in : [ "Colour:Blue" ] } })等效的操作时,我收到错误。

原始错误表明序列化程序需要是IBsonArraySerializer,现在我需要实现TryGetItemSerializationInfo方法。

根据上面的例子,这个方法应该做什么并设置为输出?

我已经尝试查找字符串序列化程序并使用它,但是它提示错误说&#34;无法转换类型&#39; System.Char&#39;输入&#39; System.String&#39;&#34;。尝试使用char序列化程序不会产生错误但不会返回任何结果。查看MongoDb的日志似乎表明搜索是逐个字符转换为整数然后进行搜索。

filter语句的代码也在

之下
Database.GetCollection<Product>("Product").Find(Builders<Product>.Filter.AnyIn("Attributes", "Colour:Blue")).ToListAsync()

1 个答案:

答案 0 :(得分:1)

首先,在串行器上做得很好。我不需要在那里更改任何代码来实现这一点。

我发现了问题。当您使用AnyIn方法时,您传递“Color:Blue”的参数需要一个可枚举的参数。由于.NET字符串实现IEnumerable,这就是为什么你看到一个奇怪的查询翻译。将其更改为新[] {“颜色:蓝色”}将解决您的问题。另一种方法是,当您只搜索单个键值对时,可以使用AnyEq。

最后,您可以从ProductAttributeSerializer类中删除BsonSerializer属性。此属性用于指示用于类或属性的序列化程序,而不是用于标识序列化程序本身。