理想的解决方案是使用内置tolower
和toupper
过滤功能在客户端处理此问题。这将允许客户选择是否区分大小写进行过滤。
在服务器端,不幸的是,当前的最佳实践是修改请求 URI 并为修改后的 URI 生成一个新的请求对象。请参阅OData V4 在服务器端修改 $filter。Web API OData 中有一个开放的问题,用于更优雅的查询选项拦截/修改机制。
直接在请求 URI 上进行字符串手术总是容易出错。我们可以通过利用OData 库(ODL)中过滤器表达式的丰富对象模型来改进链接的答案。请注意,过滤器表达式 on是表示 的值的抽象语法树。ODataQueryOptions.Filter.FilterClause.Expression
$filter
下面的代码需要来自 ODL 的命名空间:
using Microsoft.OData.Core;
using Microsoft.OData.Core.UriBuilder;
using Microsoft.OData.Core.UriParser;
using Microsoft.OData.Core.UriParser.Semantic;
using Microsoft.OData.Core.UriParser.TreeNodeKinds;
using Microsoft.OData.Edm;
首先,定义一个帮助类来重写构成过滤器 AST 的各种节点。以下类目前仅处理BinaryOperatorNode
(例如,eq
表达式)。
public static class FilterExpressionHelper
{
public static SingleValueNode RewriteAsCaseInsensitive(BinaryOperatorNode node)
{
// Handle {Edm.String eq Edm.String}
if (node.OperatorKind == BinaryOperatorKind.Equal && node.Left.TypeReference.IsString() && node.Right.TypeReference.IsString())
{
// Wrap both operands with toupper().
node = new BinaryOperatorNode(BinaryOperatorKind.Equal,
new SingleValueFunctionCallNode("toupper", new List<QueryNode> { node.Left }, node.Left.TypeReference),
new SingleValueFunctionCallNode("toupper", new List<QueryNode> { node.Right }, node.Right.TypeReference));
}
return node;
}
// Add methods to handle other node types; e.g., SingleValueFunctionCallNode.
}
接下来,定义一个助手来调用重写器并重新生成请求 URI 的查询字符串(由 建模ODataQueryOptions
)。
public class ODataUriHelper
{
public static string RewriteQuery(ODataQueryOptions queryOptions)
{
var odataUri = BuildODataUri(queryOptions);
var uriBuilder = new ODataUriBuilder(ODataUrlConventions.Default, odataUri);
var uri = uriBuilder.BuildUri();
// Do not return the leading '?'.
return uri.Query.Substring(1);
}
private static readonly Uri DummyServiceRoot = new Uri("http://localhost");
private static readonly ODataPath DummyPath = new ODataPath(Enumerable.Empty<ODataPathSegment>());
private static ODataUri BuildODataUri(ODataQueryOptions queryOptions)
{
var uri = new ODataUri();
uri.ServiceRoot = DummyServiceRoot;
uri.Path = DummyPath;
uri.Filter = RewriteFilter(queryOptions.Filter?.FilterClause);
uri.OrderBy = queryOptions.OrderBy?.OrderByClause;
uri.SelectAndExpand = queryOptions.SelectExpand?.SelectExpandClause;
uri.Skip = queryOptions.Skip?.Value;
uri.Top = queryOptions.Top?.Value;
return uri;
}
private static FilterClause RewriteFilter(FilterClause filterClause)
{
if (filterClause != null)
{
var filterExpr = filterClause.Expression;
var binaryExpr = filterExpr as BinaryOperatorNode;
if (binaryExpr != null)
{
filterExpr = FilterExpressionHelper.RewriteAsCaseInsensitive(binaryExpr);
filterClause = new FilterClause(filterExpr, filterClause.RangeVariable);
}
// Add support for other node types here.
}
return filterClause;
}
}
EnableQuery
最后,将它们与有条件地执行重写的属性的自定义版本联系在一起。
public class CustomEnableQueryAttribute : EnableQueryAttribute
{
public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
{
if (queryOptions.Filter != null)
{
var query = ODataUriHelper.RewriteQuery(queryOptions);
var uri = new UriBuilder(queryOptions.Request.RequestUri) { Query = query };
var request = new HttpRequestMessage(HttpMethod.Get, uri.Uri);
queryOptions = new ODataQueryOptions(queryOptions.Context, request);
}
return base.ApplyQuery(queryable, queryOptions);
}
}