Автоматически сгенерированная POCO-сериализация с проблемами DataContractSerializer и MetaDataTypeAttribute - PullRequest
3 голосов
/ 08 июня 2011

Как видно из названия, у меня возникли некоторые проблемы с сериализацией моих автоматически сгенерированных объектов POCO.Но сначала некоторая справочная информация:

Я создал свой уровень доступа к данным, используя EF 4.0 en ADO.Net POCO Entity Generator, следуя этому руководству: http://blogs.msdn.com/b/adonet/archive/2010/01/25/walkthrough-poco-template-for-the-entity-framework.aspx.

У меня сейчас 2библиотеки классов, одна с моделью EF, вторая с объектами POCO, сгенерированными автоматическими T4.

В настоящее время я работаю над другим проектом, в котором я хочу использовать свои библиотеки классов DAL.Я должен получить некоторые объекты и сериализовать их в XML.Сначала я попробовал XmlSerializer, но потом обнаружил, что у него проблемы с циркулярными ссылками.Я исправил эту проблему с помощью XmlIgnore, но затем у меня возникла проблема с сериализацией:

Public Overridable Property NwlGroup As ICollection(Of NwlGroup) 

Поскольку XmlSerializer не поддерживает интерфейсы.

Во-вторых, я попробовал DataContractSerializer с атрибутами [DataContract] и [DataMember]в автоматически сгенерированном файле Poco Class.Это сработало, но, естественно, мне пришлось очистить изменения от автоматически сгенерированного файла, поэтому я хотел использовать атрибут MetaDataType.Я создал дополнительный файл, как это:

Imports System.Runtime.Serialization
Imports System.ComponentModel.DataAnnotations

<MetadataType(GetType(NewsletterCustomerMetadata))>
Partial Public Class NewsletterCustomer
End Class

<DataContract()
Public Class NewsletterCustomerMetadata

    <DataMember(Name:="emailaddress", IsRequired:=True)>
    Public Overridable Property Emailaddress As String

    <DataMember(Name:="name")>
    Public Overridable Property Name As String

    <DataMember()>
    Public Overridable Property NwlGroup As ICollection(Of NwlGroup)
End Class

Автоматически сгенерированный файл:

'------------------------------------------------------------------------------
' <auto-generated>
'     This code was generated from a template.
'
'     Changes to this file may cause incorrect behavior and will be lost if
'     the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------
Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Collections.Specialized
Imports System.Runtime.Serialization



Public Class NewsletterCustomer
#Region "Primitive Properties"

    Public Overridable Property ID As Integer

    Public Overridable Property Emailaddress As String

    Public Overridable Property Name As String

...

#Region "Navigation Properties"
    Public Overridable Property NwlGroup As ICollection(Of NwlGroup)
        Get
            If _nwlGroup Is Nothing Then
                Dim newCollection As New FixupCollection(Of NwlGroup)
                AddHandler newCollection.CollectionChanged, AddressOf FixupNwlGroup
                _nwlGroup = newCollection
            End If
            Return _nwlGroup
        End Get
        Set(ByVal value As ICollection(Of NwlGroup))
            If _nwlGroup IsNot value Then
                Dim previousValue As FixupCollection(Of NwlGroup) = TryCast(_nwlGroup, FixupCollection(Of NwlGroup))
                If previousValue IsNot Nothing Then
                    RemoveHandler previousValue.CollectionChanged, AddressOf FixupNwlGroup
                End If
                _nwlGroup = value
                Dim newValue As FixupCollection(Of NwlGroup) = TryCast(value, FixupCollection(Of NwlGroup))
                If newValue IsNot Nothing Then
                    AddHandler newValue.CollectionChanged, AddressOf FixupNwlGroup
                End If
            End If
        End Set
    End Property
    Private _nwlGroup As ICollection(Of NwlGroup)

...
End Class

Затем я пытаюсь сериализовать его в XML

    Dim ctx = New ModelEntities(_connectionString)
       ctx.ContextOptions.ProxyCreationEnabled = False
       ctx.ContextOptions.LazyLoadingEnabled = False

    Dim customers = From c In ctx.NwlCustomer
                    Select c
                   Where c.SiID = 99

    Dim filename As String = "C:\test.txt"
    Dim result As NewsletterCustomer = customers.ToList.FirstOrDefault
    Dim writer As New FileStream(filename, FileMode.Create)
    Dim ser As New DataContractSerializer(GetType(NewsletterCustomer))
    ser.WriteObject(writer, customers.ToList.FirstOrDefault)
    writer.Close()

Это дало мне NewsletterCustomerXML со всеми свойствами чтения / записи, сериализованными так, как вы ожидаете, если не указан DataContract.Если я перемещу атрибут DataContract из NewsletterCustomerMetadata в NewsletterCustomer, то я получу только корневой узел, как вы ожидаете, когда DataContract указан без атрибутов DataMember.

Похоже, DataContractSerializer не работает с аннотациями данных MetaDataType.

Мои вопросы:

  1. Как я могу сериализовать мои классы POCO в CUSTOM XML?
  2. Как добавить атрибуты [DataContract] и [DataMember] в autoсгенерированные классы POCO?
  3. Каков наилучший способ сериализации автоматически сгенерированных классов POCO в XML?

Ответы [ 3 ]

2 голосов
/ 08 июня 2011

Сначала я попробовал XmlSerializer, но потом обнаружил, что у него проблемы с циркулярными ссылками.

