3

正如標題所說,我有一些問題,我的序列化自動生成的POCO對象。但首先一些背景資料:自動生成的POCO系列化與DataContractSerializer的和MetaDataTypeAttribute問題

我一直在使用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,但是後來發現它存在circral引用的問題。我上的使用XmlIgnore這個問題,但後來我有問題序列化:

Public Overridable Property NwlGroup As ICollection(Of NwlGroup) 

因爲XmlSerializer的不支持接口。

第二I試圖的DataContractSerializer使用[DataContract]和[數據成員]在自動生成的實體波索類文件屬性。這工作,但自然我不得不清理自動生成的文件的變化,因此我想使用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,則會按照您會注意的順序排列屬性。如果我從NewsletterCustomerMetadata中將DataContract屬性移動到NewsletterCustomer,那麼我只會在您指定DataContract時沒有DataMember屬性的情況下獲得根節點。

它看起來像的DataContractSerializer不MetaDataType數據註釋工作。

我的問題是:

  1. 我如何可以序列我的POCO類自定義XML?
  2. 如何將[DataContract]和[DataMember]屬性添加到自動生成的POCO類中?
  3. 將自動生成的POCO類序列化爲XML的最佳方式是什麼?

回答

1

DataContractSerializer不從外部元數據類型讀取屬性。並非.NET框架的每個功能都可以與API的其他功能一起使用,特別是新功能通常不適用於舊功能,這就是這種情況。

最好的方法是在IXmlSerializable或DTO中使用自定義序列化,如@Marc建議的那樣。在DataContractSerializer的情況下,您也可以使用IDataContractSurrogateXmlSerializer非常先進的場景是overriding XML serialization

也可以讓T4模板生成你的屬性,但是這是非常先進的技術,因爲它需要兩個步驟:

  • 手動修改EDMX文件(如XML),並添加結構標註成CSDL部分(部分定義您的實體)。結構註釋是自定義的XML元素。反向進程中的Example of using structural annotation(控制SQL生成)。
  • 修改T4模板以加載您的自定義結構註釋,並在課堂生成中使用它們。
+0

感謝您的回答。我想我會看@Marc提出的某種DTO解決方案。 – MLewi 2011-06-08 11:44:11

2

首先,我試着使用XmlSerializer,但後來發現它存在circral引用的問題。

嗯,是的:xml是一種樹形格式 - 它不會喜歡循環引用。 DataContractSerializer不允許任何同一水平比XML控制的,所以我的建議是:堅持XmlSerializer在這種情況下,和(與父性的幾個[XmlIgnore]通常)刪除您的循環引用

否則:執行IXmlSerializable,但請注意,這實際上不提供元數據,並且確實很難做到。

+0

我嘗試使用XmlSerializer的與XmlIgnore,但後來我得到了與序列化自動生成問題,接口類型的房產: 公衆可重寫屬性NwlGroup作爲ICollection的(中NwlGroup) 獲取錯誤XmlSerializer的不支持接口。 – MLewi 2011-06-08 10:33:49

+0

@MLewi在序列化時往往需要轉移到DTO模型 - 即更簡單的模型,這是一種更直接映射到序列化格式的模型 – 2011-06-08 10:37:38

+0

謝謝我將介紹某種DTO解決方案。 – MLewi 2011-06-08 11:46:09

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