Search code examples
c#asp.net-coreodata.net-5

OData routing cannot find the route


This is my ApplicationUserController (I removed unneccessary parts like ctor etc):

[ODataRouting]
public class ApplicationUserController : ApiController
{
    [EnableQuery]
    [HttpGet("ApplicationUser")]
    [HttpGet("ApplicationUser/$count")]
    public IQueryable<ApplicationUser> Get()
    {
        return _applicationUserRepository.GetQueryable();
    }

    [HttpGet("ApplicationUser/{id}")]
    [HttpGet("ApplicationUser({id})")]
    public async Task<ApplicationUser> GetById([FromODataUri] long id)
    {
        return await _applicationUserRepository.SingleOrDefault(x => x.Id == id);
    }
}

The first function works perfectly. I call http://localhost:5000/v1/ApplicationUser?$filter=startsWith(LastName, 'Test') and I get all user that last name starts with "Test".

The second one doesn't work. I call

  • http://localhost:5000/v1/ApplicationUser/1
  • http://localhost:5000/v1/ApplicationUser(1)

and none of them do work. For both, I get a 404: Not found.

Is there something I did wrong? I also tried adding the EnableQuery-Attribute, but it doesn't make any difference.

Here my code from ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    IEdmModel v1 = _getEdmModel();
    
    services.AddOData(opt => opt
        .AddModel("v{version}", v1)
        .Select()
        .Expand()
        .OrderBy()
        .Filter()
        .Count()
    );
}

and my _getEdmModel():

private static IEdmModel _getEdmModel()
{
    ODataConventionModelBuilder builder = new (new DefaultAssemblyResolver());
    builder.EntitySet<ApplicationUser>("ApplicationUser");            
    builder.EntityType<ApplicationUser>().HasKey(x => x.Id); // edit: added this line
    return builder.GetEdmModel();
}

UPDATE:

I created a new repository, with basic code here: https://github.com/matthiasburger/ApplePie

same behaviour, reproducable with following URLs:

  • https://localhost:5001/api/testentity : works
  • https://localhost:5001/api/testentity?$filter=id%20eq%201 : works
  • https://localhost:5001/api/testentity/1 : doesnt work
  • https://localhost:5001/api/testentity(1) : doesnt work

Solution

  • It's weird, but if you change the method name "GetById" to "GetTestEntity" it works...


    Offtopic

    You can simplify your Serilog instance (removing a lot of code that's already in the host builer) by doing

    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    
    public static IHostBuilder CreateHostBuilder(string[] args) => Host
        .CreateDefaultBuilder(args) // sets all the configuration options right!
        .ConfigureAppConfiguration((hostingContext, config) =>
            config.AddJsonFile("secrets.json", optional: true, reloadOnChange: true))
        .UseSerilogLogger((hostBuilderContext, loggerConfiguration) => loggerConfiguration
            .ReadFrom.Configuration(hostBuilderContext.Configuration)
            .Enrich.FromLogContext()) // no output?
        .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder
            .UseStartup<Startup>()
        );
    

    The only thing you'll miss is the starting and final exception of the web host. But if there's a problem there, you'll notice anyhow.