leozvc / protobuf-net

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

Surrogates dont work with AsReference=true (exception: A reference-tracked object changed reference during deserialization) #352

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
My email: ishine@mail.ru
Hello, Marc!

What steps will reproduce the problem?
Even simple surrogate dont work with AsReference:

[ProtoContract]
public class B_Surr
{
    public static implicit operator B (B_Surr s)
    {
        if ( s == null )
            return              null;

        return new B();
    }

    public static implicit operator B_Surr (B b)
    {
        if ( b == null )
            return                  null;

        var s                   =   new B_Surr();
        return                  s;
    }
}

public class B
{
}

[ProtoContract]
public class A
{
    [ProtoMember(1, AsReference = true)]
    public B b;
}

class Program
{
    static void Main()
    {
        RuntimeTypeModel.Default.Add(typeof(B), true).SetSurrogate(typeof(B_Surr));
        Serializer.DeepClone        (new A() { b = new B() } );
    }
}

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

exception: A reference-tracked object changed reference during deserialization

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

r622, Win7 x64 

Please provide any additional information below.

The problem is that once surrogate is read from stream, it is placed in 
netCache list
and only then is converted to final class, then when its compared to one in 
netCache its ofcourse different : surrogate and final class are of different 
types!
Temporarily I changed code so it updates value in netCache, overwrite it with 
final class value. But this dont work in all cases, i.e.: when some of fields 
store parent reference (they still find it as surrogate), so better fix is 
needed: probably storing right final class object in netCache list from the 
start.

Thanks. Sergey.

Original issue reported on code.google.com by ish...@mail.ru on 23 Jan 2013 at 6:39

GoogleCodeExporter commented 9 years ago
This problem also affects serialization of surrogates with AsReference=true.
When we store surrogate which have field that references back to it, it doesnt 
detect it as same reference (because it compares stored in cache surrogate with 
final class), so it tries to store it once more. 
Heres a repro code, posting it here, because this two problems are closely 
related.

[ProtoContract]
public class B_Surr
{
    public static implicit operator B (B_Surr s)
    {
        if ( s == null )
            return                  null;

        var b                   =   new B();
        b.reference             =   s.reference;

        return                      b;
    }

    public static implicit operator B_Surr (B b)
    {
        if ( b == null )
            return                  null;

        var s                   =   new B_Surr();
        s.reference             =   b.reference;
        return                      s;
    }

    [ProtoMember(1, AsReference = true)]
    B reference;
}

public class B
{
    public B reference;
}

[ProtoContract]
public class A
{
    [ProtoMember(1, AsReference = true)]
    public B b;
}

class Program
{
    static void Main()
    {
        RuntimeTypeModel.Default.Add(typeof(B), true).SetSurrogate(typeof(B_Surr));

        B b =   new B();
        b.reference = new B();
        b.reference.reference = b;

        Serializer.DeepClone        (new A() { b = b } );
    }
}

gives Invalid Cast Exception from "B_Surr" to "B"

Thanks. Sergey

Original comment by ish...@mail.ru on 23 Jan 2013 at 7:09