【问题标题】:Auto Generated POCO serialization with DataContractSerializer and MetaDataTypeAttribute problem使用 DataContractSerializer 和 MetaDataTypeAttribute 问题自动生成 POCO 序列化
【发布时间】:2011-06-08 08:52:10
【问题描述】:

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

我按照本指南使用 EF 4.0 en 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 类文件中尝试了具有 [DataContract] 和 [DataMember] 属性的 DataContractSerializer。这行得通,但自然我必须从自动生成的文件中清理更改,因此我想使用 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,那么当 DataContract 没有指定 DataMember 属性时,我只会得到根节点。

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

我的问题是:

  1. 如何将我的 POCO 类序列化为 CUSTOM XML?
  2. 如何将 [DataContract] 和 [DataMember] 属性添加到自动生成的 POCO 类中?
  3. 将自动生成的 POCO 类序列化为 XML 的最佳方法是什么?

【问题讨论】:

    标签: .net entity-framework xml-serialization datacontractserializer


    【解决方案1】:

    首先我尝试了 XmlSerializer,但后来我发现它在循环引用方面存在问题。

    嗯,是的:xml 是一种树格式——它不喜欢循环引用。 DataContractSerializer 不允许任何类似的东西对 xml 进行相同级别的控制,所以我的建议:在这种情况下坚持使用 XmlSerializer,并删除你的循环引用(通常在父属性上带有几个[XmlIgnore])。

    否则:实现IXmlSerializable,但请注意,这几乎不提供元数据,并且很难稳健地执行。

    【讨论】:

    • 我尝试将 XmlSerializer 与 XmlIgnore 一起使用,但后来在序列化接口类型的自动生成属性时遇到问题:Public Overridable Property NwlGroup As ICollection(Of NwlGroup) 出现 XmlSerializer 不支持接口的错误。跨度>
    • @MLewi 在序列化时通常需要转移到 DTO 模型 - 即更直接映射到序列化格式的更简单模型
    • 谢谢,我会看看某种 DTO 解决方案。
    【解决方案2】:

    DataContractSerializer 不会从外部元数据类型中读取属性。并非 .NET 框架的所有功能都适用于 API 的其余部分,尤其是较新的功能通常不适用于旧功能,而这正是这种情况。

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

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

    • 手动修改 EDMX 文件(作为 XML)并将结构注释添加到 CSDL 部分(定义实体的部分)。结构注释是自定义 XML 元素。 Example of using structural annotation 在反向过程中(控制 SQL 生成)。
    • 修改 T4 模板以加载您的自定义结构注释并在类生成中使用它们。

    【讨论】:

    • 感谢您的回答。我想我会看看@Marc 提议的某种 DTO 解决方案。
    【解决方案3】:

    您的问题已得到解答,但我在此处添加了我的解决方法,以帮助那些高度依赖 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);
    }
    

    【讨论】:

      猜你喜欢
      • 2011-03-10
      • 2014-01-04
      • 1970-01-01
      • 2017-09-25
      • 1970-01-01
      • 2011-11-21
      • 1970-01-01
      • 2013-03-03
      相关资源
      最近更新 更多