3
votes

I have an ASP.NET Core API service with OData v4 support. I am trying to introduce a controller that is based on an entity with no key. OData requires a key for the entities registered hence my issue. You could consider this more as an action method more than anything.

This problem shouldn't be specific to ASP.NET Core but was wondering if anyone has ever done this successfully with OData. I've tried both registering one of the properties in the CheckRequest model but it failed saying it should be a navigational property. I also tried just registering it as a complex type since it had no key but the service would then return 404 for the route.

See below for samples of code and more details. Note that I'm using Microsoft.AspNetCore.App 2.1.5, Microsoft.AspNetCore.OData 7.0.1, and Microsoft.AspNetCore.OData.Versioning 3.0.0-beta1. For the most part our application OData setup is based on the samples online here: https://github.com/Microsoft/aspnet-api-versioning/tree/master/samples/aspnetcore/ODataBasicSample

[ApiVersion("1.0")]
[ODataRoutePrefix("checkrequests")]
public class CheckRequestsController : ODataController
{
   [HttpPost]
   [ODataRoute]
   public async Task<IActionResult> CheckAsync([FromBody] CheckRequest request)
   {
      var success = request.Values.Count == 3 && request.OtherValues.Count == 6;
      return this.Ok(await Task.FromResult(new CheckResult { Success = true }));
   }
}

public class CheckRequest
{
   public List<int> Values { get; set; }
   public List<int> OtherValues { get; set; }
}

public class CheckResult
{
   public bool Success { get; set; }
}

public class CheckRequestModelConfiguration : IModelConfiguration
{
   public void Apply(
      ODataModelBuilder builder,
      ApiVersion apiVersion)
   {
      switch (apiVersion.MajorVersion)
      {
         default:
            ConfigureV1(builder);
            break;
      }
   }

   private static void ConfigureV1(ODataModelBuilder builder)
   {
      builder.ComplexType<V1.AccessCheckRequest>();
   }
}

public class Startup
{
   public void ConfigureServices(IServiceCollection services)
   {
      services.AddMvc();
      services.AddApiVersioning();
      services.AddOData().EnableApiVersioning();
   }

   public void Configure(
      IApplicationBuilder app,
      VersionedODataModelBuilder modelBuilder)
   {
      app.UseMvc(routes =>
      {
         routes.MapVersionedODataRoutes("odata-bypath", "v{version:apiVersion}", modelBuilder.GetEdmModels());
      });
   }
}
1

1 Answers

0
votes

So after digging through a bit more I found the answer. OData v4 has support for unbound functions and actions which don't have an binded to them, essentially utility methods.

More documentation can be found here: https://odata.github.io/WebApi/#02-03-model-builder-nonconvention in the section for unbound actions and functions.

The change to the above code sample that works is to change just the following section:

public class CheckRequestModelConfiguration : IModelConfiguration
{
   public void Apply(
      ODataModelBuilder builder,
      ApiVersion apiVersion)
   {
      switch (apiVersion.MajorVersion)
      {
         default:
            ConfigureV1(builder);
            break;
      }
   }

   private static void ConfigureV1(ODataModelBuilder builder)
   {
      builder.AddFunction("checkrequests").Returns<CheckResult>();
   }
}