添加新的 OData 控制器会使现有控制器失败

本文关键字:控制器 失败 OData 添加 | 更新日期: 2023-09-27 18:32:12

我正在从两个Web API OData示例构建一个示例,每个示例都可以作为单独的项目正常工作。但是当我添加第二个 ODataController 类时,该站点不再抱怨以前工作的 OData 路径模板。以下是更多详细信息:

只要其控制器(产品控制器)是唯一的控制器,以下操作就可以正常工作:

[HttpGet]
[ODataRoute("GetSalesTaxRate(state={state})")]
public IHttpActionResult GetSalesTaxRate([FromODataUri] string state)
{
    return Ok(GetRate(state));
}

现在,我添加一个新的控制器(电影控制器),其中包含一些操作。

我扩展了 Owin Startup 类,所以它看起来像这样:

public void Configuration(IAppBuilder builder)
{
    var config = new HttpConfiguration();
    config.MapODataServiceRoute(routeName: "functions route", routePrefix: "functions", model: FunctionStartup.GetEdmModel());
    config.MapODataServiceRoute(routeName: "actions route", routePrefix: "actions", model: ActionStartup.GetEdmModel());
    builder.UseWebApi(config);
}

但是,当我尝试执行 Web 请求 (URLBASE/functions/$metadata) 时,出现以下错误:

系统。无效操作异常路径模板 "GetSalesTaxRate(state={state})"中的操作"GetSalesTaxRate" 控制器"产品"不是有效的 OData 路径模板。资源不是 为"GetSalesTaxRate"细分市场找到。

控制器映射到不同的路由("函数"和"操作")。问题可能是每个路由都映射到自己的 EdmModel?

更新。我检查了是否可以添加更多控制器,只要它们引用相同的 EDM 模型。但是一旦我引入了第二个模型(并从MapODataServiceRoute引用它),那么整个服务就会中断。是否有任何解决方法来支持多个模型?

更新 2.如果我子类 DefaultHttpControllerTypeResolver 并且只启用单个控制器(其中任何一个),那么也可以正常工作。但我仍然感到困惑,为什么使用不同模型的多个控制器会失败。

添加新的 OData 控制器会使现有控制器失败

默认情况下,当映射 OData 属性路由约定时,HTTP 控制器选择器 IHttpControllerSelector 的默认逻辑使用 HttpConfiguration 的 DefaultAssembloesResolver,它将查找应用程序域中的所有控制器类型。 范围可以缩小到属于模型的控制器。

我们可以自定义 MapODataServiceRoute 扩展方法。一些代码片段:

public class Startup
{
    public void Configuration(IAppBuilder builder)
    {
        var config = new HttpConfiguration();
        config.CustomMapODataServiceRoute(routeName: "functions route", routePrefix: "functions",
            model: FunctionStartup.GetEdmModel(),
            controllers: new[] { typeof(ProductsController) });
        config.CustomMapODataServiceRoute(routeName: "actions route", routePrefix: "actions",
            model: ActionStartup.GetEdmModel(),
            controllers: new[] { typeof(MoviesController) });
        config.EnsureInitialized();
        builder.UseWebApi(config);
    }
}
public class CustomAttributeRoutingConvention : AttributeRoutingConvention
{
    private readonly List<Type> _controllers = new List<Type> { typeof(MetadataController) };
    public CustomAttributeRoutingConvention(IEdmModel model, HttpConfiguration configuration, IEnumerable<Type> controllers)
        : base(model, configuration)
    {
        _controllers.AddRange(controllers);
    }
    public override bool ShouldMapController(HttpControllerDescriptor controller)
    {
        return _controllers.Contains(controller.ControllerType);
    }
}
public static class HttpConfigExt
{
    public static ODataRoute CustomMapODataServiceRoute(this HttpConfiguration configuration, string routeName,
        string routePrefix, IEdmModel model, IEnumerable<Type> controllers)
    {
        var routingConventions = ODataRoutingConventions.CreateDefault();
        routingConventions.Insert(0, new CustomAttributeRoutingConvention(model, configuration, controllers));
        return configuration.MapODataServiceRoute(routeName, routePrefix, model, new DefaultODataPathHandler(),
            routingConventions);
    }
}