如何从字符串解析ODataQueryOptions? [英] How can I parse ODataQueryOptions from a string?

查看:443
本文介绍了如何从字符串解析ODataQueryOptions?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须在ASP.NET API上为符合OData规范的EF6实体提供一些读取终结点。实体检索基于接受 System.Web.Http.OData.Query.ODataQueryOptions< TEntity> 实例。

I have to provide some read endpoints for our EF6 entities on an ASP.NET API that conform to the OData specification. Entity retrieval works well based upon functions that accept a System.Web.Http.OData.Query.ODataQueryOptions<TEntity> instance.

现在,根据文档,OData的实现不支持 $ count

Now, according to the docs, that implementation of OData does not support $count.

但是,至少要提供检索(过滤的)数据集总数的功能,如 ,例如(通过稍微组合其中的一些示例):

We would, however, like to offer at least the capability to retrieve the total count of a (filtered) data set as shown in the docs, like (by slightly combining several of those samples):

http://host/service/Products/$count($filter=Price gt 5.00)

(根据规格,我知道这应该是有效的,符合规格的OData查询,以查询价格大于5的产品数量¤。如果我错了,请纠正我。)

(Based on the spec, I understand that this should be a valid, specification-conformant OData query for the number of products whose price is greater than 5¤. Please correct me if I'm wrong.)

现在,根据从<返回的 IQueryable 检索计数code> ODataQuerySettings.ApplyTo 很简单。 捕获对此路由的请求也是如此:

Now, retrieving the count based on the IQueryable returned from ODataQuerySettings.ApplyTo is trivial. So is capturing requests to this route:

[Route("$count({queryOptions})")]
public int Count(ODataQueryOptions<ProductEntity> queryOptions)

缺少的是,应将路由的 queryOptions 部分解析为 ODataQueryOptions< ProductEntity> 实例。在其他OData终结点上,此操作无需费力。但是,即使我使用 $ filter 调用此端点,我得到的只是一个空(即初始化为默认值) ODataQueryOptions<没有过滤器集的ProductEntity> 实例。

The only bit that is missing is that the queryOptions portion of the route should be parsed into the ODataQueryOptions<ProductEntity> instance. On other OData endpoints, this works without any further ado. However, even when I call this endpoint with a $filter, all I am getting is an "empty" (i.e. initialized to default values) ODataQueryOptions<ProductEntity> instance with no filter set.

或者,我可以这样声明我的Web API端点:

Alternatively, I can declare my web API endpoint like this:

[Route("$count({queryOptions})")]
public int Count(string rawQueryOptions)

在此方法中, rawQueryOptions 包含我希望传递给OData的查询选项,也就是说,解析以填充 ODataQueryOptions< ProductEntity> 实例。

Within this method, rawQueryOptions contains the query options that I wish to pass to OData, that is, parse to populate an ODataQueryOptions<ProductEntity> instance.

这必须非常简单其他任何OData端点都会发生这种情况。为了进行比较:

This must be very straightforward as the very same thing happens for any other OData endpoint. For a comparison:

[Route("")]
public IEnumerable<object> Filter(ODataQueryOptions<ProductEntity> queryOptions)

这可行;查询选项将按预期填充,这与我的上述端点不同。

This works; the query options are populated as expected, unlike it is the case with my above endpoint.

如何基于提取的字符串填充OData查询选项实例

我尝试过的其他操作:


  • [FromUri] 应用于 queryOptions 参数。

  • 已应用 [ODataQueryParameterBinding] queryOptions 参数。

  • Applied [FromUri] to the queryOptions parameter.
  • Applied [ODataQueryParameterBinding] to the queryOptions parameter.

推荐答案

鉴于您已经在ASP.Net API上安装了EF6 ...为什么不只是将OData控制器公开在另一条路由上并使它们变为只读?这样,您就可以完全实现查询支持

Given that you have EF6 already on an ASP.Net API... Why not just expose the OData controllers on another route and make them readonly? In this way you get the full implementation of query support


通过完全不执行Post / Patch / Delete方法,可以将您的控制器设为只读。因此,在VS中使用Scaffold构建默认的OData控制器,并删除您不希望支持的端点。

Your controllers can be made readonly by not implementing the Post/Patch/Delete methods at all. So use the Scaffold in VS to build a default OData controller and remove the endpoints that you don't want to support.


public partial class ResidentsController : ODataController
{
    protected MyEF.Context db = new MyEF.Context();    

    [EnableQuery]
    public async Task<IHttpActionResult> Get(ODataQueryOptions<MyEF.Resident> options)
    { 
        return Ok(db.Residents);
    }
}

尽管不支持您期望的过滤集合计数语法,但是OOTB支持的语法非常接近,因此您可以使用URL重写模块轻松地操作查询

although your expected syntax for count of filtered collections is not supported OOTB, the syntax that is supported is very close, so you could easily manipulate the query with a URL re-write module


  • 您期望的语法:

    http:// host / service / Products / $ count($ filter = Price gt 5.00)

  • .Net支持的语法

    http:// host / service / Products / $ count?$ filter =价格gt 5.00

  • Your expected syntax:
    http://host/service/Products/$count($filter=Price gt 5.00)
  • .Net Supported syntax
    http://host/service/Products/$count?$filter=Price gt 5.00

OwinMiddleware:

/// <summary>
/// Rewrite incoming OData requests that are implemented differently in the .Net pipeline
/// </summary>
public class ODataConventionUrlRewriter : OwinMiddleware
{
    private static readonly PathString CountUrlSegments = PathString.FromUriComponent("/$count");

    public ODataConventionUrlRewriter(OwinMiddleware next)
    : base(next)
    {
    }

    public override async Task Invoke(IOwinContext context)
    {
        // OData spec says this should work:                http://host/service/Products/$count($filter=Price gt 5.00)
        // But in .Net the filter needs to be in the query: http://host/service/Products/$count?$filter=Price gt 5.00
        var regex = new System.Text.RegularExpressions.Regex(@"\/\$count\((.+)\)$");
        var match = regex.Match(context.Request.Path.Value);
        if(match != null && match.Success)
        {
            // So move the $filter expression to a query option
            // We have to use redirect here, we can't affect the query inflight
            context.Response.Redirect($"{context.Request.Uri.GetLeftPart(UriPartial.Authority)}{regex.Replace(context.Request.Path.Value, "/$count")}?{match.Groups[1].Value}");
        }
        else
            await Next.Invoke(context);
    }
}

添加到Startup.cs,然后注册OData路由

app.Use(typeof(ODataConventionUrlRewriter));

这篇关于如何从字符串解析ODataQueryOptions?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