Ricardo Peres
Ricardo Peres

Reputation: 14535

ASP.NET Core 3.1 OData: Cannot Select Entity By Id

I have an OData endpoint (action method) that returns an IQueryable list of items:

[EnableQuery]
public IQueryable<Entity> Get()

The POCO class has the [Key] attribute applied to its id property. It works fine, all entities are returned and I can do filters, etc. But I cannot select an instance by its id, that is:

/odata/entity?$filter=Id eq 1   //works
/odata/entity(1)                //does not work

Is there any setup that I need to do? I also tried configuring the key in the model:

var odataBuilder = new ODataConventionModelBuilder();
odataBuilder.EntitySet<Student>("Entity").EntityType.HasKey(x => x.Id);

But it also dit not work. The only way I can achieve this is by adding an action method specifically for this:

public Entity Get(int key)      //works

But I was under the impression that this would not be needed, the other action should be enough. Am I wrong?

Upvotes: 2

Views: 872

Answers (2)

yzorg
yzorg

Reputation: 4450

TL;DR: use ODataRoutePrefix + ODataRoute because [Route("api/[controller]")] can't handle OData api/todo(10) for single navigable entity.

To get the OData convention for api/todo(10) working:

            // Startup - Configure()
            app.UseEndpoints(endpoints =>
            {
                endpoints.Count().Filter().OrderBy().Expand().Select().MaxTop(100);
                endpoints.MapODataRoute("odata", "odata", GetEdmModel())
                    .EnableDependencyInjection();
//[Route("odata/[controller]")] // remove this
[ODataPrefix("todo")]           // add this
public class TodoController : ODataController {

        // /odata/todo?$filter=id eq 142
        [EnableQuery] 
        public IQueryable<Todo> Get(
            [FromServices] MyDataContext dataContext)
        {
            return dataContext.Todo;
        }

        [EnableQuery]
        [ODataRoute("({id})")] // /odata/todo(142)
        public IActionResult GetByKey(
            [FromODataUri] long id,
            [FromServices] MyDataContext dataContext)
        {
            return Ok(dataContext.Todo.SingleOrDefault(x => x.Id == id));
        }

None of the OData samples for ASP.NET Core use [ODataRoutePrefix] and [ODataRoute] but those are necessary for routing using OData convention api/todo(10).

If you use normal [Route("api/[controller]")] on controller and [Route("({id})"] on GetByKey() then your route is

  • ~/api/todo/(10) - BADBAD
  • ~/api/todo(10) - GOOD

.. the later is the convention used in many samples including https://www.odata.org/getting-started/basic-tutorial/ .

This also seems to break the helper Created(todo) used in POST samples, because it can't find api/todo({newId}) route to return in the Location header.

Upvotes: 2

Ricardo Peres
Ricardo Peres

Reputation: 14535

Based on my research and the examples found, I don't think this is possible, e.g., there is always a need for a dedicated method for this.

Upvotes: 2

Related Questions