OData含$select请求返回不同实体引发属性不存在问题求解
解决OData $select查询返回不同实体时的属性验证错误
首先,咱们来拆解一下问题根源:你的ODataQueryOptions是针对Product类型初始化的,但当你返回GoodProduct集合时,OData框架会自动尝试将原请求中的$select=Name查询规则应用到GoodProduct上——而GoodProduct没有Name属性,自然就触发了验证错误。你之前尝试用反射清空查询选项没用,是因为ODataQueryOptions在初始化阶段就已经完成了查询验证,后续修改属性无法回滚这个验证逻辑。
下面给你几个可行的解决方案,按需选择:
方案1:先对Product应用$select,再转换为GoodProduct
如果你希望$select规则先作用于Product(只获取包含Name的Product数据),再转换为GoodProduct返回,可以这样修改代码:
public IHttpActionResult Get(ODataQueryOptions<Product> queryOptions, CancellationToken cancellationToken) { if (Context == null) Context = MainContext.CreateInstance(); var products = Context.GetEntities<Product>(); if (queryOptions.RawValues?.Select != null) { // 先将$select查询应用到Product集合上 var filteredProducts = queryOptions.ApplyTo(products) as IQueryable<Product>; // 转换为GoodProduct列表(这里可以根据需求处理属性映射,忽略Name也没关系) var goodProducts = MakeGoodProductList(filteredProducts); // 返回结果,此时OData不会再对GoodProduct重复验证查询规则 return Ok(goodProducts); } return Ok(products); }
这个方案的好处是尊重原请求的$select规则,先过滤Product数据再转换,避免不必要的数据查询。
方案2:绕过OData验证,直接返回GoodProduct完整结果
如果你希望不管原请求的$select是什么,都返回完整的GoodProduct对象,可以直接通过HttpResponseMessage返回序列化后的结果,绕开OData的自动验证逻辑:
public IHttpActionResult Get(ODataQueryOptions<Product> queryOptions, CancellationToken cancellationToken) { if (Context == null) Context = MainContext.CreateInstance(); var products = Context.GetEntities<Product>(); if (queryOptions.RawValues?.Select != null) { var goodProducts = MakeGoodProductList(products); // 手动创建响应,直接返回JSON格式的GoodProduct数据 var response = Request.CreateResponse(HttpStatusCode.OK, goodProducts); response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); return ResponseMessage(response); } return Ok(products); }
这样做的话,OData框架不会再对返回的GoodProduct执行任何查询验证,直接返回你生成的完整数据。
方案3:创建针对GoodProduct的ODataQueryOptions并清空查询规则
如果你想保留OData的响应格式,但忽略原请求的$select规则,可以创建一个针对GoodProduct的新查询选项,并清空Select参数:
public IHttpActionResult Get(ODataQueryOptions<Product> queryOptions, CancellationToken cancellationToken) { if (Context == null) Context = MainContext.CreateInstance(); var products = Context.GetEntities<Product>(); if (queryOptions.RawValues?.Select != null) { var goodProducts = MakeGoodProductList(products).AsQueryable(); // 创建针对GoodProduct的查询上下文 var goodProductQueryContext = new ODataQueryContext( queryOptions.Context.Model, typeof(GoodProduct), queryOptions.Context.Path); // 初始化新的查询选项,并清空Select参数 var newQueryOptions = new ODataQueryOptions<GoodProduct>(goodProductQueryContext, Request); typeof(ODataRawQueryOptions).GetProperty(nameof(newQueryOptions.RawValues.Select)) .SetValue(newQueryOptions.RawValues, null); // 应用空查询规则,返回完整的GoodProduct数据 var result = newQueryOptions.ApplyTo(goodProducts); return Ok(result); } return Ok(products); }
这个方案既保留了OData的响应格式,又避免了属性验证错误,适合需要OData元数据支持的场景。
内容的提问来源于stack exchange,提问作者Dilshod K
相关产品推荐
相关产品推荐

