You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 00:22:36