如何用ASP.NET OData查询无固定模型的JsonObject并解决$filter问题?
用ASP.NET OData过滤无固定结构的JsonObject集合
问题背景
需要基于无EDM、无固定实体模型的JsonObject集合实现OData查询,已完成$skip/$top等基础功能,但$filter因OData无法识别JsonNode的动态属性而抛出异常。
问题1:能否使用ASP.NET OData查询带索引器的对象?
可以实现,但默认OData绑定逻辑会试图将过滤器中的属性(如title)解析为JsonNode的强类型属性,而非动态索引访问。因此需要通过自定义过滤器绑定器修改表达式解析逻辑,将属性访问转换为对JsonObject的索引器调用。
问题2:自定义过滤器绑定器的实现示例
以下是基于ASP.NET Core OData 8.x的完整实现步骤:
1. 自定义FilterBinder
继承FilterBinder类,重写BindPropertyAccess方法,将属性访问表达式转换为索引器访问:
using Microsoft.OData.Edm; using Microsoft.OData.UriParser; using System.Linq.Expressions; public class JsonNodeFilterBinder : FilterBinder { public JsonNodeFilterBinder(IServiceProvider serviceProvider) : base(serviceProvider) { } protected override Expression BindPropertyAccess(PropertyAccessToken propertyToken, Expression source) { // 将属性访问(如 source.title)转换为索引器访问(如 source["title"]) var propertyName = propertyToken.Identifier; var indexerExpr = Expression.Property(source, "Item", Expression.Constant(propertyName)); // 处理JsonNode到目标类型的转换 return ConvertJsonNodeValue(indexerExpr, propertyToken); } private Expression ConvertJsonNodeValue(Expression nodeExpr, PropertyAccessToken propertyToken) { var edmType = propertyToken.GetEdmType() as IEdmPrimitiveType; if (edmType == null) return nodeExpr; Type targetType = edmType.GetClrType(); var getValueMethod = typeof(JsonNode).GetMethod("GetValue", new[] { typeof(Type) }); if (getValueMethod != null) { return Expression.Call(nodeExpr, getValueMethod, Expression.Constant(targetType)); } return nodeExpr; } }
2. 注册自定义FilterBinder
在Program.cs中替换默认的过滤器绑定器:
builder.Services.AddControllers().AddOData(options => { options.Select().Filter().OrderBy().SkipToken().Top(); // 注册自定义过滤器绑定器 options.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(IFilterBinder), sp => new JsonNodeFilterBinder(sp)); });
3. 调整控制器逻辑
确保控制器允许Filter查询选项:
[HttpGet] public PageResult<JsonNode> GetItems(ODataQueryOptions<JsonNode> options) { var dataQueryable = _store.GetData().AsQueryable(); var filteredData = options.ApplyTo( dataQueryable, AllowedQueryOptions.Filter | AllowedQueryOptions.Skip | AllowedQueryOptions.Top ).Cast<JsonNode>(); return new PageResult<JsonNode>(filteredData, null, dataQueryable.Count()); }
补充说明
- 上述实现会自动将OData过滤器中的
title eq 'test'转换为jsonNode["title"].GetValue(typeof(string)) == "test",适配动态属性查询。 - 若需处理嵌套属性(如
author.name eq 'foo'),可在BindPropertyAccess中递归处理属性链,转换为jsonNode["author"]["name"]的形式。 - 如果数据存储是支持JSON查询的数据库(如SQL Server),可进一步扩展绑定器,将表达式转换为EF Core支持的数据库端JSON查询(如
EF.Functions.JsonValue),提升查询性能。
内容的提问来源于stack exchange,提问作者zu1b
相关产品推荐
相关产品推荐

