1

Я пытаюсь поддерживать передовую совместимость для сериализатора контрактных данных. У меня есть проблемы с:Serializer данных Контракт Совместимость свойств, на которые ссылаются

Если у вас есть объект, который сохраняется как ссылка на свойство, которое добавлено в более позднюю версию внутри известного типа, оно станет исключением. Обратите внимание, что оба этих типа известны в обеих версиях. Единственное, что является новым, это свойство внутри одного из объектов.

Я приложил простое моделирование задачи на samples и samples:

Она состоит из двух различных проектов: V1, который является более старой версией, которая уже развернутой. V2, который является более новой версией V1. V2 сохраняет свои данные, а V1 должен иметь возможность загружать сохраненные данные V2 для поддержки прямой совместимости.

Существует три пользовательских типа: Люди: имеет две ссылки на объекты, а Person и AnotherPerson сохраняются в них.

В V1 и V2:

[DataContract(Name = "People", Namespace = "Tests.FCTests")] 
[KnownType(typeof(Person))] 
[KnownType(typeof(AnotherPerson))] 
public class People : IExtensibleDataObject 
{ 
    [DataMember] 
    public object Person { get; set; } 

    [DataMember] 
    public object AnotherPerson { get; set; } 

    public ExtensionDataObject ExtensionData { get; set; } 
} 

лицо: имеет имя.

В V1 и V2:

[DataContract(Name = "Person", Namespace = "Tests.FCTests")] 
public class Person : IExtensibleDataObject 
{ 
    [DataMember] 
    public string Name { get; set; } 

    public ExtensionDataObject ExtensionData { get; set; } 

} 

AnotherPerson: имеет имя и в V2 была добавлена ​​ссылка на Человека (FriendPerson).

В V1:

[DataContract(Name = "AnotherPerson", Namespace = "Tests.FCTests")] 
public class AnotherPerson : IExtensibleDataObject 
{ 
    [DataMember] 
    public string Name { get; set; } 

    public ExtensionDataObject ExtensionData { get; set; } 
} 

в V2:

[DataContract(Name = "AnotherPerson", Namespace = "Tests.FCTests")] 
public class AnotherPerson : IExtensibleDataObject 
{ 
    [DataMember] 
    public string Name { get; set; } 

    /* This is added in this version */ 
    [DataMember] 
    public Person FriendPerson { get; set; } 

    public ExtensionDataObject ExtensionData { get; set; } 
} 

Вариант 2 сохраняет данные:

static void Main(string[] args) 
    { 
     DataContractSerializer serializer = new DataContractSerializer(typeof(People), null, int.MaxValue, false, true, null, null); 

     var people = new People(); 
     var person = new Person() { Name = "Person" }; 
     var anotherPerson = new AnotherPerson() { Name = "AnotherPerson", FriendPerson = person }; 

     people.Person = person; 
     people.AnotherPerson = anotherPerson; 

     using (var writer = new XmlTextWriter("../../../../SavedFiles/Version2Saved.xml", null) { Formatting = Formatting.Indented }) 
     { 
      serializer.WriteObject(writer, people); 
      writer.Flush(); 
     } 

     Console.WriteLine("Save Successfull."); 
     Console.ReadKey(); 
    } 

Version 1 загружает те же данные:

static void Main(string[] args) 
    { 
     DataContractSerializer serializer = new DataContractSerializer(typeof(People), null, int.MaxValue, false, true, null, null); 

     People loadedPeople; 

     using (var reader = new XmlTextReader("../../../../SavedFiles/Version2Saved.xml")) 
     { 
      loadedPeople = (People)serializer.ReadObject(reader); 
     } 

     Console.WriteLine("Load Successful."); 

     Console.ReadKey(); 
    } 

Сохраненные данные:

<People xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="Tests.FCTests"> 
    <AnotherPerson z:Id="2" i:type="AnotherPerson"> 
    <FriendPerson z:Id="3"> 
     <Name z:Id="4">Person</Name> 
    </FriendPerson> 
    <Name z:Id="5">AnotherPerson</Name> 
    </AnotherPerson> 
    <Person z:Ref="3" i:nil="true" /> 
</People> 

Когда V1 пытается загрузить данные этого генерируется исключение:

{System.Runtime.Serialization.SerializationException: Element Person from namespace Tests.FCTests cannot have child contents to be deserialized as an object. Please use XmlNode[] to deserialize this pattern of XML. ---> System.Xml.XmlException: 'Element' is an invalid XmlNodeType. 
    at System.Xml.XmlReader.ReadEndElement() 
    at System.Runtime.Serialization.XmlReaderDelegator.ReadEndElement() 
    at System.Runtime.Serialization.ObjectDataContract.ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext context) 
    --- End of inner exception stack trace --- 
    at System.Runtime.Serialization.ObjectDataContract.ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext context) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, String name, String ns) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.DeserializeFromExtensionData(IDataNode dataNode, Type type, String name, String ns) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.GetExistingObject(String id, Type type, String name, String ns) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.TryHandleNullOrRef(XmlReaderDelegator reader, Type declaredType, String name, String ns, Object& retObj) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, String name, String ns) 
    at ReadPeopleFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[]) 
    at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract) 
    at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns) 
    at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver) 
    at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver) 
    at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlReader reader) 
    at Version1.Program.Main(String[] args) in C:\Users\Administrator\Desktop\Unknown types Test\Version1\Version1\Program.cs:line 17} 

