How to add, instance annotations to an ODataFeed?

Topics: ASP.NET Web API
Aug 10, 2014 at 12:49 PM
I am using ODataLib 6.6.0 and Microsoft.AspNet.OData 5.2.1 Nuget packages.

I have the following code which creates a custom serializer to add a custom annotation to a paged OData feed. The code compiles and is called correctly, all the way through the point of adding the custom instance annotation.
namespace WebApiTest.App_Start
using Microsoft.OData.Core;
using System.Collections.Generic;
using System.Web.Http;
using System.Web.OData.Formatter;
using System.Web.OData.Formatter.Deserialization;
using System.Web.OData.Formatter.Serialization;
using WebApiTest.Controllers;

public class FormatterConfiguration
    public static void Register(HttpConfiguration config)
        var formatters = ODataMediaTypeFormatters.Create(
            new CustomODataSerializerProvider(),
            new DefaultODataDeserializerProvider());
        config.Formatters.InsertRange(0, formatters);

public class CustomODataSerializerProvider : DefaultODataSerializerProvider
    private readonly CustomODataFeedSerializer _feedSerializer;

    public CustomODataSerializerProvider()
        _feedSerializer = new CustomODataFeedSerializer(this);

    public override ODataEdmTypeSerializer GetEdmTypeSerializer(Microsoft.OData.Edm.IEdmTypeReference edmType)
        var serializer = base.GetEdmTypeSerializer(edmType);
        var feedSerializer = serializer as ODataFeedSerializer;
        if (feedSerializer != null)
            return _feedSerializer;
        return serializer;

public class CustomODataFeedSerializer : ODataFeedSerializer
    public CustomODataFeedSerializer(ODataSerializerProvider serializerProvider)
        : base(serializerProvider)

    public override Microsoft.OData.Core.ODataFeed CreateODataFeed(System.Collections.IEnumerable feedInstance, Microsoft.OData.Edm.IEdmCollectionTypeReference feedType, ODataSerializerContext writeContext)
        ODataFeed feed = base.CreateODataFeed(feedInstance, feedType, writeContext);
        var result = feedInstance as IPageResultCustom;
        if (result != null)
            var value = new ODataComplexValue();
            var properties = new List<ODataProperty>();
            properties.Add(new ODataProperty() { Name = "CustomProperty", Value = result.CustomProperty });
            value.Properties = properties;
            feed.InstanceAnnotations.Add(new ODataInstanceAnnotation("org.CustomComplexValue", value));
        return feed;
I issue a request to a feed including the HTTP headers Prefer: odata.include-annotations="*" and Content-Type=application/json in the request. Yet, I get no annotations in the response.

Tracing through the OData framework code, the internal property Func<string,bool> Microsoft.OData.Core.MessageWriterSettings.ShouldInludeAnnotations is never set when creating the ODataMessageWriters and therefore, the ODataJsonLightValueSerializer.ShouldSkipAnnotation(string name) method call always returns true.
Aug 13, 2014 at 3:07 PM
Microsoft.AspNet.OData does not have logic to handle Prefer header yet, tracked by issue

But you can have a workaround currently. Microsoft.OData.Core.MessageWriterSettings.ShouldIncludeAnnotation which is an internal attribute will be set automatically if "Preference-Applied" header is set in response message. So normally if request has a Prefer header, corresponding response should have a "Preference-Applied" header. However, "Preference-Applied" header will not be set to response message in current implementation of ODataMediaTypeFormatter.

So you can create a custom MediaTypeFormatter inheriting from ODataMediaTypeFormatter, override WriteToStreamAsync() and GetPerRequestFormatterInstance(), and put prefer header handling logic into WriteToStreamAsync(). Following code is an example, where content.Headers will be used as response message header.
IEnumerable<string> headers;
if (Request.Headers.TryGetValues("Prefer", out headers))
       content.Headers.Add("Preference-Applied", headers);
Aug 17, 2014 at 9:51 PM
Your answer belies the difficulty in implementing this.

The fact that so many aspects of the ODataMediaTypeFormatter and ODataMediaTypeFormatters are internal, makes this next to impossible to accomplish. I would need to recreate nearly everything that ODataMediaTypeFormatters.Create() does (which raises copyright concern). If that method had been a generic that accepted subclasses of type ODataMediaTypeFormatter, it would have made things a lot simpler. Trying to duplicate the functionality of ODataMediaTypeFormatters.Create() as a generic isn't trivial because there is no default constructor for ODataMediaTypeFormatter.

So now I wind up creating the equivalent of ODataMediaTypeFormatters that is specific to my custom ODataMediaTypeFormatter and I wind up running into errors like
Error 89 The type 'System.Net.Http.Formatting.MediaTypeFormatterExtensions' exists in both 'c:\Code\Research and Development\vs2012\WebApiTest- Adding OData Branch\packages\Microsoft.AspNet.WebApi.Client.5.2.0\lib\net45\System.Net.Http.Formatting.dll' and 'c:\Code\Research and Development\vs2012\WebApiTest- Adding OData Branch\packages\Microsoft.AspNet.WebApi.Core.5.2.0\lib\net45\System.Web.Http.dll' c:\code\Research and Development\vs2012\WebApiTest- Adding OData Branch\WebApiTest\Code\OData\ODataMediaTypeFormatters.cs 102 13 WebApiTest
And MediaTypeHeaderValue is not cloneable because its Clone implementation is not public

I cannot even post the code here because of the length.

I can't see how this can be implemented without the bug being fixed internally.
Oct 2, 2014 at 12:10 PM
Setting the response header using a custom message handler may be possible. I have not tried it yet. See