我希望能够修改控制器内部的过滤器,然后根据更改后的过滤器返回数据。
因此,我在服务器端有一个ODataQueryOptions参数,可以用来查看FilterQueryOption。
让我们假设过滤器是这样的"$filter=ID eq-1",但在服务器端,如果我看到ID为"-1",这告诉我用户想要选择所有记录。
我试图将"$filter=ID eq-1"更改为"$filter=ID ne-1",这将通过设置filter.RawValue来提供所有信息,但这是只读的
我试图创建一个新的FilterQueryOption,但这需要ODataQueryContext和ODataQueryOptionParser,我不知道如何创建。
然后,我尝试将Filter设置为Null,然后将ApplyTo设置为Null。当我在控制器中设置断点并在即时窗口上检查时,ApplyTo似乎可以工作,但一旦它离开控制器上的GET方法,它就会"恢复"到URL中传递的内容。
这篇文章讨论了做一些非常类似的事情"修改WebAPI OData QueryOptions.Filter的最佳方法",但一旦它离开控制器GET方法,它就会恢复到URL查询筛选器。
使用样本代码更新
[EnableQuery]
[HttpGet]
public IQueryable<Product> GetProducts(ODataQueryOptions<Product> queryOptions)
{
if (queryOptions.Filter != null)
{
var url = queryOptions.Request.RequestUri.AbsoluteUri;
string filter = queryOptions.Filter.RawValue;
url = url.Replace("$filter=ID%20eq%201", "$filter=ID%20eq%202");
var req = new HttpRequestMessage(HttpMethod.Get, url);
queryOptions = new ODataQueryOptions<Product>(queryOptions.Context, req);
}
IQueryable query = queryOptions.ApplyTo(db.Products.AsQueryable());
return query as IQueryable<Product>;
}
运行此代码不会返回任何产品,这是因为URL中的原始查询需要产品1,我将产品1的ID筛选器与产品2交换。
现在,如果我运行SQL Profiler,我可以看到它添加了类似于"Select*from Product WHERE ID=1 AND ID=2"的内容。
但是如果我用同样的方法替换$top,那么效果很好。
[EnableQuery]
[HttpGet]
public IQueryable<Product> GetProducts(ODataQueryOptions<Product> queryOptions)
{
if (queryOptions.Top != null)
{
var url = queryOptions.Request.RequestUri.AbsoluteUri;
string filter = queryOptions.Top.RawValue;
url = url.Replace("$top=2", "$top=1");
var req = new HttpRequestMessage(HttpMethod.Get, url);
queryOptions = new ODataQueryOptions<Product>(queryOptions.Context, req);
}
IQueryable query = queryOptions.ApplyTo(db.Products.AsQueryable());
return query as IQueryable<Product>;
}
结束结果
在微软的帮助下。以下是支持筛选、计数和分页的最终输出。
using System.Net.Http;
using System.Web.OData;
using System.Web.OData.Extensions;
using System.Web.OData.Query;
/// <summary>
/// Used to create custom filters, selects, groupings, ordering, etc...
/// </summary>
public class CustomEnableQueryAttribute : EnableQueryAttribute
{
public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
{
IQueryable result = default(IQueryable);
// get the original request before the alterations
HttpRequestMessage originalRequest = queryOptions.Request;
// get the original URL before the alterations
string url = originalRequest.RequestUri.AbsoluteUri;
// rebuild the URL if it contains a specific filter for "ID = 0" to select all records
if (queryOptions.Filter != null && url.Contains("$filter=ID%20eq%200"))
{
// apply the new filter
url = url.Replace("$filter=ID%20eq%200", "$filter=ID%20ne%200");
// build a new request for the filter
HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, url);
// reset the query options with the new request
queryOptions = new ODataQueryOptions(queryOptions.Context, req);
}
// set a top filter if one was not supplied
if (queryOptions.Top == null)
{
// apply the query options with the new top filter
result = queryOptions.ApplyTo(queryable, new ODataQuerySettings { PageSize = 100 });
}
else
{
// apply any pending information that was not previously applied
result = queryOptions.ApplyTo(queryable);
}
// add the NextLink if one exists
if (queryOptions.Request.ODataProperties().NextLink != null)
{
originalRequest.ODataProperties().NextLink = queryOptions.Request.ODataProperties().NextLink;
}
// add the TotalCount if one exists
if (queryOptions.Request.ODataProperties().TotalCount != null)
{
originalRequest.ODataProperties().TotalCount = queryOptions.Request.ODataProperties().TotalCount;
}
// return all results
return result;
}
}
删除[EnableQuery]属性,您的场景应该可以工作,因为在使用该属性后,OData/WebApi将在您在控制器中返回数据后应用您的原始查询选项,如果您已经在控制器方法中应用了该属性,则不应使用该属性。
但是,如果您的查询选项包含$select,则这些代码不起作用,因为结果的类型不是Product,我们使用包装器来表示$select的结果,所以我建议您使用以下方法:
制作自定义的EnableQueryAttribute
public class MyEnableQueryAttribute : EnableQueryAttribute
{
public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
{
if (queryOptions.Filter != null)
{
queryOptions.ApplyTo(queryable);
var url = queryOptions.Request.RequestUri.AbsoluteUri;
url = url.Replace("$filter=Id%20eq%201", "$filter=Id%20eq%202");
var req = new HttpRequestMessage(HttpMethod.Get, url);
queryOptions = new ODataQueryOptions(queryOptions.Context, req);
}
return queryOptions.ApplyTo(queryable);
}
}
在控制器方法中使用此属性
[MyEnableQueryAttribute]
public IHttpActionResult Get()
{
return Ok(_products);
}
希望这能解决你的问题,谢谢!
风扇。
作为@Chris Schaller的回应,我发布了我自己的解决方案如下:
public class CustomEnableQueryAttribute : EnableQueryAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var url = actionContext.Request.RequestUri.OriginalString;
//change something in original url,
//for example change all A charaters to B charaters,
//consider decoding url using WebUtility.UrlDecode() if necessary
var newUrl = ModifyUrl(url);
actionContext.Request.RequestUri = new Uri(newUrl);
base.OnActionExecuting(actionContext);
}
}