ncupper / protobuf-net

Automatically exported from code.google.com/p/protobuf-net
Other
0 stars 0 forks source link

Nested dynamic types causes "Internal error; a key mismatch occurred" #314

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?

    using System;
    using System.Collections.ObjectModel;
    using System.IO;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    using ProtoBuf.Meta;

    [TestClass]
    public class ProtobufTest
    {
        [TestMethod]
        public void DynamicCollection()
        {
            var rtm = TypeModel.Create();

            var wrapper = rtm.Add(typeof(Wrapper), false);
            var fw = wrapper.AddField(1, "Value");
            fw.DynamicType = true;

            var outer = rtm.Add(typeof(OuterContract), false);
            outer.Add(1, "Id");
            var f = outer.AddField(2, "Collection");
            f.DynamicType = true;

            var inner1 = rtm.Add(typeof(Inner1Contract), false);
            inner1.Add(1, "Id");
            inner1.Add(2, "Value");

            var inner2 = rtm.Add(typeof(Inner2Contract), false);
            inner2.Add(1, "Id");
            inner2.Add(2, "Text");

            var test = new OuterContract
            {
                Id = Guid.NewGuid(),
                Collection =
                    new Collection<InnerBaseContract>
                    {
                        new Inner1Contract { Id = Guid.NewGuid(), Value = 123 },
                        new Inner1Contract { Id = Guid.NewGuid(), Value = 456 },
                        new Inner2Contract { Id = Guid.NewGuid(), Text = "one two three" }
                    }
            };

            var ms = new MemoryStream();
            rtm.Serialize(ms, new Wrapper { Value = test });

            ms.Position = 0;
            var result = rtm.Deserialize(ms, null, typeof(Wrapper));
        }
    }

    public class Wrapper
    {
        public object Value { get; set; }
    }

    public class OuterContract
    {
        public Guid Id { get; set; }

        public Collection<InnerBaseContract> Collection { get; set; }
    }

    public class InnerBaseContract
    {
        public Guid Id { get; set; }
    }

    public class Inner1Contract : InnerBaseContract
    {
        public int Value { get; set; }
    }

    public class Inner2Contract : InnerBaseContract
    {
        public string Text { get; set; }
    }

What is the expected output? What do you see instead?

Test should complete without error. Instead I get a ProtoException("Internal 
error; a key mismatch occurred") in NetObjectCache.cs Line 62.

What version of the product are you using? On what operating system?

Using r580

Please provide any additional information below.

The bug appears to be the order that the items are added into the NetCache. 
I moved the SetKeyedObject call in ReadNetObject to before the ReadTypedObject 
call (see patch) which fixes it for me.

Original issue reported on code.google.com by ste...@berry.asn.au on 20 Aug 2012 at 4:18

Attachments:

GoogleCodeExporter commented 9 years ago
I seem to be experiencing the same issue.  Are there any conditions where this 
exception is expected? For example, could overlap of keys for a type's 
ProtoMember declarations be an issue?

Original comment by jeff.a.z...@gmail.com on 27 Aug 2012 at 1:56

GoogleCodeExporter commented 9 years ago
This bug occured to me recently, and I could only debug it by using customized 
builds of protobuf-net.

The reason: at serialization-time protobuf net assigns ids from a 'sequence' to 
your dynamic types. The sequence always starts at 1, it's actually a list-index 
in the NetObjectCache class. Your dyn.types receive ids in the order of 
traversal (first come-first assigned). The ordering isn't fixed! Something 
similar is written to the outfile:

[typeid=1, actualtype=YourOuterConcreteType]
[typeid=2, actualtype=YourInnerConcreteType]

At deserialization for some reason always my inner type came first, not the 
outer container. protobuf-net detected that it's a YourInnerConcreteType of 
typekey 2, and wanted to put it into NetObjectCache's list with an index of 2. 
But the list was empty, the index was supposed to be 1 hence the exception. 

The solution: nested dynamictypes doesn't work, or not always work, so I had to 
specify the concrete type for my Outer class.

Hope this helps.

Original comment by akos.lov...@gmail.com on 26 Sep 2013 at 2:23