protected void SerializeLinkedResources(Stream writeStream, JsonWriter writer, JsonSerializer serializer, RelationAggregator aggregator)
        {
            /* This is a bit messy, because we may add items of a given type to the
             * set we are currently processing. Not only is this an issue because you
             * can't modify a set while you're enumerating it (hence why we make a
             * copy first), but we need to catch the newly added objects and process
             * them as well. So, we have to keep making passes until we detect that
             * we haven't added any new objects to any of the appendices.
             */
            Dictionary<Type, ISet<object>>
                processed = new Dictionary<Type, ISet<object>>(),
                toBeProcessed = new Dictionary<Type, ISet<object>>(); // is this actually necessary?
            /* On top of that, we need a new JsonWriter for each appendix--because we
             * may write objects of type A, then while processing type B find that
             * we need to write more objects of type A! So we can't keep appending
             * to the same writer.
             */
            /* Oh, and we have to keep a reference to the TextWriter of the JsonWriter
             * because there's no member to get it back out again. ?!?
             * */

            int numAdditions;

            if (aggregator.Appendices.Count > 0)
            {
                writer.WritePropertyName(IncludedKeyName);
                writer.WriteStartArray();

                do
                {
                    numAdditions = 0;
                    Dictionary<Type, ISet<object>> appxs = new Dictionary<Type, ISet<object>>(aggregator.Appendices); // shallow clone, in case we add a new type during enumeration!
                    foreach (KeyValuePair<Type, ISet<object>> apair in appxs)
                    {
                        Type type = apair.Key;
                        ISet<object> appendix = apair.Value;

                        HashSet<object> tbp;
                        if (processed.ContainsKey(type))
                        {
                            toBeProcessed[type] = tbp = new HashSet<object>(appendix.Except(processed[type]));
                        }
                        else
                        {
                            toBeProcessed[type] = tbp = new HashSet<object>(appendix);
                            processed[type] = new HashSet<object>();
                        }

                        if (tbp.Count > 0)
                        {
                            numAdditions += tbp.Count;
                            foreach (object obj in tbp)
                            {
                                Serialize(obj, writeStream, writer, serializer, aggregator); // Note, not writer, but jw--we write each type to its own JsonWriter and combine them later.
                            }
                            processed[type].UnionWith(tbp);
                        }
                    }
                } while (numAdditions > 0);

                writer.WriteEndArray();
            }


        }
 protected void SerializeMany(object value, Stream writeStream, JsonWriter writer, JsonSerializer serializer, RelationAggregator aggregator)
 {
     writer.WriteStartArray();
     foreach (object singleVal in (IEnumerable)value)
     {
         this.Serialize(singleVal, writeStream, writer, serializer, aggregator);
     }
     writer.WriteEndArray();
 }
        protected void Serialize(object value, Stream writeStream, JsonWriter writer, JsonSerializer serializer, RelationAggregator aggregator)
        {
            writer.WriteStartObject();

            var resourceType = value.GetType();
            var jsonTypeKey = _modelManager.GetResourceTypeNameForType(resourceType);
            var idProp = _modelManager.GetIdProperty(resourceType);

            // Write the type and id
            WriteTypeAndId(writer, resourceType, value);

            // Leverage the cached map to avoid another costly call to System.Type.GetProperties()
            var props = _modelManager.GetProperties(value.GetType());

            // Do non-model properties first, everything else goes in "related"
            //TODO: Unless embedded???
            var relationshipModelProperties = new List<RelationshipModelProperty>();

            // Write attributes
            WriteAttributes(props, writer, idProp, value, serializer, relationshipModelProperties);

            // Now do other stuff
            if (relationshipModelProperties.Count() > 0)
            {
                writer.WritePropertyName(RelationshipsKeyName);
                writer.WriteStartObject();
            }
            foreach (var relationshipModelProperty in relationshipModelProperties)
            {
                bool skip = false, 
                    iip = false;
                string lt = null;
                SerializeAsOptions sa = SerializeAsOptions.NoLinks;

                var prop = relationshipModelProperty.Property;

                object[] attrs = prop.GetCustomAttributes(true);

                foreach (object attr in attrs)
                {
                    Type attrType = attr.GetType();
                    if (typeof(JsonIgnoreAttribute).IsAssignableFrom(attrType))
                    {
                        skip = true;
                        continue;
                    }
                    if (typeof(IncludeInPayload).IsAssignableFrom(attrType))
                        iip = ((IncludeInPayload)attr).Include;
                    if (typeof(SerializeAs).IsAssignableFrom(attrType))
                        sa = ((SerializeAs)attr).How;
                    if (typeof(LinkTemplate).IsAssignableFrom(attrType))
                        lt = ((LinkTemplate)attr).TemplateString;
                }
                if (skip) continue;

                // Write the relationship's type
                writer.WritePropertyName(relationshipModelProperty.JsonKey);
                

                // Now look for enumerable-ness:
                if (typeof(IEnumerable<Object>).IsAssignableFrom(prop.PropertyType))
                {
                    writer.WriteStartObject();
                    // Write the data element
                    writer.WritePropertyName(PrimaryDataKeyName);
                    // Look out! If we want to SerializeAs a link, computing the property is probably 
                    // expensive...so don't force it just to check for null early!
                    if (sa == SerializeAsOptions.NoLinks && prop.GetValue(value, null) == null)
                    {
                        writer.WriteStartArray();
                        writer.WriteEndArray();
                        writer.WriteEndObject();
                        continue;
                    }

                    // Always write the data attribute of a relationship
                    IEnumerable<object> items = (IEnumerable<object>)prop.GetValue(value, null);
                    if (items == null)
                    {
                        // Return an empty array when there are no items
                        writer.WriteStartArray();
                        writer.WriteEndArray();
                    }
                    else
                    {
                        // Write the array with data
                        this.WriteIdsArrayJson(writer, items, serializer);
                        if (iip)
                        {
                            Type itemType;
                            if (prop.PropertyType.IsGenericType)
                            {
                                itemType = prop.PropertyType.GetGenericArguments()[0];
                            }
                            else
                            {
                                // Must be an array at this point, right??
                                itemType = prop.PropertyType.GetElementType();
                            }
                            if (aggregator != null) aggregator.Add(itemType, items); // should call the IEnumerable one...right?
                        }
                    } 

                    // in case there is also a link defined, add it to the relationship
                    if (sa == SerializeAsOptions.RelatedLink) 
                    {
                        if (lt == null) throw new JsonSerializationException("A property was decorated with SerializeAs(SerializeAsOptions.Link) but no LinkTemplate attribute was provided.");
                        //TODO: Check for "{0}" in linkTemplate and (only) if it's there, get the Ids of all objects and "implode" them.
                        string href = String.Format(lt, null, GetIdFor(value));
                        //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                        //TODO: Support ids and type properties in "link" object

                        writer.WritePropertyName(LinksKeyName);
                        writer.WriteStartObject();
                        writer.WritePropertyName(RelatedKeyName);
                        writer.WriteValue(href);
                        writer.WriteEndObject();
                    }
                    writer.WriteEndObject();
                }
                else
                {
                    var propertyValue = prop.GetValue(value, null);

                    writer.WriteStartObject();
                    // Write the data element
                    writer.WritePropertyName(PrimaryDataKeyName);
                    // Look out! If we want to SerializeAs a link, computing the property is probably 
                    // expensive...so don't force it just to check for null early!
                    if (sa == SerializeAsOptions.NoLinks && propertyValue == null)
                    {
                        writer.WriteNull();
                        writer.WriteEndObject();
                        continue;
                    }

                    Lazy<string> objId = new Lazy<String>(() => GetIdFor(propertyValue));

                    // Write the resource identifier object
                    WriteResourceIdentifierObject(writer, prop.PropertyType, propertyValue);

                    if (iip)
                        if (aggregator != null)
                            aggregator.Add(prop.PropertyType, propertyValue);

                    // If there are links write them next to the data object
                    if (sa == SerializeAsOptions.RelatedLink)
                    {
                        if (lt == null)
                            throw new JsonSerializationException(
                                "A property was decorated with SerializeAs(SerializeAsOptions.Link) but no LinkTemplate attribute was provided.");
                        var relatedObjectId = lt.Contains("{0}") ? objId.Value : null;
                        string link = String.Format(lt, relatedObjectId, GetIdFor(value));

                        writer.WritePropertyName(LinksKeyName);
                        writer.WriteStartObject();
                        writer.WritePropertyName(RelatedKeyName);
                        writer.WriteValue(link);
                        writer.WriteEndObject();
                    }
                    writer.WriteEndObject();
                }
            }
            if (relationshipModelProperties.Count() > 0)
            {
                writer.WriteEndObject();
            }

            writer.WriteEndObject();
        }
        public override Task WriteToStreamAsync(System.Type type, object value, Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
        {
            RelationAggregator aggregator;
            lock (this.RelationAggregators)
            {
                if (this.RelationAggregators.ContainsKey(writeStream))
                    aggregator = this.RelationAggregators[writeStream];
                else
                {
                    aggregator = new RelationAggregator();
                    this.RelationAggregators[writeStream] = aggregator;
                }
            }

            var contentHeaders = content == null ? null : content.Headers;
            var effectiveEncoding = SelectCharacterEncoding(contentHeaders);
            JsonWriter writer = this.CreateJsonWriter(typeof(object), writeStream, effectiveEncoding);
            JsonSerializer serializer = this.CreateJsonSerializer();
            if (_errorSerializer.CanSerialize(type))
            {
                // `value` is an error
                _errorSerializer.SerializeError(value, writeStream, writer, serializer);
            }
            else
            {
                var payload = value as IPayload;
                if (payload != null)
                {
                    value = payload.PrimaryData;
                }
            
                writer.WriteStartObject();
                writer.WritePropertyName(PrimaryDataKeyName);

                if (value == null)
                {
                    if (_modelManager.IsSerializedAsMany(type))
                    {
                        writer.WriteStartArray();
                        writer.WriteEndArray();
                    }
                    else
                    {
                        writer.WriteNull();
                    }
                }
                else
                {
                    Type valtype = GetSingleType(value.GetType());
                    if (_modelManager.IsSerializedAsMany(value.GetType()))
                        aggregator.AddPrimary(valtype, (IEnumerable<object>) value);
                    else
                        aggregator.AddPrimary(valtype, value);

                    //writer.Formatting = Formatting.Indented;

                    if (_modelManager.IsSerializedAsMany(value.GetType()))
                        this.SerializeMany(value, writeStream, writer, serializer, aggregator);
                    else
                        this.Serialize(value, writeStream, writer, serializer, aggregator);

                    // Include links from aggregator
                    SerializeLinkedResources(writeStream, writer, serializer, aggregator);
                }

                if (payload != null && payload.Metadata != null)
                {
                    writer.WritePropertyName("meta");
                    serializer.Serialize(writer, payload.Metadata);
                }

                writer.WriteEndObject();
            }
            writer.Flush();

            lock (this.RelationAggregators)
            {
                this.RelationAggregators.Remove(writeStream);
            }

            //return base.WriteToStreamAsync(type, obj as object, writeStream, content, transportContext);

            //TODO: For now we won't worry about optimizing this down into smaller async parts, we'll just do it all synchronous. So...
            // Just return a completed task... from http://stackoverflow.com/a/17527551/489116
            var tcs = new TaskCompletionSource<object>();
            tcs.SetResult(null);
            return tcs.Task;
        }