posts - 315, comments - 268, trackbacks - 15

My Links

News

View Pietro Libro's profile on LinkedIn

DomusDotNet
   DomusDotNet

Pietro Libro

Tag Cloud

Article Categories

Archives

Post Categories

Blogs amici

Links

Web API: Circular Object References

Quando utilizziamo le Web API, per leggere e scrivere oggetti nel corpo di un messaggio Http utilizziamo delle classi particolari, le media-type formatters. "Gratis" Web API fornisce media-type formatters per JSON e XML, utilizzate secondo della richiesta da parte dei client (“Accept”). Se JSON e XML non sono i "formati" di cui abbiamo bisogno possiamo sempre creare la nostra classe derivata da MediaTypeFormatter o BufferedMediaTypeFormatter, rispettivamente per scenari asincroni o sincroni, ma non sono l’argomento di questo post promemoria.

Quando abbiamo a che fare con grafi di oggetti complessi, possono crearsi situazioni in cui abbiamo riferimenti circolari tra oggetti. Ad esempio in uno scenario di questo tipo:

image

Dove abbiamo due classi, Book ed Author , referenziate tramite collection (praticamente un’associazione molti-a-molti) l’una con l’altra.

Se abbiamo una Web API di questo tipo :

private BookshelfDb _db = new BookshelfDb();
public IEnumerable<Book> GetBooks()
{
    return _db.Books.Include(b => b.Authors).AsEnumerable();
}
...

ed una configurazione standard delle Web API, ad eccezione dell’indentatura:

JsonMediaTypeFormatter json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.Indent = true;

In presenza di dati, invocando GetBooks() (ad esempio tramite browser), otterremo un’eccezione di questo tipo:

{ 
  "Message": "An error has occurred.", 
  "ExceptionMessage": "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.", 
  "ExceptionType": "System.InvalidOperationException", 
  "StackTrace": null, 
  "InnerException": { 
    "Message": "An error has occurred.", 
    "ExceptionMessage": "Self referencing loop detected with type 'BookshelfWebApi.Models.Book'. Path '[0].Authors[0].Books'.", 
    "ExceptionType": "Newtonsoft.Json.JsonSerializationException", 
    "StackTrace": "   …
}

La soluzione al problema è abbastanza semplice  in quanto è sufficiente aggiungere la riga:

json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All;

Il caso Xml, non è immediato come nel caso JSON, in quanto è necessario decorare le classi con l’attributo DataContract impostando la proprietà IsReference a true. In questo caso però è necessario decorare anche le proprietà con l’attributo DataMember:

[DataContract(IsReference = true)]
public class Book
{
    public Book()
    {
        this.Authors = new List<Author>();
    }
    [DataMember()]
    public int Id { get; set; }
    [DataMember()]
    public int Paperback { get; set; }
    [DataMember()]
    public string Title { get; set; }
    [DataMember()]
    public string ISBN_10 { get; set; }
    [DataMember()]
    public string ISBN_13 { get; set; }
    [DataMember()]
    public string Language { get; set; }
    [DataMember()]
    public string Publisher { get; set; }
    [DataMember()]
    public ICollection<Author> Authors { get; set; }
}

Se non si vuole “sporcare” le classi “farcendole” di attributi possiamo optare per un DataContractSerializer. Le Web API possono essere configurate modificando il codice presente nella classe WebApiConfig.cs:

image

Quanto detto è sicuramente famigliare per chi espone entità collegate tramite Http, ad esempio utilizzando WCF.

Print | posted on mercoledì 22 agosto 2012 13:17 | Filed Under [ C# ASP.NET Entity Framework 5 .Net Framework 4.5 ]

Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET