Getting OData Count in ASP.NET Core WebAPI

I could reproduce your issue when i use [Route("api/[controller]")]and [ApiController] with the startup.cs like below:

app.UseMvc(routeBuilder =>
        {
            routeBuilder.Expand().Select().Count().OrderBy().Filter();
            routeBuilder.EnableDependencyInjection();
        });

To fix it,be sure you have built a private method to do a handshake between your existing data models (OData model in this case) and EDM.

Here is a simple demo:

1.Controller(comment on Route attribute and ApiController attribute):

//[Route("api/[controller]")]
//[ApiController]
public class StudentsController : ControllerBase
{
    private readonly WSDbContext _context;
    public StudentsController(WSDbContext context)
    {
        _context = context;
    }
    // GET: api/Students
    [HttpGet]
    [EnableQuery()]
    public IEnumerable<Student> Get()
    {
        return _context.Students;
    }
}
//[Route("api/[controller]")]
//[ApiController]
public class SchoolsController : ControllerBase
{
    private readonly WSDbContext _context;
    public SchoolsController(WSDbContext context)
    {
        _context = context;
    }
    // GET: api/Schools
    [HttpGet]
    [EnableQuery()]
    public IEnumerable<School> Get()
    {
        return _context.Schools;
    }

2.Startup.cs():

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvcCore(action => action.EnableEndpointRouting = false);
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        var connection = @"Server=(localdb)\mssqllocaldb;Database=WSDB;Trusted_Connection=True;ConnectRetryCount=0";
        services.AddDbContext<WSDbContext>(options => options.UseSqlServer(connection));
        services.AddOData();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseMvc(routeBuilder =>
        {
            routeBuilder.Expand().Select().Count().OrderBy().Filter();
            routeBuilder.MapODataServiceRoute("api", "api", GetEdmModel());
        });
    }

    private static IEdmModel GetEdmModel()
    {
        var builder = new ODataConventionModelBuilder();
        builder.EntitySet<Student>("Students");
        builder.EntitySet<Student>("Schools");
        return builder.GetEdmModel();
    }
}

In my case I wanted to extend existing Api methods with [EnableQuery] but have it include the count metadata.

I ended up extending the EnableQuery attribute to return a different reponse, it worked perfectly.

public class EnableQueryWithMetadataAttribute : EnableQueryAttribute
{
    public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
    {
        base.OnActionExecuted(actionExecutedContext);

        if (actionExecutedContext.Result is ObjectResult obj && obj.Value is IQueryable qry)
        {
            obj.Value = new ODataResponse
            {
                Count = actionExecutedContext.HttpContext.Request.ODataFeature().TotalCount,
                Value = qry
            };
        }
    }
    public class ODataResponse
    {
        [JsonPropertyName("@odata.count")]
        public long? Count { get; set; }

        [JsonPropertyName("value")]
        public IQueryable Value { get; set; }
    }
}

Just been battling this.

I found that if I request my controller at /api/Things that most of the OData options work but $count doesn't.

However, $count does work if I request the same method via /odata/Things.