【问题标题】:OData Collection Parameter bindings ... do they actually work?OData 集合参数绑定......它们真的有效吗?
【发布时间】:2026-01-07 18:10:02
【问题描述】:

根据此处的文档 ...

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

...

将动作绑定到实体集

在前面的示例中,操作绑定到单个实体: 客户对单个产品进行评分。您还可以将操作绑定到 实体的集合。只需进行以下更改:

在 EDM 中,将操作添加到实体的 Collection 属性中。

var rateAllProducts = builder.Entity().Collection.Action("RateAllProducts");在里面 控制器方法,省略key参数。

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

为什么当我这样做时它不起作用......

发票参考:

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

动作设置:

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

控制器中的动作方法:

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

发布的示例 json 内容:

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

在我看来,要么我错过了什么,要么 OData 坏了。

【问题讨论】:

  • 尝试发布如下内容,您需要在帖子内容中指定带有JSON格式值的参数名称,这应该可以,{“refs”:[{“InvoiceNumber”:“5100011759|9800006622 ", "SupplierId": 2 }, { "InvoiceNumber": "5100012624|9800006635", "SupplierId": 2 }, { "InvoiceNumber": "5100012625|9800006636", "SupplierId": 2 } ] }
  • 是的,我做到了,不起作用......我也在 github 上提出了这个问题:github.com/OData/WebApi/issues/758

标签: c# asp.net odata


【解决方案1】:

在从 github 获得一些反馈后(感谢 Sam),我得出的结论是 OData 的工作方式意味着我们必须始终发布一个对象,而不是直接发布一个集合......

我在这里错过了一些微妙/隐含的规则......

我必须提供一个对象(作为容器),而不仅仅是我要发布的数组。 我不能直接绑定到 ICollection、IList、List 或 Array only IEnumerable 出于好奇:为什么这与普通的 webAPI 不同? WebAPI 绑定中的底层绑定框架很棒。

我不确定这个“奇怪”是否/是否有据可查,看起来无论我发布什么,我都应该始终提供一个对象,而不是直接在正文中提供一个集合。

因此我必须发布一个数组...

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

.. 而不是做...

[1,2,3,4]

... 然后在操作中始终将发布的集合视为 Enumerable ...

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

...我很确定这个例子是在某个地方给出的,但我很确定这个身体总是包含一个对象的要求不是(我可能是错的)。 我想这是为了鼓励人们构建强类型的请求体(感觉像是一个很好的调用 IMO)。

【讨论】: