将带有子项的Entity Framework对象序列化为XML文件

6

我正在使用Entity Framework查询具有父/子结果集的数据,并希望将此数据导出到XML文档。

var agreement = storeops.Agreements.SingleOrDefault(a => a.AgreementNumber == AgreementTextBox.Text);
XmlSerializer serializer = new XmlSerializer(agreement.GetType());
XmlWriter writer = XmlWriter.Create("Agreement.xml");
serializer.Serialize(writer, agreement);

这个方法很好用,但是它只将父对象序列化到XML中,没有包含相关的子记录。如何让子对象也被序列化到XML中呢?
我还尝试过使用POCO生成的代码,但是子集合会尝试序列化,但它们是不能序列化的ICollections。
无法序列化成员DataSnapshots.Agreement.AgreementItems,因为它是一个接口类型System.Collections.Generic.ICollection`1[[DataSnapshots.AgreementItem, DataSnapshots, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]。
2个回答

11

当处理Entity Framework实体时,XML序列化与二进制序列化和数据契约序列化的行为不同。后者将序列化加载到对象图中的任何相关对象,但XML序列化不会这样做,因此您需要使用DataContractSerializer

var agreement = storeops.Agreements.SingleOrDefault(a => a.AgreementNumber == AgreementTextBox.Text);
// make sure any relations are loaded

using (XmlWriter writer = XmlWriter.Create("Agreement.xml"))
{
    DataContractSerializer serializer = new DataContractSerializer(agreement.GetType());
    serializer.WriteObject(writer, agreement);
}
此外,Entity Framework 默认情况下对于1:Many关系使用延迟加载,如果在序列化时引用的对象尚未加载,则只会得到指向它们的键。您必须显式地加载相关实体,可以通过调用 agreement.Children.Load() 或在查询中使用 .Include("Children")(其中"Children"是相关实体集合的名称)来实现。

我实际上正在获取子项,因为它们的计数大于0,但由于某种原因它们没有被XmlSerializer序列化。我猜测这是因为子对象并不真正是一个集合类,而是一个不继承任何集合类的EntityCollection。 - Brett Mathe
1
EntityCollection 被定义为 public sealed class EntityCollection<TEntity> : RelatedEnd, ICollection<TEntity>, IEnumerable<TEntity>, IEnumerable, IListSource,因此它绝对是一个集合。 - Joel C
好的,我明白了。那么为什么子集合没有被序列化到XML文档中呢?除了子集合之外,所有其他属性都已经存在并加载完毕。我之所以说EntityCollection不像其他集合一样是因为我无法使用[]引用子元素。我认为正是因为这个原因,子元素没有被序列化。 - Brett Mathe

0

我终于找到了解决方案,但需要编辑生成的类 :(

创建POCO生成的实体类,将Lazy Loading设置为true,这将获取父项和所有子项,只需一个选择(无需使用Include或Load)。

在父类中,将子访问器类型从ICollection修改为FixupCollection。

public virtual FixupCollection AgreementItemLogs

然后在XmlSerializer中,您必须指定来自代理类的父类型和子类型。

var agreement = storeops.Agreements.Include("AgreementItems").SingleOrDefault(a => a.AgreementNumber == AgreementTextBox.Text);                                             
                var typeList = new List<Type>();

                if(agreement.AgreementItems.Count > 0)
                    typeList.Add(agreement.AgreementItems.FirstOrDefault().GetType());
                if (agreement.AgreementItemLogs.Count > 0)
                    typeList.Add(agreement.AgreementItemLogs.FirstOrDefault().GetType());
                if (agreement.AgreementPricings.Count > 0)
                    typeList.Add(agreement.AgreementPricings.FirstOrDefault().GetType());
                if (agreement.AgreementSnapshots.Count > 0)
                    typeList.Add(agreement.AgreementSnapshots.FirstOrDefault().GetType());
                if (agreement.AgreementTransactions.Count > 0)
                    typeList.Add(agreement.AgreementTransactions.FirstOrDefault().GetType());
                if (agreement.AgreementTransactionLogs.Count > 0)
                    typeList.Add(agreement.AgreementTransactionLogs.FirstOrDefault().GetType());

                XmlSerializer serializer = new XmlSerializer(agreement.GetType(), typeList.ToArray());
                XmlWriter writer = XmlWriter.Create("Agreement.xml");
                serializer.Serialize(writer, agreement);

我更新了我的回答。XmlSerializer 不会按照你想要的方式工作,但是你可以轻松地使用 DataContractSerializer,因为 EF 为你生成了 DataContract。 - Joel C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接