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?
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.