如何修改 OData 查询以添加访问过滤器?

6

我有一个C# OData终端,需要评估在OData查询中提交的过滤器,以确定是否需要添加其他过滤器来限制返回给用户的结果。

我的示例模型很简单:

学生 -> 图书目录 -> 书籍(所有实体都有分配给它们的CampusId属性)

当一个属于校园5的用户执行以下查询时:“Students$select=Id,Name,CampusId”,应该转换为:“Students$select=Id,Name,CampusId&$filter=CampusId eq 5”

我可以通过简单地将过滤器作为字符串添加来使用简单的查询来完成此操作。

我真正想做的是:

1)确定所选和展开的实体
2)确定这些实体中是否有CampusId属性
3)将必要的过滤器值添加到Uri中,以便为每个选定和/或展开的实体过滤到该校区

我试图使用Microsoft.OData.Core.UriParser.ODataUriParser解析过滤器值,然后创建新的Uri。

例如:

var parser = new Microsoft.OData.Core.UriParser.ODataUriParser(edmModel, new Uri(serviceRootPath), originalUri);
var filter = parser.ParseFilter();

使用上述代码片段,您可以获得“filter”变量,以提供一种名为Microsoft.OData.Core.UriParser.Semantic.FilterClause的类型,该类型可用于检查OData查询Uri中的当前过滤器值。
有人知道如何编辑FilterClause中的值,以便能够向Uri添加新的过滤器值吗?
我没有找到许多关于如何编辑FilterClause的示例,以生成具有新过滤器值的更新的Uri。

我这里有一个回答,可以在控制器中更改过滤器的值: http://stackoverflow.com/questions/37339114/add-filter-on-property-for-odata-query/37339655#37339655 - Fan Ouyang
谢谢,但那并没有帮助。事实上,在你提到的回复中的答案只有在你已经知道你的OData Uri字符串的结构时才有效。实际上,过滤器值可以包括各种其他设置(startsWith、Contains等),因此简单的字符串替换不可靠。正确的解决方案应该允许我们检查现有的过滤器表达式树,并在必要时附加到它上面,而不会干扰它已经包含的任何现有过滤器值。 - Klaus Barkhausen
就此事而言,我认为我几乎在这里问了一个相同的问题:https://dev59.com/uJDea4cB1Zd3GeqPWxVg。目前还没有得到很好的答案,但它可能会让你更好地理解这个问题的背景。 - Zachary Dow
1个回答

5

我通过编写一个算法,在我的模型属性基础上为请求ODataUri添加了额外的过滤器,从而解决了我的问题。它检查根级实体的任何属性以及任何扩展实体的属性,以确定需要添加到OData查询中的额外过滤器表达式。

OData v4支持在$expand子句中进行过滤,但是扩展实体中的filterOption只读,因此您无法修改扩展实体的过滤器表达式。您只能检查扩展实体中的filterOption内容。

我的解决方案是检查所有实体(根和扩展)的属性,然后在请求ODataUri的根过滤器中添加任何需要的附加$filter选项。

以下是一个示例OData请求URL:

/RootEntity?$expand=OtherEntity($expand=SomeOtherEntity)

这是我更新后的相同OData请求URL:
/RootEntity?$filter=OtherEntity/SomeOtherEntity/Id eq 3&$expand=OtherEntity($expand=SomeOtherEntity)

我用以下步骤完成了这个任务:

  1. 使用ODataUriParser将传入的Url解析为Uri对象

具体请参见下文:

var parser = new ODataUriParser(model, new Uri(serviceRootPath), requestUri);   
var odataUri = parser.ParseUri();
  1. 创建一个方法,从根节点下降到所有展开的实体,并通过引用传递ODataUri(以便您在检查每个实体时可以根据需要更新它)

第一个方法将检查根实体,并根据根实体的属性添加任何其他过滤器。

AddCustomFilters(ref ODataUri odataUri);

AddCustomFilters方法将遍历扩展实体并调用AddCustomFiltersToExpandedEntity,该方法将继续向下遍历所有扩展实体以添加任何必要的过滤器。

foreach (var item in odatauri.SelectAndExpand.SelectedItems)
{
    AddCustomFiltersToExpandedEntity(ref ODataUri odataUri, ExpandedNavigationSelectItem expandedNavigationSelectItem, string parentNavigationNameProperty)
}

方法AddCustomFiltersToExpandedEntity在遍历每个级别的扩展实体时应该调用自身。

  1. 在检查每个实体时更新根过滤器

创建一个新的过滤器子句,其中包含您的附加过滤器要求,并覆盖根级别上现有的过滤器子句。ODataUri的根级别$ filter具有setter,因此可以被覆盖。

odataUri.Filter = new FilterClause(newFilterExpression, newFilterRange);

注意:我使用BinaryOperatorKind.And创建了一个新的过滤器子句,这样任何其他的过滤表达式都可以简单地附加到ODataUri中已有的过滤表达式。

var combinedFilterExpression = new BinaryOperatorNode(BinaryOperatorKind.And, odataUri.Filter.Expression, newFilterExpression);
odataUri.Filter = new FilterClause(combinedFilterExpression, newFilterRange);
  1. 使用ODataUriBuilder基于更新的Uri创建新的Url

详见以下内容:

var updatedODataUri = new Microsoft.OData.Core.UriBuilder.ODataUriBuilder(ODataUrlConventions.Default, odataUri).BuildUri();
  1. 将请求 Uri 替换为更新后的 Uri。

这将使 OData 控制器使用包含您刚刚添加到根级筛选器的其他筛选选项的更新后的 OData Url 完成处理请求。

ActionContext.Request.RequestUri = updatedODataUri;

这使我能够添加任何所需的筛选选项,并确信我没有错误地更改了OData Url结构。希望这篇文章能帮助其他遇到同样问题的人。

似乎在AspNetCore 2中,序列化添加的额外过滤条件不会添加父实体:我创建了一个问题https://github.com/OData/WebApi/issues/1240。你现在正在运行AspNetCore吗? - Ovidiu Buligan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接