OData v4 Custom Function

12,470

Per the model builder, the function GetByExternalKey is a bound function. According to the OData Protocol v4, a bound function is invoked through the namespace or alias qualified named, so you need to add more in the route attribute:

[HttpGet]
[ODataRoute("Orders({id})/Your.Namespace.GetByExternalKey(key={key})")]
public IHttpActionResult GetByExternalKey(long key)
{
   return Ok(from o in db.Orders
      where//SpecialCrazyStuff is done
      select o);
}

If you don't know the namespace, just add below to the method GetModel():

builder.Namespace = typeof(Order).Namespace;

And replace "Your.Namespace" with the namespace of the type Order.

Here are 2 samples related to your question, just for your reference: https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/ODataFunctionSample/

https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/ODataAttributeRoutingSample/

Share:
12,470
Admin
Author by

Admin

Updated on June 18, 2022

Comments

  • Admin
    Admin almost 2 years

    I'm trying to create a custom function in an OData v4 Web API solution. I need to return a collection of "Orders" based on unique logic that can't be handled natively by OData. I cannot seem to figure out how to create this custom function without destroying the entire OData service layer. When I decorate the Controller method with an ODataRoute attribute it all goes to hell. Any basic request produces the same error. Can someone please take a look at the code below and see if you notice something that I must be missing?

    WebApiConfig.cs

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
    
            config.MapHttpAttributeRoutes();
    
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
    
            config.MapODataServiceRoute("odata", "odata", model: GetModel());
        }
    
        public static Microsoft.OData.Edm.IEdmModel GetModel()
        {
            ODataModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<Account>("Accounts");
            builder.EntitySet<Email>("Emails");
            builder.EntitySet<PhoneNumber>("PhoneNumbers");
            builder.EntitySet<Account>("Accounts");
            builder.EntitySet<Address>("Addresses");
            builder.EntitySet<Order>("Orders");
            builder.EntitySet<OrderDetail>("OrderDetails");
    
            var orders = builder.EntityType<Order>();
            var function = orders.Function("GetByExternalKey");
            function.Parameter<long>("key");
            function.ReturnsCollectionFromEntitySet<Order>("Orders");
    
            return builder.GetEdmModel();
         }
     }
    

    OrdersController.cs

    public class OrdersController : ODataController
    {
        private SomeContext db = new SomeContext();
    
        ...Other Stuff...
    
        [HttpGet]
        [ODataRoute("GetByExternalKey(key={key})")]
        public IHttpActionResult GetByExternalKey(long key)
        {
           return Ok(from o in db.Orders
              where //SpecialCrazyStuff is done
              select o);
        }
    }
    }
    

    When issuing ANY request against the OData layer I receive the following error response.

    The path template 'GetByExternalKey(key={key})' on the action 'GetByExternalKey' in controller 'Orders' is not a valid OData path template. Resource not found for the segment 'GetByExternalKey'.