support of @odata.bind in asp.net web api (link to existing entity during post)
Asked Answered
M

1

9

I'm having a really hard time migrating from WCF data service to web api odata v4. I'm stuck on the following issue:

odata web api doesn't seem to support @odata.bind.

I've found the following link: https://github.com/OData/WebApi/issues/158 There someone suggested to implement an own ODataEntityDeserializer derived class like so:

public class ExtendedODataEntityDeserializer : ODataEntityDeserializer
{
    public ExtendedODataEntityDeserializer(ODataDeserializerProvider deserializerProvider) : base(deserializerProvider)
    {
    }

    public override void ApplyNavigationProperty(
        object entityResource, 
        ODataNavigationLinkWithItems navigationLinkWrapper, 
        IEdmEntityTypeReference entityType, 
        ODataDeserializerContext readContext)
    {
        base.ApplyNavigationProperty(entityResource, navigationLinkWrapper, entityType, readContext);

        foreach (var childItem in navigationLinkWrapper.NestedItems)
        {
            var entityReferenceLink = childItem as ODataEntityReferenceLinkBase;

            if (entityReferenceLink != null)
            {
                var navigationPropertyName = navigationLinkWrapper.NavigationLink.Name;
                Uri referencedEntityUrl = entityReferenceLink.EntityReferenceLink.Url;

                if (!referencedEntityUrl.IsAbsoluteUri)
                {
                    referencedEntityUrl = new Uri(readContext.Request.RequestUri, referencedEntityUrl);
                }

                var linkedEntities = (Model.LinkedEntityCollection)entityResource;
                linkedEntities.Add(navigationPropertyName, referencedEntityUrl);
            }
        }
    }
}

LinkedEntityCollection is a base class for my entity classes that serves as dictionary (MyEntity : LinkedEntityCollection implementation is trivial).

public class ExtendedODataDeserializerProvider : ODataDeserializerProvider
{
    private static ExtendedODataDeserializerProvider _instance = null;

    private ExtendedODataDeserializerProvider()
    {
        _instance = this;
    }

    public static ExtendedODataDeserializerProvider Instance
    {
        get { return _instance ?? new ExtendedODataDeserializerProvider(); }
    }

    public override ODataEdmTypeDeserializer GetEdmTypeDeserializer(Microsoft.OData.Edm.IEdmTypeReference edmType)
    {
        return DefaultODataDeserializerProvider.Instance.GetEdmTypeDeserializer(edmType);
    }

    public override ODataDeserializer GetODataDeserializer(Microsoft.OData.Edm.IEdmModel model, Type type, System.Net.Http.HttpRequestMessage request)
    {
        return new ExtendedODataEntityDeserializer(DefaultODataDeserializerProvider.Instance);
    }
}

Registering in the http configuration:

public static void Register(HttpConfiguration config)
{
    ODataModelBuilder builder = new ODataConventionModelBuilder();

    // registering entities: builder.EntitySet<T>("EntityName") ...

    ODataBatchHandler batchHandler = new DefaultODataBatchHandler(new HttpServer(config));
    batchHandler.MessageQuotas.MaxOperationsPerChangeset = 10;
    batchHandler.MessageQuotas.MaxPartsPerBatch = 10;

    var odataFormatters = ODataMediaTypeFormatters.Create(DefaultODataSerializerProvider.Instance, ExtendedODataDeserializerProvider.Instance);

    config.Formatters.Clear();
    config.Formatters.AddRange(odataFormatters);

    config.MapODataServiceRoute(routeName: "Central", routePrefix: "Odata", model: builder.GetEdmModel(), batchHandler: batchHandler);
}

However if you do use this hack you loose the capability of creating links between enities like described in this aritcle: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/entity-relations-in-odata-v4

The "@odata.id" doesn't get parsed, it is assumed that it's an entity in the function ODataEntityDeserializer.Read. Maybe I'm missing somthing fundamentally here, since I'm fairly new to web api (config.Formatters.Clear() seems a bit harsh). Or is there any other workaround to support @odata.bind?

Is there any chance that this feature gets implemented in the near future?

Update

I got my hack working. The Problem was that I should have derrived ExtendedODataDeserializerProvider from DefaultODataDeserializerProvider instead of ODataDeserializerProvider and return the deserializers accordingly:

public override ODataDeserializer GetODataDeserializer(
     Microsoft.OData.Edm.IEdmModel model, 
     Type type,     
     System.Net.Http.HttpRequestMessage request)
{
    if (type == typeof(ODataActionParameters) || 
        type == typeof(ODataUntypedActionParameters) || 
        type == typeof(Uri))
    {
        return base.GetODataDeserializer(model, type, request);
    }

    return new ExtendedODataEntityDeserializer(DefaultODataDeserializerProvider.Instance);
 }

So the question that remains: when do we get @odata.bind support in asp.net webapi odata?

Malicious answered 16/6, 2015 at 15:30 Comment(2)
I'm in a situation where I have my custom serialier and want to register it. I am using config.Formatters.InsertRange(0,mycustomformatter). However, it doesn't work if I don't Clear() before that. My question to you: are you still clearing before you add your formatter or did you find an alternative way?Glomeration
I'm still using it as described above (clear and then add formatters). The only change I've made since then is that I'm adding also the JsonMediaTypeFormatter and XmlMediaTypeFormatter to the Formatter collection. Since this works for me, I've stopped looking for an alternative solution.Malicious
D
1

Trying to answer

So the question that remains: when do we get @odata.bind support in asp.net webapi odata?

There seems to be a feature relating to this in the vnext repo. However, the issue is marked as having low impact. https://github.com/OData/WebApi/milestones/vNext and hence there is no way to know when this feature will be implemented.

Disraeli answered 28/6, 2015 at 20:35 Comment(3)
Thanks for this. It would be cool if someone from the aps.net web api odata team could make a Statement on this. I find this Feature quite relevant, especially if you have to migrate an existing WCF Data ServiceMalicious
@Malicious I think they will respond in the GitHub repo if you create a new issue stating your concern. Best of luck to you!Disraeli
Thanks again. There is already an open issue on github (github.com/OData/WebApi/issues/158) with little to none response from the asp.net web api odata team. It seems that the feature was scheduled for V5.6 and is currently postponed for V5.7 (but I'm still sceptical)Malicious

© 2022 - 2024 — McMap. All rights reserved.