Хорошо, да: xml - это древовидный формат - он не будет любить циклические ссылки. DataContractSerializer ничего не разрешает как такой же уровень контроля над xml, поэтому мой совет: придерживайтесь XmlSerializer в этом случае, и удалите ваши циклические ссылки (обычно с несколькими [XmlIgnore] на родительских свойствах).

В противном случае: внедрите IXmlSerializable, но имейте в виду, что это практически не дает метаданных и реальную трудность делать надежно.

1 голос
/ 17 мая 2012

На ваш вопрос ответили, но я добавляю здесь мой обходной путь для тех, кто сильно зависит от XML-сериализации. Циркулярные ссылки, конечно, не очень подходят для XML, но удивительно, что это полностью отвлекает людей от XML.

Я использую Code First и генерирую классы POCO из EDMX. Я настраиваю шаблон T4 так, чтобы каждый класс сущности имел ToXmlElement и FromXmlElement.

Функции имеют возможность:

  • Откажитесь от циклических ссылок полностью.
  • Включить предопределенное количество уровней циклических зависимостей.

Вот код. Не беспокойтесь о неизвестных именах. Смысл иллюстрации в том, что это весь сгенерированный код, который обрабатывает собственные типы, сложные типы, внешние ключи и дочерние объекты (многие заканчиваются). Наконец, он не конфликтует с FixUpCollection, которую EF накладывает на ваши классы POCO.

public System.Xml.XmlElement ToXmlElement (System.Xml.XmlDocument document)
{
    return (this.ToXmlElement(document, 3, new System.Collections.Generic.List<object>()));
}

public System.Xml.XmlElement ToXmlElement (System.Xml.XmlDocument document, int level)
{
    return (this.ToXmlElement(document, level, new System.Collections.Generic.List<object>()));
}

public System.Xml.XmlElement ToXmlElement (System.Xml.XmlDocument document, int level, System.Collections.Generic.List<object> collection)
{
    System.Xml.XmlElement element = null;

    collection.Add(this);

    element = document.CreateElement(System.Data.Objects.ObjectContext.GetObjectType(this.GetType()).Name);

    // Native Types.
    element.Attributes.Append(document, "Id", this.Id.ToString());
    element.Attributes.Append(document, "Assessment_StudentId", this.Assessment_StudentId.ToString());

    // Complex Types.

    // Foreign Keys.
    if (!collection.Contains(this.Assessment_Student) && level > 0) { element.AppendChild(this.Assessment_Student.ToXmlElement(document, level - 1, collection)); }
    if (!collection.Contains(this.PackageServer) && level > 0) { element.AppendChild(this.PackageServer.ToXmlElement(document, level - 1, collection)); }
    if (!collection.Contains(this.PackageClient) && level > 0) { element.AppendChild(this.PackageClient.ToXmlElement(document, level - 1, collection)); }

    // Child Objects.
    foreach (Core.SessionTasks _SessionTasks in this.SessionTasks)
    {
        if (!collection.Contains(_SessionTasks) && level > 0)
        {
            collection.Add(_SessionTasks);
            element.AppendChild(_SessionTasks.ToXmlElement(document, level - 1, collection));
        }
    }

    return (element);
}

public bool FromXmlElement (System.Xml.XmlElement element)
{
    bool result = true;

    //this.InitializeData();

    // Native Types.
    this.Id = int.Parse(element.Attributes ["Id"].Value);
    this.Assessment_StudentId = int.Parse(element.Attributes ["Assessment_StudentId"].Value);

    // Complex Types.

    // Foreign Keys.
    Core.Assessment_Student __Assessment_Student = new Core.Assessment_Student();
    if (element ["Assessment_Student"] != null)
    {
        __Assessment_Student.FromXmlElement(element ["Assessment_Student"]);
        this.Assessment_Student = __Assessment_Student;
    }

    Core.PackageServer __PackageServer = new Core.PackageServer();
    if (element ["PackageServer"] != null)
    {
        __PackageServer.FromXmlElement(element ["PackageServer"]);
        this.PackageServer = __PackageServer;
    }

    Core.PackageClient __PackageClient = new Core.PackageClient();
    if (element ["PackageClient"] != null)
    {
        __PackageClient.FromXmlElement(element ["PackageClient"]);
        this.PackageClient = __PackageClient;
    }

    // Child Objects.
    this.SessionTasks.FromXmlElement(element ["SessionTasks"]);

    return (result);
}
1 голос
/ 08 июня 2011

DataContractSerializer не читает атрибуты из внешнего типа метаданных. Не каждая функция .NET Framework работает с остальным API, особенно новые функции обычно не работают со старыми, и это именно тот случай.

Лучший способ - использовать пользовательскую сериализацию в IXmlSerializable или DTO, как предложено @Marc. В случае DataContractSerializer вы также можете использовать IDataContractSurrogate. Очень продвинутый сценарий XmlSerializer - это переопределение сериализации XML .

Также возможно позволить шаблону T4 генерировать атрибуты для вас, но это очень продвинутый метод, потому что он требует двух шагов:

  • Вручную измените файл EDMX (как XML) и добавьте структурные аннотации в часть CSDL (часть, определяющую ваши сущности). Структурные аннотации - это пользовательские элементы XML. Пример использования структурной аннотации в обратном процессе (управление генерацией SQL).
  • Модифицируйте шаблон T4, чтобы загружать ваши собственные структурные аннотации и использовать их при генерации классов.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...