I have a circular object graph in my model, but that is inevitable.
As per the advice given in this article, I have used the DataContractAttribute
and set IsReference = true
on all members. I have also provided the DataMemberAttribute
on all properties that I want to serialize.
To make sure that the serializer does not face any problem again, I have only chosen not to serialize navigational properties.
However, I still encounter an exception in my catch block. The details of the exception are as follows:
_innerException: {"Type
'System.Data.Entity.DynamicProxies.Author_615FB9F8BB22B55A7CA168DA5ED29EC6A0B59F62FD79D1346045351BE2F163A4' with data contract name
'Author_615FB9F8BB22B55A7CA168DA5ED29EC6A0B59F62FD79D1346045351BE2F163A4:
http://schemas.datacontract
.org/2004/07/System.Data.Entity.DynamicProxies' is not expected.
Consider using a
DataContractResolver or add any types not known statically to
the list of known types - for
example, by using the KnownTypeAttribute attribute or by adding them
to the list of known types
passed to DataContractSerializer."}
I could but do not wish to:
1) Disable proxy creation. I could remove proxy creation just for the sake of serialization, which I may do. But I also want to learn why I am still getting the exception and what I can do about it.
2) Remove the circular references. Reason: These sorts of references are very common in Entity Framework generated models. If I were to be doing a large project with 800 - 1000 classes in the model, it be a nightmare to implement it by removing circular references.
I have described the architectural elements of this little spike solution below.
Database Schema
Id AuthorName
-------------------------------
1 Charles Dickens
2 Charles Petzold
3 Charles Darwin
4 Charles Chaplin
5 Leo Tolstoy
6 Fydor Dostoevsky
7 Ayn Rand
8 Napolean Hill
9 Claude M. Bristol
10 Edward Dwight Easty
11 O. Henry
12 William Shakespeare
13 Juwal Lowy
14 Jeffrey Richter
15 Chris Sells
16 Don Box
17 Steven Pinker
18 Jim Rohn
19 George Eliot
20 Sathyaish Chakravarthy
Id Title AuthorId
----------- -------------------------------------------------- -----------
1 Nicholas Nickleby 1
Id BookId Review
----------- ---------------------------------------------------------------
1 1 How do I know? I haven't read it.
Model
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace BookReviewsModel
{
[DataContract(IsReference = true)]
public partial class Author
{
[DataMember]
public virtual int Id { get; set; }
[DataMember]
public virtual string AuthorName { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
}
namespace BookReviewsModel
{
[DataContract(IsReference = true)]
public partial class Book
{
[DataMember]
public virtual int Id { get; set; }
[DataMember]
public virtual string Title { get; set; }
[DataMember]
public virtual int AuthorId { get; set; }
public virtual Author Author { get; set; }
public virtual ICollection<BookReview> BookReviews { get; set; }
}
}
namespace BookReviewsModel
{
[DataContract(IsReference = true)]
public partial class BookReview
{
[DataMember]
public virtual int Id { get; set; }
[DataMember]
public virtual int BookId { get; set; }
[DataMember]
[AllowHtml]
public virtual string Review { get; set; }
public virtual Book Book { get; set; }
}
}
Controller Code
namespace BookReviews.Controllers
{
public class AuthorController : ApiController
{
[HttpGet]
public IEnumerable<Author> Index()
{
try
{
using (var context = new BookReviewEntities())
{
var authors = context.Authors.ToList();
var str = Serialize(new XmlMediaTypeFormatter(), authors);
System.Diagnostics.Debugger.Break();
System.Diagnostics.Debug.Print(str);
return authors;
}
}
catch (Exception ex)
{
var responseMessage = new HttpResponseMessage
{
Content = new StringContent("Couldn't retreive the list of authors."),
ReasonPhrase = ex.Message.Replace('\n', ' ')
};
throw new HttpResponseException(responseMessage);
}
}
string Serialize<T>(MediaTypeFormatter formatter, T value)
{
Stream stream = new MemoryStream();
var content = new StreamContent(stream);
formatter.WriteToStreamAsync(typeof(T), value, stream, content, null).Wait();
stream.Position = 0;
return content.ReadAsStringAsync().Result;
}
}
}
Solution:
This issue is resolved in the AspNetWebStack Nightly Build.
I have not tracked down which check-in corrects the behavior as I am following up on multiple issues.
You can update your solution to use the latest nightly packages by adding http://www.myget.org/F/aspnetwebstacknightly/ to your Package Manager config, and then explicitly updating from this additional repository.
As far as I can tell, the 1/18 nightly is stable within my solution (odata queries return MUCH faster as well.)
Workaround:
If you can't use the latest aspnetwebstack builds, there is a potential workaround if you don't require an XML formatted feed.
http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization
This document shows how to configure formatters used by web-api controllers, it also shows how to deal with circular references, and how to reconfigure/replace the default xml formatter.
As a workaround you can remove the xml formatter during Application_Start:
var xmlFormatter = config.Formatters.XmlFormatter;
if (xmlFormatter != null)
{
config.Formatters.Remove(xmlFormatter);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With