Search code examples
c#asp.net-web-apiodataasp.net-web-api-routingasp.net-web-api-odata

The path template on the action in controller is not a valid OData path template


I am getting the following error:

The path template 'GetClients()' on the action 'GetClients' in controller 'Clients' is not a valid OData path template. Resource not found for the segment 'GetClients'.

My controller method looks like this

public class ClientsController : ODataController
{
    [HttpGet]
    [ODataRoute("GetClients(Id={Id})")]
    public IHttpActionResult GetClients([FromODataUri] int Id)
    {
        return Ok(_clientsRepository.GetClients(Id));
    }
}

My WebAPIConfig file has

builder.EntityType<ClientModel>().Collection
       .Function("GetClients")
       .Returns<IQueryable<ClientModel>>()
       .Parameter<int>("Id");

config.MapODataServiceRoute(
    routeName: "ODataRoute",
    routePrefix: "odata",
    model: builder.GetEdmModel());

I am hoping to be able to call the odata rest api like this:

http://localhost/odata/GetClients(Id=5)

Any idea what I am doing wrong?


Solution

  • You don't even need to add such a function to get an entity.

    builder.EntitySet<ClientModel>("Clients")
    

    is all you need.

    And then write your action as:

    public IHttpActionResult GetClientModel([FromODataUri] int key)
    {    
          return Ok(_clientsRepository.GetClients(key).Single());
    }
    

    Or

    This is what worked. The above did not work:

    public IHttpActionResult Get([FromODataUri] int key)
    {    
        return Ok(_clientsRepository.GetClients(key).Single());
    }
    

    Then the Get request

    http://localhost/odata/Clients(Id=5)
    

    or

    http://localhost/odata/Clients(5)
    

    will work.

    Update: Use unbound function to return many ClientModels.

    The follow code is for v4. For v3, you can use action.

    builder.EntitySet<ClientModel>("Clients");
    var function = builder.Function("FunctionName");
    function.Parameter<int>("Id");
    function.ReturnsCollectionFromEntitySet<ClientModel>("Clients");
    

    Add a method in the controller like:

    [HttpGet]
    [ODataRoute("FunctionName(Id={id})")]
    public IHttpActionResult WhateverName(int id)
    {
        return Ok(_clientsRepository.GetClients(id));
    }
    

    Send a request like:

    GET ~/FunctionName(Id=5)