.NET 8升级后泛型OData控制器注册失效问题求助
我将项目升级到.NET 8.0,并把OData包更新至8.*版本后,原有的泛型OData控制器注册方式失效了。此前通过反射为所有实现IODataView接口的OData实体注册泛型控制器的逻辑在旧版本正常运行,但新版中,反射注册的控制器在OData调试端点里不显示,手动创建的控制器却能正常工作。
原注册及路由代码
泛型控制器注册逻辑
foreach (var type in oDataEntities) { feature.Controllers.Add(typeof(GenericODataController<>).MakeGenericType(type).GetTypeInfo()); }
控制器路由设置
// OData endpoint registration var route = $"odata/{genericType.Name.Replace("V_", "").Singularize()}"; controller.Selectors.Add(new SelectorModel { AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(route)), }); controller.ControllerName = genericType.Name;
控制器代码
泛型控制器
// [AllowAnonymous] public class GenericODataController<TViewEntity> : BaseODataController<TViewEntity> where TViewEntity : class { public GenericODataController(IBaseSphereDbContext dbContext) : base(dbContext) { } }
基类控制器
public abstract class BaseODataController<TViewEntity>(IBaseSphereDbContext dbContext) : ODataController where TViewEntity : class { [EnableQuery] //[ODataAttributeRouting] [HttpGet] public IQueryable<TViewEntity> Get() { return dbContext.Set<TViewEntity>().AsQueryable().AsNoTracking(); } }
可正常工作的手动控制器
[Route("odata/NavigationViewConfig")] public class NavigationViewConfigODataController(IBaseSphereDbContext dbContext) : ODataController { [EnableQuery] [HttpGet] public IQueryable<V_NavigationViewConfig> Get() { return dbContext.Set<V_NavigationViewConfig>().AsQueryable().AsNoTracking(); } }
EDM配置代码
服务注册部分
var edmModel = GetEdmModel(); services.AddControllers(options => { options.Conventions.Add(new GenericControllerRouteConvention()); }) .AddOData(opt => opt.Count().Filter().Expand().Select().OrderBy().SetMaxTop(60) .AddRouteComponents("odata", edmModel) ) .AddApplicationPart(assembly) .ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new GenericTypeControllerFeatureProvider())) .AddNewtonsoftJson(opt => opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
GetEdmModel实现
public static ODataConventionModelBuilder GetODataEdmBuilder() { var odataBuilder = new ODataConventionModelBuilder(); odataBuilder.EnableLowerCamelCase(); var odataViews = ReflectionHelper.GetAllODataViewEntities(); var entitySetMethod = typeof(ODataModelBuilder) .GetMethods() .First(m => m.Name == "EntitySet" && m.IsGenericMethod); foreach (var entityType in odataViews) { var name = entityType.Name.Replace("V_", ""); var genericMethod = entitySetMethod.MakeGenericMethod(entityType); genericMethod.Invoke(odataBuilder, new object[] { name }); } return odataBuilder; }
问题原因及解决步骤
核心原因
OData Web API 8.x调整了控制器发现与路由匹配的逻辑,泛型控制器的名称约定、与EDM实体集的绑定规则都有变化,原有的注册方式无法适配新规则。
解决步骤
修正控制器名称约定
OData 8.x默认通过[实体集名称]ODataController的规则匹配控制器与EDM实体集,需将泛型控制器的名称调整为与手动控制器一致的格式:var entitySetName = genericType.Name.Replace("V_", ""); var route = $"odata/{entitySetName.Singularize()}"; controller.Selectors.Add(new SelectorModel { AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(route)), }); // 改为符合OData约定的控制器名称 controller.ControllerName = $"{entitySetName}ODataController";启用OData属性路由
取消基类控制器Get方法上[ODataAttributeRouting]的注释,确保OData路由系统能识别该端点:[EnableQuery] [ODataAttributeRouting] [HttpGet] public IQueryable<TViewEntity> Get() { return dbContext.Set<TViewEntity>().AsQueryable().AsNoTracking(); }验证泛型控制器特性提供者逻辑
确保GenericTypeControllerFeatureProvider正确将泛型控制器类型添加到控制器特性中,且类型被识别为继承自ODataController:public class GenericTypeControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature> { public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature) { var oDataEntities = ReflectionHelper.GetAllODataViewEntities(); foreach (var type in oDataEntities) { var genericControllerType = typeof(GenericODataController<>).MakeGenericType(type); feature.Controllers.Add(genericControllerType.GetTypeInfo()); } } }对齐EDM实体集名称与路由
确认EDM中创建的实体集名称(entityType.Name.Replace("V_", ""))与控制器路由中的名称完全一致,避免因名称不匹配导致无法绑定。
验证方式
启动项目后访问/odata/$metadata,检查是否包含泛型控制器对应的实体集定义;同时直接访问泛型控制器的路由(如/odata/NavigationViewConfig),验证是否能正常返回数据。
内容的提问来源于stack exchange,提问作者fattikus

