使用Expand时如何正确将ODataQueryOptions<T>转换为IQueryable<T>
问题根因
当请求携带$expand参数时,OData默认会对查询结果进行包装,生成SelectExpandWrapper<TModel>类型的代理对象用于投影和扩展属性处理,因此ApplyTo返回的IQueryable实际是IQueryable<SelectExpandWrapper<TModel>>类型,无法直接转换为IQueryable<TModel>,所以as运算返回null。
解决方案
方案1:使用带ODataQuerySettings的重载,禁用结果包装
修改ApplyTo的调用逻辑,传入配置禁用代理包装,直接返回原生实体对象,该方案适用于不需要单独支持$select参数的场景:
public async Task<ActionResult<TModel>> SingleAsync() { var validationSettings = new ODataValidationSettings() { AllowedFunctions = AllowedFunctions.AllFunctions, AllowedQueryOptions = AllowedQueryOptions.All, }; ODataQueryOptions<TModel> queryOptions = Request.GetODataQueryOptions<TModel>(); queryOptions.Validate(validationSettings); // 新增查询配置 var querySettings = new ODataQuerySettings { HandleNullPropagation = HandleNullPropagationOption.False, EnableConstantParameterization = true, EnsureStableOrdering = true, // 核心配置:不生成SelectExpandWrapper包装类 PageSize = null }; // 调用带settings的ApplyTo重载,执行后再转换实体类型 IQueryable query = queryOptions.ApplyTo(EntityDbSet, querySettings); TModel model = await query.FirstOrDefaultAsync() as TModel; return model; }
方案2:先执行查询再提取原生实体
如果需要同时兼容$select和$expand参数,可以保留OData默认的包装逻辑,执行查询后从包装类中提取原生实体:
public async Task<ActionResult<TModel>> SingleAsync() { var validationSettings = new ODataValidationSettings() { AllowedFunctions = AllowedFunctions.AllFunctions, AllowedQueryOptions = AllowedQueryOptions.All, }; ODataQueryOptions<TModel> queryOptions = Request.GetODataQueryOptions<TModel>(); queryOptions.Validate(validationSettings); IQueryable query = queryOptions.ApplyTo(EntityDbSet); var resultObj = await query.FirstOrDefaultAsync(); // 从包装类中提取原生实体 if (resultObj is ISelectExpandWrapper wrapper) { TModel model = wrapper.ToDictionary()["Instance"] as TModel; return model; } return resultObj as TModel; }
版本兼容说明
如果你使用的是ASP.NET Core OData 8.x版本,Request.GetODataQueryOptions扩展方法有调整,需要先注入IODataQueryOptionsParser解析查询选项,上述转换逻辑依然兼容。
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

