1
votes

I've got a tree-structure-class called ConfigNode (similar to SerializationInfo), which can store configuration values in a list and controls child nodes. When I add IEnumerable<ConfigNode> as interface to derive from, protobuf-net fails during serialization and causes a StackOverFlowException, even with the IgnoreListHandling flag set to true.

[Serializable, DataContract, ProtoContract(IgnoreListHandling = true)]
public class ConfigNode : Entity, ICloneable, INotifyPropertyChanged
{
    [DataMember, ProtoMember(1)]
    private Dictionary<String, PrimitiveSurrogate> values = new Dictionary<String, PrimitiveSurrogate>();

    private Dictionary<String, ConfigNode> _Childs = new Dictionary<String, ConfigNode>();

    [DataMember, ProtoMember(2)]
    public Dictionary<String, ConfigNode> Childs
    {
        get
        {
            return _Childs;
        }
        private set
        {
            _Childs = value;
        }
    }

    [DataMember, ProtoMember(3)]
    public ConfigNode Parent { get; set; }
}

is working fine. PrimitiveSurrogate is a struct storing nullables of all commonly used "almost-primitives" like String, Guid, DataTime, float / double, bool, char, etc. The config values themselves will be added in a "Store"-method which is of no importance. It takes an object as parameter and tries to cast the type to one of the storable data types and stores it the strongly typed PrimitiveSurrogate. The entity base-class just provides a Name-property, nothing else.

But as soon as I add IEnumerable<ConfigNode> to the list of interfaces and add the appropriate methods (see below), any serialization attempts throw a StackOverflowException.

    public void Add(ConfigNode child)
    {
        if (child == null)
            throw new ArgumentNullException("child");
        if (child.Name == null)
            throw new ArgumentException("The child's name was null. The child cannot be added.");

        child.Parent = this;
        lock (this.Childs)
        {
            this.Childs[child.Name] = child;
        }
    }

    public IEnumerator<ConfigNode> GetEnumerator()
    {
        lock (this.Childs)
        {
            return this.Childs.Values.Clone().GetEnumerator();
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        lock (this.Childs)
        {
            return this.Childs.Values.Clone().GetEnumerator();
        }
    }

"Clone" is an extension method and looks like this:

    public static IEnumerable<T> Clone<T>(this IEnumerable<T> collection)
    {
        return collection.ToList();
    }

It seems like protobuf-net ignores the IgnoreListHandling-flag, can anyone help?

1

1 Answers

0
votes

Nevermind, I found the mistake. After some changes I introduced a lock in the Childs-Property and in Childs.get() I was locking the property itself and not the backing field. This caused a StackOverflow due to the Monitor-Class trying to access the property and causing the get-Accessor to be accessed again.

It now (de)serializes smoothly.

Fixed version:

[DataMember, ProtoMember(2)]
public Dictionary<String, ConfigNode> Childs
{
    get
    {
        lock (_Childs)
        {
            return _Childs;
        }
    }
    private set
    {
        lock (_Childs)
        {
            _Childs = value;
        }
    }
}