Adding a new OData controller does not allow an existing controller

I am creating a sample of two OData Web API samples, each of which works just fine as a separate project. But when I add the second ODataController class, then the site no longer works, complaining about the OData path templates that worked previously. Here is more detailed information:

The following action works fine while its controller (ProductsController) is the only controller:

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

Now I am adding a new controller (MovieController) with several actions.

I am extending the Owin startup class to look like this:

 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); } 

However, when I try to execute a web request (URLBASE / functions / $ metadata), I get the following error:

System.InvalidOperationExceptionWhen the path template 'GetSalesTaxRate (state = {state})' in the action 'GetSalesTaxRate' in the Products controller is not a valid OData path template. Resource not found for GetSalesTaxRate.

Controllers are mapped to different routes ("functions" and "actions"). Maybe the problem is that each route maps to its own EdmModel?

UPDATE I checked that I can add more controllers if they belong to the same EDM model. But as soon as I present the second model (and refer to it with MapODataServiceRoute), then the whole service breaks. Is there a way around several models?

UPDATE 2. If I subclass DefaultHttpControllerTypeResolver and turn on only one controller (any of them), it also works fine. But I'm still puzzled by why several controllers using different models fail.

+3
source share
1 answer

By default, when mapping OData map attribute routes, the default logic of the IHttpControllerSelector HTTP controller uses the HttpConfiguration DefaultAssembloesResolver, which will look for all types of controllers in the application domain. The volume can be reduced to the fact that the controllers belong to the model.

We can customize the extension methods of MapODataServiceRoute. Some snippets of code:

 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); } } 
+5
source

Source: https://habr.com/ru/post/973713/


All Articles