Lés*_*ter 7 serialization entity-framework circular-reference asp.net-mvc-4 asp.net-web-api
我正在用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; }
}
Run Code Online (Sandbox Code Playgroud)
评论类:
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; }
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,当我尝试通过Web API获取帖子时,它会向我吐出以下错误:
Object graph for type 'APIServer.Models.Comment' contains cycles and cannot be serialized if reference tracking is disabled.
Run Code Online (Sandbox Code Playgroud)
当我尝试通过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.
Run Code Online (Sandbox Code Playgroud)
如果我用Comment注释Comment类
[DataContract(IsReference = true)]
Run Code Online (Sandbox Code Playgroud)
错误消失,但序列化只返回注释的ID并忽略其他字段.
关于如何解决这个问题的任何建议?
提前致谢,
莱斯特
解决方案#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; }
}
Run Code Online (Sandbox Code Playgroud)
接下来,在另一个文件中,您将创建另一个分部类并将其装饰如下:
[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
}
}
Run Code Online (Sandbox Code Playgroud)
MetadataType本质上会将Metadata伙伴类中的属性添加到具有相同名称的属性中Comment(不是直接的,但对于我们的目的,它足够接近......这是不同帖子的主题).当然,如果您的Comment实体发生变化,您需要相应地更新它.
解决方案#2:
每次进行更改时都必须编辑第二个文件,这与直接编辑自动生成的文件相比只是略有改进.幸运的是,还有另一种方法更容易维护.详细信息可以发现在这里,但作为一个总结,所有你需要做的是装饰你OperationContract是消耗Comment带有一个附加属性ReferencePreservingDataContractFormat.请注意,该页面上提供的代码中存在轻微错误,这将导致无限递归.正如指出的这个帖子,解决方法是相当简单:不是在所有的递归,只需创建一个新的DataContractSerializer
这种方法的优点是无论你改变了多少Comment,你仍然不需要更新任何东西.
作为代码的示例,假设您使用Comment如下:
[OperationContract]
Comment FindComment(string criteria);
Run Code Online (Sandbox Code Playgroud)
您需要做的就是添加
[OperationContract]
[ReferencePreservingDataContractFormat]
Comment FindComment(string criteria);
Run Code Online (Sandbox Code Playgroud)
然后你需要定义的其他地方ReferencePreservingDataContractFormat看起来像这样:
//From http://blogs.msdn.com/b/sowmy/archive/2006/03/26/561188.aspx and https://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
);
}
}
Run Code Online (Sandbox Code Playgroud)
就是这样!
这两种方法都可以正常工作 - 选择适合你的方法.
您可以通过从 Post 属性定义中删除 virtual 来禁用 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; }
}
Run Code Online (Sandbox Code Playgroud)
这应该可以解决循环引用异常。
| 归档时间: |
|
| 查看次数: |
8840 次 |
| 最近记录: |