OData v4 WebAPI 响应中的项目计数
Posted
技术标签:
【中文标题】OData v4 WebAPI 响应中的项目计数【英文标题】:Items count in OData v4 WebAPI response 【发布时间】:2015-02-17 03:49:24 【问题描述】:如何在 OData v4 HTTP 响应中返回项目数?
我需要这个数字来分页,所以它应该是过滤后的项目数,但在'skip'和'top'之前。
我已经尝试在 url (https://damienbod.wordpress.com/2014/06/13/web-api-and-odata-v4-queries-functions-and-attribute-routing-part-2/ - "Example of $count") 中的查询选项中传递 '$inlinecount=allpages' 和 '$count=true' 参数,但我来自 WebAPI 的响应始终只有查询结果 (集合) - 整个响应看起来像:
[
"Name":"name1",
"age":5
,
"Name":"name2",
"age":15
]
响应中没有类似“odata.count”的内容。
我还尝试在我的 WebAPI 控制器操作中返回 PageResult 而不是 IQueryable(如下所述:http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options#server-paging),但 Request.GetInlineCount() 已被弃用,其值始终为 null。
有什么想法吗?
[更新] 我刚刚在这里发现了同样的问题:WebApi with Odata NextPage and Count not appearing in the JSON response 并且我删除了 [EnableQuery] 属性,现在我的回复看起来像:
"Items":
[
"Name":"name1",
"age":5
,
"Name":"name2",
"age":15
],
"NextPageLink":null,
"Count":null
但“计数”始终为空。 :(
编辑:在我的控制器的请求属性中调试和搜索计数值后,我发现正确的计数值位于名为“System.Web.OData.TotalCount”的属性中。所以现在我从那个请求属性中提取这个值,我的控制器看起来像这样:
public PageResult<People> Get(ODataQueryOptions<People> queryOptions)
var query = _context.People.OrderBy(x => x.SomeProperty);
var queryResults = (IQueryable<People>)queryOptions.ApplyTo(query);
long cnt = 0;
if (queryOptions.Count != null)
cnt = long.Parse(Request.Properties["System.Web.OData.TotalCount"].ToString());
return new PageResult<People>(queryResults, null, cnt);
它工作正常,但我仍然不知道为什么我必须使用这样的解决方法。
【问题讨论】:
如果您可以附加您为获取实体集而编写的控制器方法会很有帮助。 【参考方案1】:请您看一下https://github.com/OData/ODataSamples/blob/master/Scenarios/TripPin 上的示例服务 TripPin web api 实现。您可以按照 Airports 控制器中的代码进行操作,代码为 http://services.odata.org/TripPinWebApiService/Airports?$count=true 的服务可以正确返回计数。
【讨论】:
它适用于 TripPin,但它仍然无法在我的应用程序中运行。 :( 在我的控制器的请求属性中调试和搜索计数值后,我发现正确的计数值在名为“System.Web.OData.TotalCount”的属性中。所以现在我从该请求属性中提取这个值:@ 987654323@(有关此解决方法的更多信息在我上面编辑的帖子中) 是否可以在服务器级别设置默认的$count=true?【参考方案2】:这就是我在 oData v4 中使用的:
Request.ODataProperties().NextLink,
Request.ODataProperties().TotalCount
【讨论】:
你能提供一个更详细的例子吗?【参考方案3】:供将来参考(OData v4):
首先$inlinecount
不支持OData v4
,所以你应该改用$count=true
。
其次,如果你有一个普通的ApiController
并且你返回一个像IQueryable<T>
这样的类型,你可以将count
属性附加到返回的结果:
using System.Web.OData;
using System.Web.OData.Query;
using System.Web.OData.Extensions;
//[EnableQuery] // -> If you enable globally queries does not require this decorator!
public IHttpActionResult Get(ODataQueryOptions<People> queryOptions)
var query = _peopleService.GetAllAsQueryable(); //Abstracted from the implementation of db access. Just returns IQueryable<People>
var queryResults = (IQueryable<People>)queryOptions.ApplyTo(query);
return Ok(new PageResult<People>(queryResults, Request.ODataProperties().NextLink, Request.ODataProperties().TotalCount));
注意:
ApiController
s 不支持 OData 功能,因此您 不能有count
或$metadata
之类的东西。如果你选择 使用简单的ApiController
上面的方式是你应该使用的方式 返回count
属性。
要完全支持 OData 功能,您应该通过以下方式实现 ODataController
:
PeopleController.cs
using System.Web.OData;
using System.Web.OData.Query;
public class PeopleController : ODataController
[EnableQuery(PageSize = 10, AllowedQueryOptions = AllowedQueryOptions.All)]
public IHttpActionResult Get()
var res = _peopleService.GetAllAsQueryable();
return Ok(res);
App_Start \ WebApiConfig.cs
public static void ConfigureOData(HttpConfiguration config)
//OData Models
config.MapODataServiceRoute(routeName: "odata", routePrefix: null, model: GetEdmModel(), batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
config.EnsureInitialized();
private static IEdmModel GetEdmModel()
var builder = new ODataConventionModelBuilder
Namespace = "Api",
ContainerName = "DefaultContainer"
;
builder.EntitySet<People>("People").EntityType.HasKey(item => item.Id); //I suppose the returning list have a primary key property(feel free to replace the Id key with your key like email or whatever)
var edmModel = builder.GetEdmModel();
return edmModel;
然后您以这种方式访问您的 OData Api(示例):
编码的 uri:
http://localhost:<portnumber>/People/?%24count=true&%24skip=1&%24top=3
解码:
http://localhost:<portnumber>/People/?$count=true&$skip=1&$top=3
参考资料:
How to Use Web API OData to Build an OData V4 Service without Entity Framework Web API OData V4 Pitfalls Create an OData v4 Endpoint Using ASP.NET Web API 2.2【讨论】:
如需进一步参考 ASP.NET Core,可在Request.ODataFeature().NextLink
找到下一个链接【参考方案4】:
这也可以通过动作过滤器来实现:
/// <summary>
/// Use this attribute whenever total number of records needs to be returned in the response in order to perform paging related operations at client side.
/// </summary>
public class PagedResultAttribute: ActionFilterAttribute
/// <summary>
///
/// </summary>
/// <param name="actionExecutedContext"></param>
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
base.OnActionExecuted(actionExecutedContext);
if (actionExecutedContext.Response != null)
dynamic responseContent=null;
if (actionExecutedContext.Response.Content != null)
responseContent = actionExecutedContext.Response.Content.ReadAsAsync<dynamic>().Result;
var count = actionExecutedContext.Response.RequestMessage.ODataProperties().TotalCount;
var res = new PageResult<dynamic>() TotalCount=count,Items= responseContent ;
HttpResponseMessage message = new HttpResponseMessage();
message.StatusCode = actionExecutedContext.Response.StatusCode;
var strMessage = new StringContent(JsonConvert.SerializeObject(res), Encoding.UTF8, "application/json");
message.Content = strMessage;
actionExecutedContext.Response = message;
而自定义的PageResult类是:
public class PageResult<T>
public long? TotalCount get; set;
public T Items get; set;
用法:
[PagedResult]
[EnableQuery()]
【讨论】:
以上是关于OData v4 WebAPI 响应中的项目计数的主要内容,如果未能解决你的问题,请参考以下文章
WebAPI 2.2 中没有命名空间的 OData v4 自定义函数