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.
My project has 2 classes, Post and Comment (foreign key from Post).
These are my classes.
Post class:
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; }
}
Comment class:
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; }
}
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.
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.
If I annotate the Comment class with
[DataContract(IsReference = true)]
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?
Thanks in advance,
Léster
You can disable Lazy Loading on your Comment class by removing virtual from the Post property definition...
This should sort out the circular reference exception.
Here are 2 solutions
Solution #1:
I had this same problem and so I decorated my class with
DataContract
and the members withDataMember
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 theMetadataType
attribute. In your case, it would look like this...First, you will keep the auto generated entity as is:
Next, in another file, you will create another partial class and decorate it like this:
MetadataType
will essentially add the attributes from theMetadata
buddy class to the ones with the same name inComment
(not directly, but for our purposes, it's close enough... that's a topic for a different post). Of course, if yourComment
entity changes, you'll need to update this accordingly.Solution #2:
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 consumingComment
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 newDataContractSerializer
The advantage to this approach is that no matter how much you change
Comment
, you still don't need update anything.As an example for your code, let's say you are using
Comment
as follows:All you need to do is add
And then somewhere else you need to define
ReferencePreservingDataContractFormat
which will look like this:And that's it!
Either method will work just fine--pick the one that works for you.