Search code examples
c#asp.net.netasp.net-web-apiodata

Bound function returns 404


I'm trying to add a bound function to my Web API 2.2 OData v4 controller, but it returns 404 with the error message:

{
  "error":{
    "code":"","message":"No HTTP resource was found that matches the request URI 'http://localhost:2390/Hierarchies('300924834')/FullHierarchy'.","innererror":{
      "message":"No routing convention was found to select an action for the OData path with template '~/entityset/key/unresolved'.","type":"","stacktrace":""
    }
  }
}

This is my method on the HierarchiesController:

[HttpGet]
public IHttpActionResult FullHierarchy([FromODataUri]string key)
{
     return Ok(new Hierarchy());
}

I tried to add the attribute [ODataRoute("Hierarchies({key}/FullHierarchy)")]on my method but then I got the following error on the line GlobalConfiguration.Configure(WebApiConfig.Register);when first running the application:

The path template 'Hierarchies({key}/FullHierarchy)' on the action 'FullHierarchy' in controller 'Hierarchies' is not a valid OData path template. Bad Request - Error in query syntax.

This is my configuration in WebApiConfig:

ODataConventionModelBuilder builder = new ODataConventionModelBuilder();

builder.Namespace = ""; // tried with and without this line.

var hierachyType = builder.EntitySet<Hierarchy>("Hierarchies").EntityType;
hierachyType.Function("FullHierarchy").Returns<Hierarchy>();

Note that bound functions to collections don't work as well, but unbound functions do work.

This is how my $metadata looks like:

<?xml version="1.0" encoding="UTF-8"?>
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
    <edmx:DataServices>
        <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="Entities.Models">
            <EntityType Name="Hierarchy">
                <Key>
                    <PropertyRef Name="Id" />
                </Key>
                <Property Name="Name" Type="Edm.String" />
                <Property Name="Id" Type="Edm.String" Nullable="false" />
                <NavigationProperty Name="Policy" Type="Entities.Models.Policy" />
                <NavigationProperty Name="Nodes" Type="Collection(Entities.Models.HierarchyNode)" />
            </EntityType>
            <EntityType Name="Node" OpenType="true">
                <Key>
                    <PropertyRef Name="Id" />
                </Key>
                <Property Name="Name" Type="Edm.String" />
                <Property Name="Id" Type="Edm.String" Nullable="false" />
            </EntityType>
            <EntityType Name="Site" BaseType="Entities.Models.Node" OpenType="true">
                <Property Name="Address" Type="Edm.String" />
            </EntityType>
            <EntityType Name="Policy">
                <Key>
                    <PropertyRef Name="Id" />
                </Key>
                <Property Name="Id" Type="Edm.String" Nullable="false" />
                <Property Name="Name" Type="Edm.String" />
                <NavigationProperty Name="Rules" Type="Collection(Entities.Rules.Rule)" />
            </EntityType>
            <EntityType Name="HierarchyNode">
                <Key>
                    <PropertyRef Name="Id" />
                </Key>
                <Property Name="Id" Type="Edm.String" Nullable="false" />
                <Property Name="Name" Type="Edm.String" />
                <NavigationProperty Name="Nodes" Type="Collection(Entities.Models.HierarchyNode)" />
            </EntityType>
            <EntityType Name="Building" BaseType="Entities.Models.Node" OpenType="true">
                <Property Name="StaffCount" Type="Edm.Int32" Nullable="false" />
            </EntityType>
            <EntityType Name="Source" BaseType="Entities.Models.Node" OpenType="true">
                <Property Name="GroupByOperation" Type="Edm.String" />
                <Property Name="Factor" Type="Edm.Int32" Nullable="false" />
            </EntityType>
        </Schema>
        <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="Entities.Rules">
            <EntityType Name="Rule" Abstract="true">
                <Key>
                    <PropertyRef Name="Id" />
                </Key>
                <Property Name="Id" Type="Edm.String" Nullable="false" />
                <Property Name="Name" Type="Edm.String" />
                <Property Name="NodeType" Type="Edm.String" />
            </EntityType>
            <EntityType Name="ParentChildRule" BaseType="Entities.Rules.Rule">
                <Property Name="AllowedParentsTypes" Type="Edm.String" />
                <Property Name="ForbiddenParentsTypes" Type="Edm.String" />
            </EntityType>
            <EntityType Name="PredicateRule_1OfTNode" BaseType="Entities.Rules.Rule">
                <Property Name="Predicate" Type="System.Linq.Expressions.Expression_1OfFunc_2OfTNode_String" />
            </EntityType>
            <EntityType Name="PropertyRule" BaseType="Entities.Rules.Rule">
                <Property Name="RequiredProperties" Type="Edm.String" />
            </EntityType>
        </Schema>
        <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="System.Linq.Expressions">
            <ComplexType Name="Expression_1OfFunc_2OfTNode_String">
                <Property Name="Parameters" Type="Collection(System.Linq.Expressions.ParameterExpression)" />
            </ComplexType>
            <ComplexType Name="ParameterExpression" />
        </Schema>
        <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm">
            <Function Name="FullHierarchy" IsBound="true">
                <Parameter Name="bindingParameter" Type="Entities.Models.Hierarchy" />
                <ReturnType Type="Entities.Models.Hierarchy" />
            </Function>
            <EntityContainer Name="Container">
                <EntitySet Name="Hierarchies" EntityType="Entities.Models.Hierarchy" />
                <EntitySet Name="Nodes" EntityType="Entities.Models.Node" />
                <EntitySet Name="Sites" EntityType="Entities.Models.Site" />
                <EntitySet Name="Rules" EntityType="Entities.Rules.Rule" />
            </EntityContainer>
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>

Solution

  • There are several things:

    1. The schema needs to have a namespace. I.e. builder.Namespace = ""; needs to be removed or replaced with a non-empty string.
    2. The function needs to be invoked by it's fully qualified name. I.e. The request URL needs to be http://localhost:2390/Hierarchies('300924834')/<namespace>.FullHierarchy in which <namespace> should be the default namespace or the one you specify in item #1.
    3. As a function bound to a single entity, writing the controller method like this is adequate: [HttpGet] public IHttpActionResult FullHierarchy() { return Ok(new Hierarchy()); } I don't know the exact reason. I'd guess the convention model builder helps you with such routing.

    It works for me, and following are my test function details:

    WebApiConfig:

    builder.EntityType<Product>().Function("SomeFunction").Returns<string>();
    

    In the ProductsController:

    [HttpGet]
        public IHttpActionResult SomeFunction()
        {
            return Ok("Some");
        }
    

    The request:

    GET http://localhost:54017/Products(1)/Default.SomeFunction