.NET Core动态EDM模型创建的OData路由配置问题求助
我来帮你解决从ASP.NET Web API迁移动态OData到.NET Core时遇到的这些问题。你遇到的$metadata异常、$filter失效,还有之前的"source is null"错误,核心原因是.NET Core的OData路由机制和传统ASP.NET Web API有差异,手动构建ControllerActionDescriptor的方式不够严谨,同时忽略了OData特殊路由(比如$metadata)的处理逻辑。下面是分步的解决方案:
ASP.NET Core的OData路由系统依赖IODataRoutingConvention的实现来匹配请求到控制器动作,但它对动作描述符的完整性要求更高——比如元数据请求需要特定的处理逻辑,EnableQueryAttribute生效需要正确绑定方法的特性信息,而你之前的代码把所有请求都硬转到Get方法,导致元数据请求无法被正确处理,同时$filter等查询选项因为缺少必要的上下文而失效。
$metadata是OData的特殊路由,需要交给默认的路由规则处理,不能全部转发到你的Get方法。在自定义路由规则里先判断请求是否为元数据请求,是的话返回null,让其他默认的路由约定来处理:
using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.OData.Routing; using Microsoft.AspNetCore.OData.Routing.Conventions; using Microsoft.OData.Core.UriParser; using Microsoft.AspNetCore.OData.Attributes; public class MatchAllRoutingConvention : IODataRoutingConvention { public IEnumerable<ControllerActionDescriptor> SelectAction(RouteContext routeContext) { // 先识别元数据请求,交给默认路由处理 var odataFeature = routeContext.HttpContext.ODataFeature(); if (odataFeature.Path?.Segments.Any(segment => segment is MetadataSegment) == true) { return null; // 返回null,让后续的默认路由约定处理 } // 其他请求绑定到ODataEndpointController的Get方法 var getMethod = typeof(ODataEndpointController).GetMethod(nameof(ODataEndpointController.Get)); if (getMethod == null) { return Enumerable.Empty<ControllerActionDescriptor>(); } var actionDescriptor = new ControllerActionDescriptor { ControllerName = nameof(ODataEndpointController).Replace("Controller", string.Empty), ActionName = nameof(ODataEndpointController.Get), ControllerTypeInfo = typeof(ODataEndpointController).GetTypeInfo(), MethodInfo = getMethod }; // 确保EnableQuery特性被正确关联 var enableQueryAttr = getMethod.GetCustomAttribute<EnableQueryAttribute>(); if (enableQueryAttr != null) { actionDescriptor.FilterDescriptors.Add(new FilterDescriptor(enableQueryAttr, FilterScope.Global)); } return new List<ControllerActionDescriptor> { actionDescriptor }; } }
$filter生效需要两个前提:
- 在
ConfigureServices中启用OData的查询支持 - 确保
EnableQueryAttribute能正确识别并处理请求
更新你的Startup.cs配置:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => { options.EnableEndpointRouting = false; // 因为你使用的是UseMvc而非端点路由 }).AddOData(options => { // 启用所有需要的查询选项 options.Select().Filter().OrderBy().Expand().Count().SetMaxTop(null); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 其他中间件配置(比如异常处理、静态文件等)... IEdmModel model = this.GetEdmModel(app.ApplicationServices); // 使用默认路由约定集合,再插入自定义规则到最前面 var routingConventions = ODataRoutingConventions.CreateDefault(); routingConventions.Insert(0, new MatchAllRoutingConvention()); app.UseMvc(routeBuilder => { routeBuilder.MapODataServiceRoute("odata", "odata", model, new CustomPathHandler(), routingConventions); }); }
这个错误大概率是DataSourceProvider.Get方法没有正确填充EdmEntityObjectCollection,或者你的EdmModel中没有正确定义Products实体集。可以在Get方法里加个断点,检查:
entityType是否正确获取到了Products的实体类型collection在调用DataSourceProvider.Get后是否有数据
- 启动项目后访问
http://localhost:port/odata/$metadata,确认能正常返回元数据文档 - 访问
http://localhost:port/odata/Products,确认能返回数据 - 测试带$filter的请求,比如
http://localhost:port/odata/Products?$filter=Id gt 5,确认过滤逻辑生效
内容的提问来源于stack exchange,提问作者Arun

