Отслеживание ссылок в словарях не работает при использовании Surrogates с protobuf-net - PullRequest
1 голос
/ 11 июля 2011

Я не хочу путать наши BusinessObjects с какой-либо логикой сериализации.Вот почему я хотел внедрить Surrogate для каждого BusinessObject, который инкапсулирует Данные, необходимые для сериализации и десериализации BusinessObject.Кроме того, он содержит всю бизнес-логику, которая должна выполняться после сериализации.

Однако в словарях, похоже, есть проблема с отслеживанием ссылок.Рассмотрим следующий пример:

   //DataContracts:


        public class SerializeClass
        {
            public Dictionary<string, SerializeDictionaryItem> MyDictionary { get; set; }
            public List<SerializeDictionaryItem> MyList { get; set; }
        }

        [ProtoContract]
        public class SerializeDictionaryItem
        {
            [ProtoMember(1)]
            public string MyField { get; set; }
        }

            [ProtoContract(SkipConstructor = true)]
            public class SerializeClassSurrogate
            {
                [ProtoMember(1000, AsReference = true)]
                public Dictionary<string, SerializeDictionaryItem> MyDictionary { get; set; }

                [ProtoMember(1001, AsReference = true)]
                public List<SerializeDictionaryItem> MyList { get; set; }

                public static implicit operator SerializeClass(SerializeClassSurrogate surrogate)
                {
                    if (surrogate == null)
                        return null;

                    var serializeClass = new SerializeClass();
                    serializeClass.MyDictionary = surrogate.MyDictionary;
                    serializeClass.MyList = surrogate.MyList;

                    return serializeClass;
                }

                public static implicit operator SerializeClassSurrogate(SerializeClass serializeClass)
                {
                    if (serializeClass == null)
                        return null;

                    var surrogate = new SerializeClassSurrogate();
                    surrogate.MyDictionary = serializeClass.MyDictionary;
                    surrogate.MyList = serializeClass.MyList;

                    return surrogate;
                }
            }


    //Serialization Logic:
RuntimeTypeModel.Default[typeof(SerializeClass)].SetSurrogate(typeof(Surrogates.SerializeClassSurrogate));

                var myDictionaryItem = new SerializeDictionaryItem();
                myDictionaryItem.MyField = "ABC";

                SerializeClass m = new SerializeClass() {MyDictionary = new Dictionary<string, SerializeDictionaryItem>()};
                m.MyDictionary.Add("def", myDictionaryItem);
                m.MyDictionary.Add("abc", myDictionaryItem);

                m.MyList = new List<SerializeDictionaryItem>();
                m.MyList.Add(myDictionaryItem);
                m.MyList.Add(myDictionaryItem);

                using (var writer = new StreamWriter(OutputDir + "proto.bin"))
                {
                    Serializer.Serialize(writer.BaseStream, m);
                }

                using(var reader = new StreamReader(OutputDir + "proto.bin"))
                {
                    var deserialized = Serializer.Deserialize<SerializeClass>(reader.BaseStream);
                    var areEqualInDictionary = deserialized.MyDictionary["def"] == deserialized.MyDictionary["abc"];
                    var areEqualInList = deserialized.MyList[0] == deserialized.MyList[1];
                }

Два логических значения areEqualIn ... должны быть истинными после десериализации.Однако я получаю следующее исключение во время десериализации:

"Объект, отслеживаемый ссылками, изменил ссылку во время десериализации"

Это ошибка или я что-то не так делаю?

/РЕДАКТИРОВАТЬ: Это, похоже, не имеет ничего общего с использованием суррогатов.Если я попробую сериализацию без них и просто заменим SerializeClass на ProtoContract и его членов соответственно на ProtoMembers, то возникнет точно такая же проблема.

Кстати.Я использую protobuf-net v2 429

/ EDIT2: Это исключение также можно спровоцировать с помощью этого кода, который вообще не нуждается в словарях:

    public class SerializeClass
    {
        public string MyField { get; set; }

        public SerializeClass GroupTrailerRef { get; set; }
    }

        [ProtoContract(SkipConstructor = true)]
        public class SerializeClassSurrogate
        {
            [ProtoMember(1001)]
            public string MyField { get; set; }

            [ProtoMember(1002, AsReference = true)]
            public SerializeClass GroupTrailerRef { get; set; }

            public static implicit operator SerializeClass(SerializeClassSurrogate surrogate)
            {
                if (surrogate == null)
                    return null;

                var serializeClass = new SerializeClass();
                serializeClass.MyField = surrogate.MyField;
                serializeClass.GroupTrailerRef = surrogate.GroupTrailerRef;

                return serializeClass;
            }

            public static implicit operator SerializeClassSurrogate(SerializeClass serializeClass)
            {
                if (serializeClass == null)
                    return null;

                var surrogate = new SerializeClassSurrogate();
                surrogate.MyField = serializeClass.MyField;
                surrogate.GroupTrailerRef = serializeClass.GroupTrailerRef;

                return surrogate;
            }
        }

RuntimeTypeModel.Default[typeof(SerializeClass)].SetSurrogate(typeof(Surrogates.SerializeClassSurrogate));

//Serialization Code:
SerializeClass groupTrailer = new SerializeClass();
            groupTrailer.MyField = "Group";

            SerializeClass m = new SerializeClass() {};
            m.GroupTrailerRef = groupTrailer;
            m.MyField = "First";

            SerializeClass k = new SerializeClass();
            k.GroupTrailerRef = groupTrailer;
            k.MyField = "Second";


            var lst = new List<SerializeClass>();
            lst.Add(m);
            lst.Add(k);

            using (var writer = new StreamWriter(OutputDir + "proto.bin"))
            {
                Serializer.Serialize(writer.BaseStream, lst);
            }

            using(var reader = new StreamReader(OutputDir + "proto.bin"))
            {
                var deserialized = Serializer.Deserialize<List<SerializeClass>>(reader.BaseStream);
                var areEqual = deserialized[0].GroupTrailerRef == deserialized[1].GroupTrailerRef;
            }

Что вы предлагаете?Мне нравится суррогатный метод для отделения логики сериализации от моих бизнес-объектов.Как вы думаете, я должен просто придерживаться "нормального" пути?Однако в этом случае я снова застрял из-за проблемы 203 ...

Спасибо, TH

...