.NET 8中ODataQueryOptions<IQueryable>应用时机及结果转换问题
.NET 8 + OData 9 前置获取处理后结果的问题解决
问题根源拆解
你遇到的几个问题本质都是OData的动态包装类型机制导致的:当使用$select、$expand这类OData查询参数时,queryOptions.ApplyTo不会直接返回原始实体,而是生成SelectSome<T>这类动态代理类型——它只包含你指定选择/展开的字段,并非完整的实体类。
逐个问题解决
1. SelectSome 转 Product 实体或 JSON
不要直接强转,OData提供了专门的序列化工具处理这类包装类型:
- 注入
IODataSerializerProvider,用对应序列化器处理结果:
private readonly IODataSerializerProvider _serializerProvider; public YourController(IODataSerializerProvider serializerProvider) { _serializerProvider = serializerProvider; } [HttpGet] public IActionResult Get(ODataQueryOptions<Product> queryOptions) { var query = _dbContext.Products.AsQueryable(); var odataResult = queryOptions.ApplyTo(query) as IQueryable; // 获取OData序列化器 var serializer = _serializerProvider.GetEdmTypeSerializer(EdmLibHelpers.GetEdmType(odataResult.ElementType)); // 序列化到JSON var stringWriter = new StringWriter(); serializer.WriteObject(odataResult, typeof(IQueryable), new ODataMessageWriter(stringWriter), new ODataSerializerContext()); var jsonResult = stringWriter.ToString(); // 这里可直接返回JSON,或手动映射到实体 return Ok(jsonResult); }
- 手动转实体的话,先转成
ISelectExpandWrapper,再通过ToDictionary拿字段键值对映射:
var wrappedResult = odataResult.Cast<ISelectExpandWrapper>().FirstOrDefault(); if (wrappedResult != null) { var dict = wrappedResult.ToDictionary(); var product = new Product { Id = (int)dict["Id"], Name = (string)dict["Name"], // 递归处理展开的Orders Orders = ((IEnumerable<ISelectExpandWrapper>)dict["Orders"])? .Select(o => { var orderDict = o.ToDictionary(); return new Order { Id = (int)orderDict["Id"], OrderDate = (DateTime)orderDict["OrderDate"] }; }).ToList() }; }
2. IActionFilter 中序列化未应用OData参数
这是因为Filter执行时机不对——OData的EnableQueryAttribute是在OnActionExecuted阶段才完成查询参数应用。要在Filter里处理,需手动调用queryOptions.ApplyTo:
public class YourFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { // 此处拿到的是未经过OData处理的原始IQueryable } public void OnActionExecuted(ActionExecutedContext context) { if (context.Result is ObjectResult objectResult && objectResult.Value is IQueryable queryable) { // 从上下文获取ODataQueryOptions var queryOptions = context.HttpContext.RequestServices.GetRequiredService<ODataQueryOptions<Product>>(); // 手动应用OData参数 var processedQuery = queryOptions.ApplyTo(queryable); objectResult.Value = processedQuery; } } }
注意:要确保Filter在EnableQueryAttribute之后执行,或直接移除EnableQueryAttribute,自己在Filter里处理OData参数。
3. $expand=Orders 后 Orders 是 List<SelectSome>
原因和第一个问题一致——OData对展开的关联实体也生成了SelectSome<Order>包装类型。解决办法:
- 用OData序列化器统一处理整个结果,序列化器会自动递归解析所有包装类型;
- 手动转实体时,参照上面的代码,把Orders的每个
ISelectExpandWrapper实例逐一转成Order实体。
内容的提问来源于stack exchange,提问作者Uladzimir
相关产品推荐
相关产品推荐

