首页 文章

使用DataContractSerializer和MetaDataTypeAttribute问题自动生成POCO序列化

提问于
浏览
3

正如 Headers 所说,我在序列化自动生成的POCO对象时遇到了一些问题 . 但首先是一些背景信息:

我已按照本指南使用EF 4.0和ADO.Net POCO实体生成器创建了数据访问层:http://blogs.msdn.com/b/adonet/archive/2010/01/25/walkthrough-poco-template-for-the-entity-framework.aspx .

我现在有2个类库,一个使用EF模型,第二个使用T4自动生成的POCO实体 .

目前我正在开发另一个项目,我想使用我的DAL类库 . 我必须检索一些对象并将它们序列化为XML . 首先我尝试了XmlSerializer,但后来我发现它有圆周参考的问题 . 我使用XmlIgnore修复了这个问题,但后来我遇到序列化问题:

Public Overridable Property NwlGroup As ICollection(Of NwlGroup)

因为XmlSerializer不支持接口 .

其次,我在自动生成的实体Poco类文件中尝试了DataContractSerializer和[DataContract]和[DataMember]属性 . 这工作,但自然我不得不清理自动生成的文件中的更改,因此我想使用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()

这给了我NewsletterCustomer xml,其中所有的读/写属性都是序列化的,就像没有指定DataContract时那样 . 如果我将DataContract属性从NewsletterCustomerMetadata移动到NewsletterCustomer,那么我只会在没有DataMember属性的情况下指定DataContract时获得根节点 .

看起来DataContractSerializer不适用于MetaDataType数据注释 .

我的问题是:

  • 如何将POCO类序列化为CUSTOM XML?

  • 如何将[DataContract]和[DataMember]属性添加到自动生成的POCO类中?

  • 将自动生成的POCO类序列化为XML的最佳方法是什么?

3 回答

  • 1

    DataContractSerializer 不与旧的合作,这正是这种情况 .

    最好的方法是使用_2575304中的自定义序列化或@Marc建议的DTO . 如果是 DataContractSerializer ,您也可以使用 IDataContractSurrogate . 非常高级的 XmlSerializer 场景是overriding XML serialization .

    也可以让T4模板为您生成属性,但这是非常先进的技术,因为它需要两个步骤:

    • 手动修改EDMX文件(作为XML)并将结构注释添加到CSDL部分(定义实体的部分) . 结构注释是自定义XML元素 . Example of using structural annotation在反向过程中(控制SQL生成) .

    • 修改T4模板以加载自定义结构注释并在类生成中使用它们 .

  • 2

    首先我尝试了XmlSerializer,但后来我发现它有圆周参考的问题 .

    嗯,是的:xml是树格式 - 它不会喜欢循环引用 . DataContractSerializer 不允许对xml进行相同级别的控制,所以我的建议是:在这种情况下坚持 XmlSerializer ,并且 remove your circular references (通常在父属性上有一些 [XmlIgnore] ) .

    否则:实现 IXmlSerializable ,但请注意,这几乎不提供任何元数据,并且是一个非常痛苦的做法 .

  • 1

    您的问题已得到解答,但我在此处添加了我的解决方法,以帮助那些高度依赖XML序列化的人 . 当然,循环引用并不适合XML,但令人惊讶的是,它完全取代了XML .

    我使用Code First并从EDMX生成POCO类 . 我自定义T4模板,使每个实体类都有ToXmlElement和FromXmlElement .

    这些功能有能力:

    • 完全丢弃循环引用 .

    • 包含预定义数量的循环依赖关系 .

    这是代码 . 不要担心晦涩的名字 . 图示的重点是,这是所有生成的代码,它处理本机类型,复杂类型,外键和子对象(多端) . 最后,它不会与EF对您的POCO类强加的FixUpCollection冲突 .

    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);
    }
    

相关问题