Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OData Collection Parameter bindings ... do they actually work?

Tags:

c#

asp.net

odata

According to the docs here ...

http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v3/odata-actions

...

Binding an Action to an Entity Set

In the previous example, the action is bound to a single entity: The client rates a single product. You can also bind an action to a collection of entities. Just make the following changes:

In the EDM, add the action to the entity's Collection property.

var rateAllProducts = builder.Entity().Collection.Action("RateAllProducts"); In the controller method, omit the key parameter.

[HttpPost] public int RateAllProducts(ODataActionParameters parameters) { // .... }

Why when I do this does it not work ...

Invoice Ref:

public class InvoiceReference
{
    public string InvoiceNumber { get; set; }
    public int SupplierId { get; set; }
}

Action setup:

var getByRefs = Builder.EntityType<SIHead>().Collection.Action("ByRefs");
getByRefs.CollectionParameter<InvoiceReference>("refs");
getByRefs.ReturnsCollectionFromEntitySet<SIHead>("SIHead");

Action method in the controller:

[HttpPost]
[EnableQuery]
[ODataRoute("ByRefs")]
public async Task<IHttpActionResult> ByRefs(ODataActionParameters p)
{
   var refs = p["refs"] as InvoiceReference[];
   // exception p is null
}

Example json content posted:

[
  {
    "InvoiceNumber": "5100011759|9800006622",
    "SupplierId": 2
  },
  {
    "InvoiceNumber": "5100012624|9800006635",
    "SupplierId": 2
  },
  {
    "InvoiceNumber": "5100012625|9800006636",
    "SupplierId": 2
  }
]

Seems to me that either I missed something or OData is broken.

like image 701
War Avatar asked Jun 23 '16 13:06

War


1 Answers

After gettting some feedback from github (thanks Sam) I came to the conclusion that the way OData works means we must always post an object and never a collection directly ...

I missed some subtle / implied rules here ...

I have to provide an object (as a container) and not just the array I want to post. I can not bind directly to an ICollection, IList, List or Array only IEnumerable Out of curiosity: Why is this different to normal webAPI? The underlying binding framework in WebAPI's binding is awesome.

I'm not sure this "oddity" was / is well documented, it looks like no matter what I am posting I should always provide an object and never a collection directly in the body.

to post an array I therefore have to do ...

{ "foos": [1,2,3,4] }

.. instead of doing ...

[1,2,3,4]

... and then in the action always treat the posted collection as an Enumerable ...

Task PostStuff(ODataActionParameters p)
{
   var foos = p["foos"] as IEnumerable<Foo>;
   ...
}

... I'm pretty sure this example is given somewhere but i'm pretty sure this requirement that the body always contain an object is not (i could be wrong). I guess this is to encourage people to build strongly typed request bodies (feels like a good call IMO).

like image 120
War Avatar answered Oct 11 '22 18:10

War