正如标题所说,我在序列化自动生成的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数据注释。
我的问题是:
答案 0 :(得分:2)
首先我尝试了XmlSerializer,但后来我发现它有圆周参考的问题。
嗯,是的:xml是树格式 - 它不会喜欢循环引用。 DataContractSerializer
不允许任何喜欢对xml具有相同级别的控制权,所以我的建议是:在这种情况下坚持XmlSerializer
,然后 删除您的循环引用 (通常在父属性上有少量[XmlIgnore]
。)
否则:实施IXmlSerializable
,但请注意,这几乎不提供任何元数据,这是一个非常痛苦的做法。
答案 1 :(得分:1)
DataContractSerializer
不读取外部元数据类型的属性。并非.NET框架的每个功能都与API的其余部分一起使用,特别是较新的功能通常不适用于旧的功能,这正是这种情况。
最好的方法是使用IXmlSerializable
中的自定义序列化或@Marc建议的DTO。如果是DataContractSerializer
,您还可以使用IDataContractSurrogate
。 XmlSerializer
的非常高级的方案是overriding XML serialization。
也可以让T4模板为您生成属性,但这是非常先进的技术,因为它需要两个步骤:
答案 2 :(得分: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);
}