You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 8升级后泛型OData控制器注册失效问题求助

升级至.NET 8.0 + OData 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实体集的绑定规则都有变化,原有的注册方式无法适配新规则。

解决步骤

  1. 修正控制器名称约定
    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";
    
  2. 启用OData属性路由
    取消基类控制器Get方法上[ODataAttributeRouting]的注释,确保OData路由系统能识别该端点:

    [EnableQuery]
    [ODataAttributeRouting]
    [HttpGet]
    public IQueryable<TViewEntity> Get()
    {
        return dbContext.Set<TViewEntity>().AsQueryable().AsNoTracking();
    }
    
  3. 验证泛型控制器特性提供者逻辑
    确保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());
            }
        }
    }
    
  4. 对齐EDM实体集名称与路由
    确认EDM中创建的实体集名称(entityType.Name.Replace("V_", ""))与控制器路由中的名称完全一致,避免因名称不匹配导致无法绑定。


验证方式

启动项目后访问/odata/$metadata,检查是否包含泛型控制器对应的实体集定义;同时直接访问泛型控制器的路由(如/odata/NavigationViewConfig),验证是否能正常返回数据。

内容的提问来源于stack exchange,提问作者fattikus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 15:07:06