1

The scenario is an application that uses OData v4, server-side API based on an Entity Framework model using ODataLib, client-side using the OData client code generator extension for Visual Studio

I'm failing to get OData attribute routing working for deleting relationships for entities in m:m relationships, for which the client generates DELETE requests in the form:

http://developer4:8080/odata/tblTestRestaurant(241)/tblTestDishes/$ref?$id=http://developer4:8080/odata/tblTestDish(1)

Attribute routing for POST for creating a link works just fine - the related entity identifier is encoded in the body, and the following controller action method declaration works (the controller itself has [ODataRoutePrefix("tblTestRestaurant")]):

    [ODataRoute("({pRestaurantID})/tblTestDishes/$ref")]
    [HttpPost]
    [EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
    public async Task<IHttpActionResult> PostAttachtblTestDishes([FromODataUri] int pRestaurantID,
        [FromBody] Uri uri) { ... }

But I can't get something similar working for DELETE where the ID of the related entity is specified using the $ref?id=... syntax in the URL.

I have tried the following using the ODataRoute attribute:

    [ODataRoute("({pRestaurantID})/tblTestDishes/$ref")]
    [HttpDelete]
    public async Task<IHttpActionResult> TestRemoveRef1([FromODataUri] int pRestaurantID,
        [FromODataUri] Uri relatedUri)
    {
        throw new NotImplementedException();
    }
    [ODataRoute("({pRestaurantID})/tblTestDishes/$ref")]
    [HttpDelete]
    public async Task<IHttpActionResult> TestRemoveRef2([FromODataUri] int pRestaurantID,
        [FromODataUri] string relatedUri)
    {
        throw new NotImplementedException();
    }
    [ODataRoute("({pRestaurantID})/tblTestDishes/$ref?$id={pRelated}")]
    [HttpDelete]
    public async Task<IHttpActionResult> TestRemoveRef3([FromODataUri] int pRestaurantID,
        [FromODataUri] string pRelated)
    {
        throw new NotImplementedException();
    }
    [ODataRoute("({pRestaurantID})/tblTestDishes/$ref?$id={pRelated}")]
    [HttpDelete]
    public async Task<IHttpActionResult> TestRemoveRef4([FromODataUri] int pRestaurantID,
        [FromODataUri] Uri pRelated)
    {
        throw new NotImplementedException();
    }

But none of the above controller actions get hit in response to a DELETE request to http://developer4:8080/odata/tblTestRestaurant(241)/tblTestDishes/$ref?$id=http://developer4:8080/odata/tblTestDish(1).

The only way I can get it working is not to use attribute routing but instead to rely on the OData routing conventions, ie

    [HttpDelete]
    public async Task<IHttpActionResult> DeleteRef([FromODataUri] int key,
        [FromODataUri] string relatedKey, string navigationProperty)
    {
        throw new NotImplementedException();
    }

This method relies on testing the string navigationProperty to work out which collection navigation property on the entity to modify - instead I would prefer to use attribute routing and have a separate action method in my controller for each collection navigation property.

I've used a number of tutorials and documentation in particular https://damienbod.wordpress.com/2014/06/10/getting-started-with-web-api-and-odata-v4/

I have also been through some of the OData WebApi test cases, particularly this one which uses a mixture of attribute routing and OData routing conventions - but doesn't contain an example for attribute routing for deleting links.

So my question is - what ODataRoute attribute syntax and method parameters should I be using, assuming that the ODataRoute attribute does support this ...$ref?id=... syntax in the URL for deletes; and if it doesn't then what alternatives are there?

stebay
  • 13
  • 5

1 Answers1

2

Web API OData parse the Uri in $Id to create a key segment appended to the origin path segments. So, If you change the template as below, it should work:

[ODataRoute("({pRestaurantID})/tblTestDishes({pRelated})/$ref")]
[HttpDelete]
public async Task<IHttpActionResult> TestRemoveRef([FromODataUri] int pRestaurantID, [FromODataUri] int pRelated)
{
        ...
}

You can refer to my sample project here. Hope it can help you. Thanks.

Sam Xu
  • 3,264
  • 1
  • 12
  • 17
  • Your template does work,Thanks。But does $id work in adding reference scene? – user1729842 Jul 09 '15 at 15:25
  • What's the adding reference scene? you mean "Post"? – Sam Xu Jul 10 '15 at 00:48
  • This works great, thank you very much. As an aside, is there any kind of debugging tool I could use to determine how OData has translated the incoming request - ie so that I can work out for other scenarios what the route attribute should be? – stebay Jul 13 '15 at 09:54
  • @stebay, you're welcome. For debug, I am sorry there isn't such tool. However, ODataTeam publishes the symbols along with nugget packages. So, you can debug not only Web API OData codes, but also ODL codes by setting `http://srv.symbolsource.org/pdb/Public` and `http://srv.symbolsource.org/pdb/MyGet` into your symbol files search location. – Sam Xu Jul 14 '15 at 01:27