It's very common to have circular reference in models. For example, the following models shows a bidirection navigation property:
When using with Web Api by generating an EF scaffolding api controller, it won't work by default, however. The following error will occur when serializing with json.net serializer:
Self referencing loop detected for property 'Category' with type
The error occurs because the serializer doesn't know how to handle cirular reference. (Similar error occurs in xml serializer as well)
Disable proxy and include reference
EF proxy doesn't work well with POCO data serialization. There are several workarounds. For simplicity, we just disable it in the data context class:
However, after disable proxy, the navigation property won't be lazy loaded. So you have to include the reference when retrieving data from database. Change the scaffolding controller code to:
The include call will include the reference data for all records.
Fix 1: Ignoring circular reference globally
json.net serializer supports to ignore circular reference on global setting. A quick fix is to put following code in WebApiConfig.cs file:
The simple fix will make serializer to ignore the reference which will cause a loop. However, it has limitations:
- The data loses the looping reference information
- The fix only applies to JSON.net
- The level of references can't be controlled if there is a deep reference chain
Fix 2: Preserving circular reference globally
This second fix is similar to the first. Just change the code to:
The data shape will be changed after applying this setting.
The $id and $ref keeps the all the references and makes the object graph level flat, but the client code needs to know the shape change to consume the data and it only applies to JSON.NET serializer as well.
Fix 3: Ignore and preserve reference attributes
This fix is decorate attributes on model class to control the serialization behavior on model or property level. To ignore the property:
JsonIgnore is for JSON.NET and IgnoreDataMember is for XmlDCSerializer.
To preserve reference:
[JsonObject(IsReference = true)] is for JSON.NET and [DataContract(IsReference = true)] is for XmlDCSerializer. Note that: after applying DataContract on class, you need to add DataMember to properties that you want to serialize.
The attributes can be applied on both json and xml serializer and gives more controls on model class.