Внутренняя Исключение:

{System.Xml.XmlException: 'Element' is an invalid XmlNodeType. 
    at System.Xml.XmlReader.ReadEndElement() 
    at System.Runtime.Serialization.XmlReaderDelegator.ReadEndElement() 
    at System.Runtime.Serialization.ObjectDataContract.ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext context)} 

Я подозреваю, что ошибка происходит потому, что объект ссылается на тип, который десериализуется внутри объекта расширения и не имеет какого-либо типа. Причина в том, что если вы добавили новый экземпляр Person внутри People и не ссылаетесь на тот же экземпляр внутри AnotherPerson (FriendPerson).

var anotherPerson = new AnotherPerson() { Name = "AnotherPerson", FriendPerson = new Person() }; 

Затем сохраненный файл будет следующим, и все работает просто отлично:

<People xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="Tests.FCTests"> 
    <AnotherPerson z:Id="2" i:type="AnotherPerson"> 
    <FriendPerson z:Id="3"> 
     <Name i:nil="true" /> 
    </FriendPerson> 
    <Name z:Id="4">AnotherPerson</Name> 
    </AnotherPerson> 
    <Person z:Id="5" i:type="Person"> 
    <Name z:Id="6">Person</Name> 
    </Person> 
</People> 

Я попытался решить эту проблему с помощью контракта данных Разрешитель, Добавление известных типов внутри сериализатором динамически и контракт данных Суррогат, но никто из них не работал. Причина в том, что исключение возникает, когда сериализатор де-сериализует FriendPerson, и переопределенные методы внутри суррогата или резольвера до этого не вызываются.

ПРИМЕЧАНИЕ Нам нужно сохранить ссылки на объекты, и удаление их не является вариантом.

ответ

0

я связывал с падающей поддержкой MSDN и через 2 месяца ходит взад и вперед, они ответили:

Мы занимались группой продуктов и официальным слова есть ошибка в IExtensibleDataObject (когда циклические ссылки НА).

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

2

Проблема заключается в порядке полей в V2 контракта с персоналом. Новое поле должно быть добавлено в конце сериализованного документа для того, чтобы быть вперед-совместимыми:

<People xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="Tests.FCTests"> 
    <AnotherPerson z:Id="2" i:type="AnotherPerson"> 
    <FriendPerson z:Id="3"> 
     <Name z:Id="4">Person</Name> 
    </FriendPerson> 
    <Name z:Id="5">AnotherPerson</Name> 
    </AnotherPerson> 
    <Person z:Ref="3" i:nil="true" /> 
</People> 

Обратите внимание, как появляется над «Name» тегом тег «FriendPerson» в приведенном выше XML в " AnotherPerson ". Это будет работать, если ваш объект был сериализовать следующим образом:

<People xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="Tests.FCTests"> 
    <AnotherPerson z:Id="2" i:type="AnotherPerson"> 
    <Name z:Id="5">AnotherPerson</Name> 
    <FriendPerson z:Id="3"> 
     <Name z:Id="4">Person</Name> 
    </FriendPerson> 
    </AnotherPerson> 
    <Person z:Ref="3" i:nil="true" /> 
</People> 

Для достижения этой цели, укажите параметр «Заказ» на DataMemberAttribute имущества «FriendPerson» в V2 «AnotherPerson» класса следующим образом:

[DataContract(Name = "AnotherPerson", Namespace = "Tests.FCTests")] 
public class AnotherPerson : IExtensibleDataObject 
{ 
    [DataMember] 
    public string Name { get; set; } 

    /* This is added in this version */ 
    [DataMember(Order = 2)] 
    public Person FriendPerson { get; set; } 

    public ExtensionDataObject ExtensionData { get; set; } 
} 

Как правило, вы не должны использовать параметр «Заказ» в первой версии вашего datacontract. Для любой более новой версии вы должны указать параметр «Заказ» для любого нового атрибута DataMemberAttribute и увеличить указанный номер вместе с номером версии. Это совершенно законно иметь несколько одинаковых значений параметров «порядок» в одном DataContract, например, как в этом V3:

[DataContract(Name = "AnotherPerson", Namespace = "Tests.FCTests")] 
public class AnotherPerson : IExtensibleDataObject 
{ 
    [DataMember] 
    public string Name { get; set; } 

    /* This is added in this version */ 
    [DataMember(Order = 2)] 
    public Person FriendPerson { get; set; } 

    [DataMember(Order = 3)] 
    public string Remarks { get; set; } 

    [DataMember(Order = 3)] 
    public bool? IsMarried { get; set; } 

    public ExtensionDataObject ExtensionData { get; set; } 
} 

PS: Мой ответ может прийти поздно, но все еще может быть полезным для других ...

+0

Это решит эту проблему, но на самом деле это не общее решение. Что делать, если в V2 Person также добавляется ссылка на AnotherPerson? Тогда использование заказа не может решить проблему. – Arash