Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 596
Jeldrik Avatar asked Jun 16 '15 15:06

Jeldrik


1 Answers

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.

like image 56
Magnus Karlsson Avatar answered Nov 15 '22 17:11

Magnus Karlsson