Search code examples
odataasp.net-web-api-routing

Updating Navigation Property in oData WebAPI


I am using WebAPI oData. The requirement is to update the Navigation property of the entity.

public class Question
{
    public int QuestionId { get; set; }
    public string QuestionTitle { get; set; }
    public string QuestionBody { get; set; }
    public List<Response> Responses { get; set; } //navigation property
}

public class Response
{
    public string ResponseId { get; set; }
    public int QuestionId { get; set; } //fk
    public string ResponseBody { get; set; }
}

Now if I use the following link to fetch the responses it works in oData Webapi

GET - /odata/questions(1)/responses ----worked successfully. In the controller I added a action to handle this request as:

public IQueryable<Response> GetResponses([FromODataUri] Guid key)
{
    //
}

POST - /odata/questions(1)/responses ----This is not working; error message is: This service doesn't support OData requests in the form '~/entityset/key/navigation'

The method I added in the controller is:

public List<Responses> CreateResponses([FromODataUri] Guid key, List<Response> responses)
{
     //
}

How can I support adding/updating navigation properties in oData WebAPI


Solution

  • You need a custom routing convention to handle POST's to navigation properties. Code below,

    // routing convention to handle POST requests to navigation properties.
    public class CreateNavigationPropertyRoutingConvention : EntitySetRoutingConvention
    {
        public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
        {
            if (odataPath.PathTemplate == "~/entityset/key/navigation" && controllerContext.Request.Method == HttpMethod.Post)
            {
                IEdmNavigationProperty navigationProperty = (odataPath.Segments[2] as NavigationPathSegment).NavigationProperty;
                controllerContext.RouteData.Values["key"] = (odataPath.Segments[1] as KeyValuePathSegment).Value; // set the key for model binding.
                return "PostTo" + navigationProperty.Name;
            }
    
            return null;
        }
    }
    

    Registering the routing convention,

    var routingConventions = ODataRoutingConventions.CreateDefault();
    routingConventions.Insert(0, new CreateNavigationPropertyRoutingConvention());
    server.Configuration.Routes.MapODataRoute("odata", "", GetEdmModel(), new DefaultODataPathHandler(), routingConventions);
    

    The complete sample is here.