Почему Redis с protobuf сохраняет пустые массивы как null?

Я поддерживаю приложение, используя protobuf-net 2.3.3 на сервере Redis 2.8.2103, используя StackExchange .Redis 1.2.6.

Для таких объектов, как:

[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class Cachable { Foo[] Foos { get; set; } }

Когда я сохраняю с помощью простого:

using (var memoryStream = new MemoryStream())
{
    Serializer.Serialize(memoryStream, cachable);
    database.HashSetAsync("category", "key", memoryStream.ToArray());
}

А затем получить с помощью:

var response = database.HashGet("category", "key");
if (!response.HasValue) return null;
using (var memoryStream = new MemoryStream(response, false))
{
    return Serializer.Deserialize<Cachable>(memoryStream);
}

Если кэшированный массив Foos имел пустой экземпляр, как в new Foo[0], после десериализации Cachable массив становится null. Это меняет поведение некоторой части приложения и вызывает ошибки.
Ожидается ли такое поведение? Есть ли способ изменить это?


person Giulio Caccin    schedule 21.10.2019    source источник


Ответы (1)


Настоящая проблема в том, что Foo[0] это null? Если так:

  • protobuf не имеет представления о null; он не может представлять и хранить null, поэтому по умолчанию protobuf-net пропускает нулевые значения по умолчанию, делая этот по существу пустой массив
  • с небольшой оговоркой о «пустых упакованных примитивах» protobuf не имеет понятия «пустой» последовательности; в терминах .proto вы говорите о поле repeated, в котором нет элементов, что означает: оно просто не существует в полезной нагрузке вообще
  • а если его нет в полезной нагрузке, он никогда не десериализуется во что-либо, потому что в полезной нагрузке никогда нет ничего, что можно было бы указать десериализовать.

so:

  • избегайте null, если вы не имеете в виду необязательный подэлемент; определенно избегайте null в списках/массивах/и т. д.
  • не предполагайте, что пустые списки/массивы/и т. д. будут инициализированы ненулевыми значениями библиотекой

ИМО, следующее было бы разумным и прагматичным для второго пункта:

Foo[] Foos { get; set; } = Array.Empty<Foo>();

(что позволяет избежать проблемы с его инициализацией как null)

person Marc Gravell    schedule 21.10.2019