2016-03-04 1 views
0

Мы используем MongoDb для хранения атрибутов продукта и создали собственный сериализатор массивов, чтобы взять словарь и сериализовать его в пользовательском формате. Формат принимает ключ и значение каждого словаря и сохраняет его как отдельный элемент массива, разделенный двоеточием.Как реализовать TryGetItemSerializationInfo в сериализаторе массива Bson?

класса Пример продукта

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.

На основании приведенного выше примера, что должен сделать этот метод и установить его в качестве выходного?

Я попытался найти строковый сериализатор и использовать его, но он дает сообщение об ошибке «Невозможно наложить объект типа« System.Char »на тип« System.String ». Попытка сериализатора символов не дает ошибки, но не возвращает никаких результатов. Глядя в журналы для MongoDb, похоже, указывает, что поиск был преобразован символом по символам в целые числа, а затем был просмотрен.

Код для заявления фильтра также ниже

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

ответ

1

Во-первых, большая работа по сериализатором. Мне не нужно было менять какой-либо код там, чтобы это работало.

Я нашел проблему. Когда вы используете метод AnyIn, параметр, который вы передаете «Цвет: синий», требует перечисления. Так как строки .NET реализуют IEnumerable, вот почему вы увидели странный перевод запросов. Изменение его на новый [] {«Цвет: синий»} устранит вашу проблему. Альтернатива: если вы ищете только одну пару значений ключа, вы можете использовать AnyEq вместо этого.

Наконец, вы можете удалить атрибут BsonSerializer из класса ProductAttributeSerializer. Эти атрибуты используются для указания того, какой сериализатор использовать для класса или свойства, а не для определения самих сериализаторов.

+0

Большое спасибо Craig, который отлично работает с использованием массива – rrrr

Смежные вопросы