Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I am building a sample from two Web API OData samples, each of them works fine as a separate project. But when I add second ODataController class, then the site no longer works complaining about OData path templates that worked previously. Here are more details:

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

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

Now I add a new controller (MoviesController) with a few actions.

I extend Owin Startup class so it looks 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.InvalidOperationExceptionThe path template 'GetSalesTaxRate(state={state})' on the action 'GetSalesTaxRate' in controller 'Products' is not a valid OData path template. Resource not found for the segment 'GetSalesTaxRate'.

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

UPDATE. I checked that I can add more controllers as long as they refer to the same EDM model. But once I introduce a second model (and reference it from MapODataServiceRoute), then the whole service breaks. Is there any workaround to support multiple models?

UPDATE 2. If I subclass DefaultHttpControllerTypeResolver and only enable single controller (any of them), then is also works fine. But I am still puzzled why multiple controllers using different models fail.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
234 views
Welcome To Ask or Share your Answers For Others

1 Answer

By default, when map OData attribute route conventions, the default logic of HTTP controller selector IHttpControllerSelector uses HttpConfiguration's DefaultAssembloesResolver, which will look up all controller types in an app domain. The scope could be reduced to controllers belong to a model.

We can customize the MapODataServiceRoute extension methods. Some code snippet:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...