OData / WebApi

OData Web API: A server library built upon ODataLib and WebApi
https://docs.microsoft.com/odata
Other
854 stars 475 forks source link

EnableQuery does not work at Method or Controller Level without setting global query options. #1179

Open memilavi opened 6 years ago

memilavi commented 6 years ago

Installed the Beta 1 of OData 7.0.0 for ASP.NET Core, but couldn't make it work. Whenever I try to use $filter in the QueryString I get the error: "The query specified in the URI is not valid. Could not find a property named 'Url' on type 'Edm.String'." I made sure that 'Url' (in this specific casing, as well as other) is part of the Blog entity.

Assemblies affected

Microsoft.AspNetCore.OData 7.0.0-beta1 Microsoft.OData.Core (7.3.1) My website uses also EF core, not sure if it's rellevant. If so - Microsoft.EntityFrameworkCore.SqlServer (2.0.1)

Reproduce steps

  1. Add this to ConfigureServices method in Startup.cs: services.AddOData(); services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("BloggingDatabase"))); (Note that BloggingDatabase is the one used in the Getting Started of EF Core here: https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/existing-db)

  2. Add this to the Configure method in Startup.cs: var builder = new ODataConventionModelBuilder(app.ApplicationServices); builder.EntitySet("Products"); builder.EntitySet("Blogs").EntityType.Filter(Microsoft.AspNet.OData.Query.QueryOptionSetting.Allowed);

        app.UseMvc(routeBuilder=>routeBuilder.MapODataServiceRoute("ODataRoute",null,builder.GetEdmModel()));
        app.UseMvc(routeBuilder => routeBuilder.EnableDependencyInjection());
  3. Create a new Controller named BlogsController: using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using ODataTest.Models; using Microsoft.AspNet.OData;

namespace ODataTest.Controllers { [Produces("application/json")] [Route("api/Blogs")] [EnableQuery(AllowedQueryOptions =Microsoft.AspNet.OData.Query.AllowedQueryOptions.All)] public class BlogsController : ODataController { private readonly BloggingContext _context;

    public BlogsController(BloggingContext context)
    {
        _context = context;
    }

    // GET: api/Blogs
    [HttpGet]
    [EnableQuery(AllowedQueryOptions = Microsoft.AspNet.OData.Query.AllowedQueryOptions.All)]
    public IEnumerable<Blog> GetBlog()
    {
        return _context.Blog;
    }       
}

}

  1. Run scaffolding on the DB to create the model classes. As a result, a context and a Blog class (also a Post class) are generated from the DB. This is the Blog class: using System; using System.Collections.Generic;

namespace ODataTest.Models { public partial class Blog { public Blog() { Post = new HashSet(); }

    public int BlogId { get; set; }
    public string Url { get; set; }

    public ICollection<Post> Post { get; set; }
}

}

When running the app end navigating to: http://localhost:59924/api/blogs

The result is: [{"blogId":1,"url":"http://blogs.msdn.com/dotnet","post":[]},{"blogId":2,"url":"http://blogs.msdn.com/webdev","post":[]},{"blogId":3,"url":"http://blogs.msdn.com/visualstudio","post":[]}]

Expected result

When navigating to: http://localhost:59924/api/blogs?$filter=contains(Url,%27webdev%27)

The expected result is: [{"blogId":2,"url":"http://blogs.msdn.com/webdev","post":[]}]

Actual result

This error appears: "The query specified in the URI is not valid. Could not find a property named 'Url' on type 'Edm.String'."

Additional Details

It's interesting to note that when the EnableQuery attribute was applied only at the controller level and not on the Action itself, the error message was different, and stated that $filter cannot be used. I'm not sure if this is a bug or by design.

sorcerb commented 6 years ago

Case sensitive. Try: http://localhost:59924/api/blogs?$filter=contains(url,%27webdev%27)

memilavi commented 6 years ago

Nope, got the exact same message...

sorcerb commented 6 years ago

I have same problem with $select. This help me: routeBuilder.Count().Filter().OrderBy().Expand().Select().MaxTop(null);

app.UseMvc(routeBuilder =>
{
    routeBuilder.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
    routeBuilder.MapODataServiceRoute("ODataRoute", "odata", builder.GetEdmModel());
    routeBuilder.EnableDependencyInjection();
});

But with $filter all work without this line.

memilavi commented 6 years ago

Yes! That did it! Thanks!

Is it somewhere in the docs and I missed it?

sorcerb commented 6 years ago

I found it here: http://odata.github.io/WebApi/#13-01-modelbound-attribute $filter work for me, because I have: builder.EntitySet<Categories>("Categories").EntityType.HasKey(s => s.CategoryId).Filter(Microsoft.AspNet.OData.Query.QueryOptionSetting.Allowed);

Why this line didn't help you with $filter... interesting....: builder.EntitySet("Blogs").EntityType.Filter(Microsoft.AspNet.OData.Query.QueryOptionSetting.Allowed);

With $select I had another error - cannot be used

"The query specified in the URI is not valid. The property 'Name' cannot be used in the $select query option."

robward-ms commented 6 years ago

@memilavi - Yep, that is a new requirement for WebApi 6.0 and therefore 7.0 as @sorcerb pointed out.

Seems like there is still an issue to investigate here as your model was set to allow all query options so let's leave the issue open to track that. I'd like to change the title though if that's OK.

memilavi commented 6 years ago

Sure, go ahead.

robward-ms commented 6 years ago

@memilavi - I tried with both the EnableQuery on the controller and the controller & action and filtering worked without the global query option as long as the model defined filter as enabled. So this should work expected. Couple of questions:

1.) I noticed you have an "[Route("api/Blogs")]" attribute on the controller and the example Url contains api/Blogs. This returned a 500 for me, I used a Url without api, i.e. http://localhost:59924/blogs because the prefix parameter to MapODataServiceRoute was null. Did you try with or without "api"?

2.) Is there more to your controller and/or can you verify that the GetBlog() method is hit using a breakpoint?

Angelinsky7 commented 6 years ago

for me with version 7.0.1 this

routes.Count().Filter().OrderBy().Expand().Select().MaxTop(null);

in

app.UseMvc(routes => { ... }

is not working anymore without adding this to each controller

[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]

Is this normal ? How can i set by default for all my controllers thoses options ? Thanks

buchatsky commented 5 years ago

This help me: routeBuilder.Count().Filter().OrderBy().Expand().Select().MaxTop(null);

If you use app.UseOData("odata", "odata", GetEdmModel()); instead of

app.UseMvc(routeBuilder =>
{
    routeBuilder.MapODataServiceRoute("odata", "odata", GetEdmModel());
    ...
}

there is no need for explicit calling the line above just for OData routes

iant-ee commented 4 years ago

I found it here: http://odata.github.io/WebApi/#13-01-modelbound-attribute

This link has now moved to https://docs.microsoft.com/en-us/odata/webapi/modelbound-attribute (shame Microsoft never seem to add redirects when they reorganise documentation)