在ASP.NET的Web API序列化对象时,循环引用错误 [英] Circular Reference error when serializing objects in ASP.NET Web API
问题描述
我写在C#中使用实体框架从数据库中提取数据,序列化并将其发送到客户端的Web API项目。
I'm writing a Web API project in C# that uses Entity Framework to pull data from a DB, serialize it and send it to a client.
我的项目有2个班,Post和Comment(从邮政外键)。
My project has 2 classes, Post and Comment (foreign key from Post).
这是我的课。
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一个帖子来获得,它吐出我下面的错误:
My problem is that when I try to get via Web API a Post, it spits me the following error:
Object graph for type 'APIServer.Models.Comment' contains cycles and cannot be serialized if reference tracking is disabled.
当我试图让通过Web API评论,错误如下:
And when I try to get a Comment via Web API, the error is as follows:
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.
如果我用注释注释类
[DataContract(IsReference = true)]
错误消失,但系列化只返回注释的ID,而忽略了其他领域。
the errors disappear, but the serialization only returns the ID of the comment and ignores the other fields.
如何解决这个有什么建议?
Any suggestions on how to solve this?
由于提前,
莱斯特
推荐答案
解决方案1:
我有这个同样的问题,所以我饰我的类 DataContract
和数据成员
成员像你提到的。但是,我不喜欢直接编辑自动生成的code,因为我每次重新生成该文件的时间来重做。为了解决这个问题,我使用了 MetadataType
属性。对你来说,它看起来像这样...
I had this same problem and so I decorated my class with DataContract
and the members with DataMember
like you mention. HOWEVER, I don't like editing auto-generated code directly because I have to redo it every time I regenerate the file. In order to get around this, I used the MetadataType
attribute. In your case, it would look like this...
首先,你将保持自动生成的实体是:
First, you will keep the auto generated entity as is:
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; }
}
接下来,在另一个文件中,您将创建另一个部分类和装饰它是这样的:
Next, in another file, you will create another partial class and decorate it like this:
[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
将主要从元
哥们类具有相同名称的那些在<$添加属性C $ C>注释(不直接,但对于我们来说,这是足够接近......这是一个不同的文章主题)。当然,如果你的注释
实体变更,则需要相应地更新这一点。
MetadataType
will essentially add the attributes from the Metadata
buddy class to the ones with the same name in Comment
(not directly, but for our purposes, it's close enough... that's a topic for a different post). Of course, if your Comment
entity changes, you'll need to update this accordingly.
解决方案2:
有编辑您的第二个文件每次你做出改变只是从直接编辑自动生成的文件,略有改善的时间。幸运的是,另一种方法是更容易维护。详情可参阅这里但作为一个总结,所有你需要做的是装饰你的 OperationContract的
正消耗 注释带有附加属性,
参考preservingDataContractFormat
。需要注意的是在所提供的网页,将导致无限递归在code略有误差。正如<著名href=\"http://stackoverflow.com/questions/4266008/endless-loop-in-a-$c$c-sample-on-serialization\">this后,该修补程序很简单:不是递归的所有,只是创建一个新的的DataContractSerializer
Having to edit your second file every time you make a change is only a slight improvement from directly editing auto-generated files. Fortunately, there is another approach that is much easier to maintain. Details can be found here but as a summary, all you need to do is decorate your OperationContract
that is consuming Comment
with an additional attribute, ReferencePreservingDataContractFormat
. Note that there is a slight error in the code provided on that page that would cause infinite recursion. As noted in this post, the fix is quite simple: instead of recursing at all, just create a new DataContractSerializer
这种方法的好处是,不管你有多少改变注释
,你仍然不需要更新任何东西。
The advantage to this approach is that no matter how much you change Comment
, you still don't need update anything.
至于你的code为例,假设你正在使用注释
如下:
As an example for your code, let's say you are using Comment
as follows:
[OperationContract]
Comment FindComment(string criteria);
所有你需要做的就是添加
All you need to do is add
[OperationContract]
[ReferencePreservingDataContractFormat]
Comment FindComment(string criteria);
然后在其他地方,你需要定义参考preservingDataContractFormat
这将是这样的:
//From http://blogs.msdn.com/b/sowmy/archive/2006/03/26/561188.aspx and http://stackoverflow.com/questions/4266008/endless-loop-in-a-code-sample-on-serialization
public class ReferencePreservingDataContractFormatAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
{
}
public void ApplyClientBehavior(OperationDescription description, System.ServiceModel.Dispatcher.ClientOperation proxy)
{
IOperationBehavior innerBehavior = new ReferencePreservingDataContractSerializerOperationBehavior(description);
innerBehavior.ApplyClientBehavior(description, proxy);
}
public void ApplyDispatchBehavior(OperationDescription description, System.ServiceModel.Dispatcher.DispatchOperation dispatch)
{
IOperationBehavior innerBehavior = new ReferencePreservingDataContractSerializerOperationBehavior(description);
innerBehavior.ApplyDispatchBehavior(description, dispatch);
}
public void Validate(OperationDescription description)
{
}
}
class ReferencePreservingDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
public ReferencePreservingDataContractSerializerOperationBehavior(OperationDescription operationDescription) : base(operationDescription) { }
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new DataContractSerializer(type, name, ns, knownTypes,
0x7FFF, //maxItemsInObjectGraph
false, //ignoreExtensionDataObject
true, //preserveObjectReferences
null //dataContractSurrogate
);
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new DataContractSerializer(type, name, ns, knownTypes,
0x7FFF, //maxItemsInObjectGraph
false, //ignoreExtensionDataObject
true, //preserveObjectReferences
null //dataContractSurrogate
);
}
}
这就是它!
这两种方法都工作得很好 - 挑选适合你的工作有一个
Either method will work just fine--pick the one that works for you.
这篇关于在ASP.NET的Web API序列化对象时,循环引用错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!