Вопрос по c# – protobuf-net: обнаружена возможная рекурсия

4

Я получаю исключение, пытаясь сериализовать граф объектов (не очень глубоко). Значимая часть этого такова:

[ERROR] FATAL UNHANDLED EXCEPTION: ProtoBuf.ProtoException: обнаружена возможная рекурсия (смещение: 5 уровней): красный в ProtoBuf.ProtoWriter.CheckRecursionStackAndPush (object) <0x00127> в ProtoBuf.Start. ProtoBuf.TutoTuButoTuTuTutoTuTuTuTutoTuTuTuTutoTuTuTutoTuButoTuTuTuTuTuTuTuTu. ProtoWriter, bool) <0x0002f>

График представляет структуру файла / каталога, и моя модель (упрощенно) выглядит следующим образом:

<code>[ProtoContract] 
[ProtoInclude(100, typeof(PackageDirectory))]
[ProtoInclude(200, typeof(PackageFile))]
public abstract class PackageMember
{
   [ProtoMember(1)] 
   public virtual string Name { get; protected set; }

   [ProtoMember(2, AsReference=true)] 
   public PackageDirectory ParentDirectory { get; protected set; }  
}

[ProtoContract]
public class PackageDirectory : PackageMember
{
   [ProtoMember(3)]
   private Dictionary<string, PackageMember> _children;

   public PackageDirectory()
   {
      _children = new Dictionary<string, PackageMember>();
   }

   public PackageDirectory (string name, PackageDirectory parentDirectory)
      : this()
   {
      this.ParentDirectory = parentDirectory;
      this.Name = name;         
   }

   public void Add (PackageMember member)
   {
      _children.Add(member.Name, member);
   }
}

[ProtoContract]
public class PackageFile : PackageMember
{
   private Stream _file;
   private BinaryReader _reader;

   private PackageFile() 
   {}

   public PackageFile (string name, int offset, int length, PackageDirectory directory,  Stream file)
   {
      this.Name = name;
      this.Length = length;
      this.Offset = offset;
      this.ParentDirectory = directory;

      _file = file;
      _reader = new BinaryReader(_file);
   }

   [OnDeserialized]
   protected virtual void OnDeserialized(SerializationContext context)
   {
      var deserializationContext = context.Context as DeserializationContext;

      if (deserializationContext != null)
      { 
         _file = deserializationContext.FileStream;
         _reader = new BinaryReader(_file);
      }
   }

   [ProtoMember(3)]
   public int Offset { get; private set; }

   [ProtoMember(4)]
   public int Length { get; private set; }
}
</code>

Глубина этого дерева около 10-15 уровней, что меньшеProtoBuf.ProtoWriter.RecursionCheckDepth значение (25). (Так может это ошибка версия protobuf-net используется один скомпилированный из ствола V2 (rev 491).

На самом деле, я решил это с помощью модификации кода protobuf-net. Я изменил значениеProtoBuf.ProtoWriter.RecursionCheckDepth до 100 и все вроде бы нормально.

Вопрос в том, существует ли какой-либо «истинный» способ сериализации такого рода графов без модификации кода protobuf? Правильно ли такое поведение или это ошибка?

Моя платформа Mono-2.10-8 в 64-разрядной версии Windows 7 Professional

P.S. Также я обнаружил, что если я десерилизую с помощью следующего кода, у меня должен быть конструктор PackageDirectory без параметров, который будет общедоступным.

<code>var value = new PackageDirectory();
RuntimeTypeModel.Default.Deserialize(ms, value, typeof(PackageDirectory), new SerializationContext {
   Context = new DeserializationContext {
   FileStream = _file,
}});
</code>

Это другая тема, но она хорошо проиллюстрирована представленным кодом. Я думаю, что в этом случае объявление частного конструктора должно быть разрешено, потому что теперь поведение отличается от такового для Serializer.Deserialize (...).

моно на ...? Linux? IOS? Также: который Точный номер версии вы используете? Marc Gravell♦
@ MarcGravell, извините за неполную информацию, я забыл указать свою платформу. Это моно. Я отредактирую вопрос. ILya
А точную версию protobuf-net? Marc Gravell♦
Марк, как я и обещал, я сделал обновление моего вопроса сразу после комментария. Вы, вероятно, пропустили это. Версия уже указана, повторяю здесь: Mono-2.10-8 на Windows 7 Professional 64-bit ILya
Проблема конструктора звучит странно и неожиданно - опять же, потребуется повторение; на какой платформе это работает? обычный .NET? или...? (SL и CF имеют некоторые тонкие различия из-за ограничений платформы) Marc Gravell♦

Ваш Ответ

1   ответ
6

ссылк отображается в данных (дважды по одному и тому же пути), а отслеживание равно только включен когда глубина не меньшеRecursionCheckDepth. Это сразу вызывает у меня подозрение в отношении приведенного предела глубины 10-15, хотя это не обязательно тот случай, когда protobuf обрабатывает уровни Довольно так же, как вы рассчитываете. Для меня не имеет смысла, что повышение этого числа до 100 должно заставить его работать - фактически, само существование этогоRecursionCheckDepth - это просто оптимизация, ограничивающая усилия, связанные с «типичными» графами, позволяющая только более тщательно проверять, начинает ли он углубляться.

Я, однако, помнил, что это также может указывать на небольшую ошибку в обработке на основе наследования, возможно, также связанную сAsReference. Я пользуюсь protobuf-net широко и постоянно, и я не видел такой проблемы. Если у вас есть воспроизводимое воспроизведение, я бы очень хотел его увидеть.

Ты на самом деле пытаешься обнаружить Циклов, правильно? Рекурсия кажется совершенно нормальной, хотя она также может быть вредной, если она достаточно глубока. (связанный список кого-нибудь?) Ben Voigt
Спасибо за ваш ответ! Изменение RecursionCheckDepth было лишь временным решением, позволяющим мне двигаться дальше с моим проектом, так что на самом деле это не вариант ... Я подготовлю воспроизводимый пример ошибки завтра и поделюсь им где-нибудь. ILya
@ BenVoigt да, проблема заключается в циклах (так как это сериализатор на основе дерева с ограниченной опциональной поддержкой отслеживания ссылок) Marc Gravell♦
@ Marc: Тогда об ошибке и документации, вероятно, стоит поговорить о Циклов, как в «Обнаружен цикл». И смещение глубины рекурс длина цикла. Ben Voigt
Только что решил мою проблему с круговыми ссылками. Я добавил «AsReference = true» в атрибут ProtoMember, когда свойство является свойством навигации. Olivier MATROT

Похожие вопросы