我正在用C#编写一个Web API项目,它使用Entity Framework从数据库中提取数据,将其序列化并发送给客户端。
我的项目有2个类,Post和Comment(来自Post的外键)。
这些是我的课程。
发布课程:
public partial class Post
{
public Post()
{
this.Attachment = new HashSet<Attachment>();
this.Comment = new HashSet<Comment>();
}
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public System.DateTime Created { get; set; }
public Nullable<System.DateTime> Modified { get; set; }
public virtual ICollection<Attachment> Attachment { get; set; }
public virtual ICollection<Comment> Comment { get; set; }
}
评论类:
public partial class Comment
{
public int CommentId { get; set; }
public string Content { get; set; }
public System.DateTime Posted { get; set; }
public bool Approved { get; set; }
public int AnswersTo { get; set; }
public int PostId { get; set; }
public virtual Post Post { get; set; }
}
我的问题是,当我尝试通过Web API发布邮件时,它会向我发出以下错误:
Object graph for type 'APIServer.Models.Comment' contains cycles and cannot be serialized if reference tracking is disabled.
当我尝试通过Web API获取评论时,错误如下:
Object graph for type 'System.Collections.Generic.HashSet`1[[APIServer.Models.Comment, APIServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' contains cycles and cannot be serialized if reference tracking is disabled.
如果我用
注释Comment类[DataContract(IsReference = true)]
错误消失,但序列化只返回注释的ID并忽略其他字段。
有关如何解决此问题的任何建议?
提前致谢,
莱斯特
答案 0 :(得分:6)
解决方案#1:
我遇到了同样的问题,所以我用DataContract
和DataMember
成员装饰了我的课程,就像你提到的那样。但是,我不喜欢直接编辑自动生成的代码,因为每次重新生成文件时我都要重做它。为了解决这个问题,我使用了MetadataType
属性。在你的情况下,它看起来像这样......
首先,您将按原样保留自动生成的实体:
public partial class Comment
{
public int CommentId { get; set; }
public string Content { get; set; }
public System.DateTime Posted { get; set; }
public bool Approved { get; set; }
public int AnswersTo { get; set; }
public int PostId { get; set; }
public virtual Post Post { get; set; }
}
接下来,在另一个文件中,您将创建另一个部分类并按如下所示进行装饰:
[MetadataType(typeof(Metadata))]
[DataContract(IsReference = true)]
public partial class Comment
{
private class Metadata
{
[DataMember]
public int CommentId { get; set; }
[DataMember]
public string Content { get; set; }
[DataMember]
public System.DateTime Posted { get; set; }
[DataMember]
public bool Approved { get; set; }
[DataMember]
public int AnswersTo { get; set; }
[DataMember]
public int PostId { get; set; }
[DataMember]
public virtual Post Post { get; set; } // you can remove "virtual" if you wish
}
}
MetadataType
基本上会将Metadata
伙伴类中的属性添加到Comment
中具有相同名称的属性(不是直接,但就我们的目的而言,它足够接近......这是一个不同帖子的主题)。当然,如果您的Comment
实体发生变化,则需要相应更新。
解决方案#2:
每次进行更改时都必须编辑第二个文件,这与直接编辑自动生成的文件相比只是略有改进。幸运的是,还有另一种方法更容易维护。可以找到详细信息here但是作为摘要,您需要做的就是使用其他属性OperationContract
来装饰正在消耗Comment
的{{1}}。请注意,该页面上提供的代码中存在轻微错误,这将导致无限递归。正如this帖子中所述,修复非常简单:只需创建一个新的ReferencePreservingDataContractFormat
这种方法的优势在于,无论您更改DataContractSerializer
多少,您仍然不需要更新任何内容。
作为代码的示例,假设您正在使用Comment
,如下所示:
Comment
您需要做的就是添加
[OperationContract]
Comment FindComment(string criteria);
然后在其他地方你需要定义[OperationContract]
[ReferencePreservingDataContractFormat]
Comment FindComment(string criteria);
,如下所示:
ReferencePreservingDataContractFormat
就是这样!
任何一种方法都可以正常工作 - 选择适合你的方法。
答案 1 :(得分:1)
您可以通过从Post属性定义中删除虚拟来禁用Comment类上的延迟加载...
public partial class Comment
{
public int CommentId { get; set; }
public string Content { get; set; }
public System.DateTime Posted { get; set; }
public bool Approved { get; set; }
public int AnswersTo { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
}
这应该排除循环引用异常。