19

Is it possible to get OData to do the following? I would like to be able to query a REST call by passing on parameters that may not be the primary key. Can I call a REST method like --> GetReports(22, 2014) or Reports(22, 2014)?

[HttpGet]
[ODataRoute("Reports(Id={Id}, Year={Year})")]
public IHttpActionResult GetReports([FromODataUri]int Id, [FromODataUri]int Year)
{
    return Ok(_reportsRepository.GetReports(Id, Year));
}

Here is my latest change.

//Unbound Action  OData v3
var action = builder.Action("ListReports");
action.Parameter<int>("key");
action.Parameter<int>("year");
action.ReturnsCollectionFromEntitySet<Report>("Reports");

my method for controller ReportsController

[HttpPost]
[EnableQuery]
public IHttpActionResult ListReports([FromODataUri] int key, ODataActionParameters parameters)
{
    if (!ModelState.IsValid)
    {
        throw new HttpResponseException(HttpStatusCode.BadRequest);
    }

    int year = (int)parameters["year"];
    
    return Ok(_reportsRepository.GetReports(key, year).Single());
}

I tried calling the url like:

http://localhost:6064/odata/Reports(key=5,year=2014)/ListReports

No HTTP resource was found that matches the request URI 'http://localhost:6064/odata/Reports(key%3D5%2Cyear%3D2014)/ListReports'.`

spaleet
  • 838
  • 2
  • 10
  • 23
Nate
  • 2,044
  • 4
  • 23
  • 47

2 Answers2

34

You can define a function import named GetReports that has two parameters.

(Note: the name of the function import can't be the same with entity set name)

Configure your EDM model as:

var builder = new ODataConventionModelBuilder();
builder.EntitySet<Report>("Reports");
var function = builder.Function("GetReports");
function.Parameter<int>("Id");
function.Parameter<int>("Year");
function.ReturnsCollectionFromEntitySet<Report>("Reports");
var model = builder.GetEdmModel();

And then write your method as:

[HttpGet]
[ODataRoute("GetReports(Id={Id},Year={Year})")]
public IHttpActionResult WhateverName([FromODataUri]int Id, [FromODataUri]int Year)
{
    return Ok(_reportsRepository.GetReports(Id, Year));
}

Then the request

Get ~/GetReports(Id=22,Year=2014)

will work.

soccer7
  • 3,547
  • 3
  • 29
  • 50
Feng Zhao
  • 2,977
  • 1
  • 14
  • 20
  • How can this be done with Odatav3? v3 does not have a function. – Nate Jul 10 '14 at 05:22
  • I have to use Odata v3 because I am using Jaydata and it sems to only work with v3. If I use Jaydata with Odata v4 it raises a 404 error. Any solution with v3?Thanks – Nate Jul 10 '14 at 06:11
  • 1
    With v3, you can define an action. If you define an unbound action, you have to use HttpPost and pass the parameters through the request body, and you need to add a method "HandleUnmappedRequest" in the controller to match the unbound action request. If you define it as a bound action bound to entity set although this doesn't make sense, you can add a method "[ActionName]OnCollectioinOf[EntityTypeName]" and call HttpPost "~/[EntitySetName]/[ActionName]", and pass the parameters in the request body. – Feng Zhao Jul 10 '14 at 06:25
  • Thanks Feng. Do you have an example somewhere that I can look at? Any blogs showing how to create an unbound action? – Nate Jul 10 '14 at 13:36
  • There are many samples on https://aspnet.codeplex.com/SourceControl/latest#Samples/ReadMe.txt For v3 action sample, check this https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v3/ODataActionsSample/ODataActionsSample/ReadMe.txt – Feng Zhao Jul 10 '14 at 14:51
  • Thanks but this example does not override HandleUnmappedRequest which you indicated should be done. I have not found any clear examples on overriding and using HandleUnmappedRequest. Is an override of HandleUnmappedRequest needed? – Nate Jul 10 '14 at 15:04
  • This sample uses another way to route unbound action, which is adding a customer routing convention "NonBindableActionRoutingConvention" to route the unbound action request. For HandleUnmappedRequest, check the System.Web.Http.OData.Routing.RoutingCustomersController.HandleUnmappedRequest: https://aspnetwebstack.codeplex.com/SourceControl/latest#test/System.Web.Http.OData.Test/OData/Routing/ODataRoutingTest.cs – Feng Zhao Jul 10 '14 at 15:23
  • I changed method to ListReports.error--No HTTP resource was found that matches the request URI see above for details – Nate Jul 10 '14 at 16:08
  • Feng, The problem with the first v3 example that you linked to is that it does not show how to call the SetDueDate Action from JavaScript, which is what I think many people are looking for when they come to this post. – Robert McLaws Aug 04 '14 at 03:25
  • Is it possible to implement parameters on get method? Method name would just get and with ODataRoute. – gee'K'iran Sep 02 '15 at 10:33
2

For OData v4.0 endpoints, you don't have to make it a function, you can simply do...

public class ReportsController : ODataController
{
    [EnableQuery]
    [ODataRoute("Reports({id}, {year})")]
    public IQueryable<ReportModel> Get([FromODataUri] int id, [FromODataUri] int year)
    {
        ...
    }
}

Then you can call it like...

/Reports(42, 2019)