API and OData behavior if there are two methods in the controller

Viewed 54

Here I have three controllers which inherited from the same controller.

All three controllers in the same configurations.

When I do the following queries; https://localhost:7069/odata/Persons or https://localhost:7069/odata/Employees the method with this signature which is called

public ActionResult<IQueryable<TModel>> Get(ODataQueryOptions<TModel> oDataQueryOptions)

This is the expected behavior

Problem

When I make this request https://localhost:7069/odata/Staffs

It is this method that is called

public async Task<ActionResult<TModel>> Get(int id)

If I remove the Get(int id) method.

The Get(ODataQueryOptions oDataQueryOptions) method which is called And everything works

Questions

Where does the problem come from ?

Setup me

Here are the two methods which is in the base controller :

[HttpGet]
[EnableQuery(MaxExpansionDepth = 10, MaxNodeCount = 1000)]
public ActionResult<IQueryable<TModel>> Get(ODataQueryOptions<TModel> oDataQueryOptions)
{
    modelService.ODataQueryOptions = oDataQueryOptions;
    var result = modelService.GetModelsAsync().Result;
    return Ok(result.Value);
}
[HttpGet("{id}")]
public async Task<ActionResult<TModel>> Get(int id)
{
      var result = await modelService.GetModelAsync(id);
     return OK(result);
}

The controllers inherit: they have the same configurations:

[Route("api/[controller]")]
[ApiController]
public class EmployeesController : PersonCollectionControllerBase<EmployeeDto, EmployeesService>
{
    public EmployeesController(IEmployeesService employeesService): base(employeesService)
    {
            
    }
}
[Route("api/[controller]")]
[ApiController]
public class PersonsController : PersonCollectionControllerBase<PersonDto, PersonsService>
{
    public PersonsController(IPersonsService persons): base(persons)
    {
    }
}
[Route("api/[controller]")]
[ApiController]
public class StaffsController : PersonCollectionControllerBase<StaffDto, StaffsService>
{
    public StaffsController(IStaffService modelService) : base(modelService)
    {
    }
}

IServiceCollection configuration:

builder.Services.AddControllers().AddOData(options =>
    {
        options.AddRouteComponents(routePrefix: "odata", GetEdmModel())
            .Select().Count().Filter().Expand().OrderBy().SetMaxTop(maxTopValue: 100);
    });

IEdm configuration :

public static IEdmModel GetEdmModel()
{
    ODataConventionModelBuilder builder = new();
    builder.EntitySet<PersonDto>(name: "Person")
            .EntityType.HasKey(person => person.BusinessEntityId);
    builder.EntitySet<PersonDtoBase>(name: "PersonsBase")
            .EntityType.HasKey(person => person.BusinessEntityId);
    builder.EntitySet<EmployeeDto>(name: "Employees")
            .EntityType.HasKey(employee => employee.BusinessEntityId);
    builder.EntitySet<EmployeeDtoBase>(name: "EmployeesBase")
            .EntityType.HasKey(employee => employee.BusinessEntityId);
    builder.EntitySet<StaffDto>(name: "Staffs")
        .EntityType.HasKey(staff => staff.BusinessEntityId);
    return builder.GetEdmModel();
}
1 Answers

The cause of problems is the name id in method Get(int id) and in its attribute [HttpGet("{id}")] they must be replaced by key.

The attribute [Route(“api/[Controller]”)], the controller must be explained example [Route(“odata/staffs”)] do it for any controllers that use Odata,

Another thing if you want to use Odata and Api endpoints in Route attribute replace odata with api example [Route(“api/staffs”)]

This solution is suggested by Juliano Leal Goncalves in the site https://github.com/OData/AspNetCoreOData/issues/664

Related