/// <summary>
        /// Writes the metadata properties for a resource which can only occur at the end.
        /// </summary>
        /// <param name="resourceState">The resource state for which to write the metadata properties.</param>
        /// <param name="duplicatePropertyNameChecker">The DuplicatePropertyNameChecker to use.</param>
        internal void WriteResourceEndMetadataProperties(IODataJsonLightWriterResourceState resourceState, IDuplicatePropertyNameChecker duplicatePropertyNameChecker)
        {
            Debug.Assert(resourceState != null, "resourceState != null");

            ODataResourceBase resource = resourceState.Resource;

            var navigationLinkInfo = resource.MetadataBuilder.GetNextUnprocessedNavigationLink();

            while (navigationLinkInfo != null)
            {
                Debug.Assert(resource.MetadataBuilder != null, "resource.MetadataBuilder != null");
                navigationLinkInfo.NestedResourceInfo.MetadataBuilder = resource.MetadataBuilder;

                this.WriteNavigationLinkMetadata(navigationLinkInfo.NestedResourceInfo, duplicatePropertyNameChecker);
                navigationLinkInfo = resource.MetadataBuilder.GetNextUnprocessedNavigationLink();
            }

            // write "odata.actions" metadata
            IEnumerable <ODataAction> actions = resource.Actions;

            if (actions != null && actions.Any())
            {
                this.WriteOperations(actions.Cast <ODataOperation>(), /*isAction*/ true);
            }

            // write "odata.functions" metadata
            IEnumerable <ODataFunction> functions = resource.Functions;

            if (functions != null && functions.Any())
            {
                this.WriteOperations(functions.Cast <ODataOperation>(), /*isAction*/ false);
            }
        }
        /// <summary>
        /// Writes the metadata properties for a resource which can only occur at the start.
        /// </summary>
        /// <param name="resourceState">The resource state for which to write the metadata properties.</param>
        internal void WriteResourceStartMetadataProperties(IODataJsonLightWriterResourceState resourceState)
        {
            Debug.Assert(resourceState != null, "resourceState != null");

            ODataResourceBase resource = resourceState.Resource;

            // expectedResourceTypeName : if expected is of base type. but the resource real type is derived,
            // we need to set the resource type name.
            //      Writing response: expected type info can be identical with context uri info.
            //      Writing request: contextUri may not be provided, we always use the type from metadata info.
            //                       From model: if the resource can be found in model.
            //                       From serializationInfo: if user set the serializationInfo for the resource.
            string expectedResourceTypeName = null;

            if (this.WritingResponse)
            {
                expectedResourceTypeName = resourceState.GetOrCreateTypeContext(this.WritingResponse).ExpectedResourceTypeName;
            }
            else
            {
                if (resourceState.ResourceTypeFromMetadata == null)
                {
                    expectedResourceTypeName = resourceState.SerializationInfo == null
                     ? null
                     : resourceState.SerializationInfo.ExpectedTypeName;
                }
                else
                {
                    expectedResourceTypeName = resourceState.ResourceTypeFromMetadata.FullTypeName();
                }
            }

            // Write the "@odata.type": "typename"
            string typeName = this.JsonLightOutputContext.TypeNameOracle.GetResourceTypeNameForWriting(
                expectedResourceTypeName,
                resource,
                resourceState.IsUndeclared);

            if (typeName != null && !string.Equals(typeName, ODataConstants.ContextUriFragmentUntyped, StringComparison.Ordinal))
            {
                this.ODataAnnotationWriter.WriteODataTypeInstanceAnnotation(typeName);
            }

            // Write the "@odata.id": "Entity Id"
            Uri id;

            if (resource.MetadataBuilder.TryGetIdForSerialization(out id))
            {
                this.ODataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataId);
                if (id != null && !resource.HasNonComputedId)
                {
                    id = this.MetadataDocumentBaseUri.MakeRelativeUri(id);
                }

                this.JsonWriter.WriteValue(id == null ? null : this.UriToString(id));
            }

            // Write the "@odata.etag": "ETag value"
            string etag = resource.ETag;

            if (etag != null)
            {
                this.ODataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataETag);
                this.JsonWriter.WriteValue(etag);
            }
        }
        /// <summary>
        /// Writes the metadata properties for a resource which can occur both at the start or at the end.
        /// </summary>
        /// <param name="resourceState">The resource state for which to write the metadata properties.</param>
        /// <remarks>
        /// This method will only write properties which were not written yet.
        /// </remarks>
        internal void WriteResourceMetadataProperties(IODataJsonLightWriterResourceState resourceState)
        {
            Debug.Assert(resourceState != null, "resourceState != null");

            ODataResourceBase resource = resourceState.Resource;

            // Write the "@odata.editLink": "edit-link-uri"
            Uri editLinkUriValue = resource.EditLink;

            if (editLinkUriValue != null && !resourceState.EditLinkWritten)
            {
                this.ODataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataEditLink);
                this.JsonWriter.WriteValue(this.UriToString(
                                               resource.HasNonComputedEditLink || !editLinkUriValue.IsAbsoluteUri
                    ? editLinkUriValue
                    : this.MetadataDocumentBaseUri.MakeRelativeUri(editLinkUriValue)));
                resourceState.EditLinkWritten = true;
            }

            // Write the "@odata.readLink": "read-link-uri".
            // If readlink is identical to editlink, don't write readlink.
            Uri readLinkUriValue = resource.ReadLink;

            if (readLinkUriValue != null && readLinkUriValue != editLinkUriValue && !resourceState.ReadLinkWritten)
            {
                this.ODataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataReadLink);
                this.JsonWriter.WriteValue(this.UriToString(
                                               resource.HasNonComputedReadLink
                    ? readLinkUriValue
                    : this.MetadataDocumentBaseUri.MakeRelativeUri(readLinkUriValue)));
                resourceState.ReadLinkWritten = true;
            }

            // Write MLE metadata
            ODataStreamReferenceValue mediaResource = resource.MediaResource;

            if (mediaResource != null)
            {
                // Write the "@odata.mediaEditLink": "edit-link-uri"
                Uri mediaEditLinkUriValue = mediaResource.EditLink;
                if (mediaEditLinkUriValue != null && !resourceState.MediaEditLinkWritten)
                {
                    this.ODataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataMediaEditLink);
                    this.JsonWriter.WriteValue(this.UriToString(
                                                   mediaResource.HasNonComputedEditLink
                        ? mediaEditLinkUriValue
                        : this.MetadataDocumentBaseUri.MakeRelativeUri(mediaEditLinkUriValue)));
                    resourceState.MediaEditLinkWritten = true;
                }

                // Write the "@odata.mediaReadLink": "read-link-uri"
                // If mediaReadLink is identical to mediaEditLink, don't write readlink.
                Uri mediaReadLinkUriValue = mediaResource.ReadLink;
                if (mediaReadLinkUriValue != null && mediaReadLinkUriValue != mediaEditLinkUriValue && !resourceState.MediaReadLinkWritten)
                {
                    this.ODataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataMediaReadLink);
                    this.JsonWriter.WriteValue(this.UriToString(
                                                   mediaResource.HasNonComputedReadLink
                        ? mediaReadLinkUriValue
                        : this.MetadataDocumentBaseUri.MakeRelativeUri(mediaReadLinkUriValue)));
                    resourceState.MediaReadLinkWritten = true;
                }

                // Write the "@odata.mediaContentType": "content/type"
                string mediaContentType = mediaResource.ContentType;
                if (mediaContentType != null && !resourceState.MediaContentTypeWritten)
                {
                    this.ODataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataMediaContentType);
                    this.JsonWriter.WriteValue(mediaContentType);
                    resourceState.MediaContentTypeWritten = true;
                }

                // Write the "@odata.mediaEtag": "ETAG"
                string mediaETag = mediaResource.ETag;
                if (mediaETag != null && !resourceState.MediaETagWritten)
                {
                    this.ODataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataMediaETag);
                    this.JsonWriter.WriteValue(mediaETag);
                    resourceState.MediaETagWritten = true;
                }
            }

            // TODO: actions
            // TODO: functions
            // TODO: association links
        }
        /// <summary>
        /// Writes the metadata properties for a resource which can only occur at the start.
        /// </summary>
        /// <param name="resourceState">The resource state for which to write the metadata properties.</param>
        internal void WriteResourceStartMetadataProperties(IODataJsonLightWriterResourceState resourceState)
        {
            Debug.Assert(resourceState != null, "resourceState != null");

            ODataResource resource = resourceState.Resource;

            // expectedResourceTypeName : if expected is of base type. but the resource real type is derived,
            // we need to set the resource type name.
            //      Writing response: expected type info can be identical with context uri info.
            //      Writing request: contextUri may not be provided, we always use the type from metadata info.
            //                       From model: if the resource can be found in model.
            //                       From serializationInfo: if user set the serializationInfo for the resource.
            string expectedResourceTypeName = null;

            if (this.WritingResponse)
            {
                expectedResourceTypeName = resourceState.GetOrCreateTypeContext(this.WritingResponse).ExpectedResourceTypeName;
            }
            else
            {
                if (resourceState.ResourceTypeFromMetadata == null)
                {
                    expectedResourceTypeName = resourceState.SerializationInfo == null
                     ? null
                     : resourceState.SerializationInfo.ExpectedTypeName;
                }
                else
                {
                    expectedResourceTypeName = resourceState.ResourceTypeFromMetadata.FullTypeName();
                }
            }

            // Write the "@odata.type": "typename"
            string typeName = this.JsonLightOutputContext.TypeNameOracle.GetResourceTypeNameForWriting(
                expectedResourceTypeName,
                resource,
                resourceState.IsUndeclared);

            if (typeName != null)
            {
                this.ODataAnnotationWriter.WriteODataTypeInstanceAnnotation(typeName);
            }

            // uncomment the below if decide to expose OData information via .InstanceAnnotations
            // else
            // {
            //    // close path for roundtrip :
            //    // write it to the wire if @odata.type is ever read from payload into Resource.InstanceAnnotations.
            //    ODataInstanceAnnotation odataTypeAnnotation = resource.InstanceAnnotations.FirstOrDefault(
            //        s => string.Equals(ODataAnnotationNames.ODataType, s.Name, StringComparison.OrdinalIgnoreCase));
            //    if (odataTypeAnnotation != null)
            //    {
            //        string rawTypeName = (string)odataTypeAnnotation.Value.FromODataValue();
            //        this.ODataAnnotationWriter.WriteODataTypeInstanceAnnotation(rawTypeName, writeRawValue: true);
            //    }
            // }
            // Write the "@odata.id": "Entity Id"
            Uri id;

            if (resource.MetadataBuilder.TryGetIdForSerialization(out id))
            {
                this.ODataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataId);
                if (id != null && !resource.HasNonComputedId)
                {
                    id = this.MetadataDocumentBaseUri.MakeRelativeUri(id);
                }

                this.JsonWriter.WriteValue(id == null ? null : this.UriToString(id));
            }

            // Write the "@odata.etag": "ETag value"
            string etag = resource.ETag;

            if (etag != null)
            {
                this.ODataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataETag);
                this.JsonWriter.WriteValue(etag);
            }
        }