Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API Explorer Settings Not Honored with Implicit API Version #519

Closed
commonsensesoftware opened this issue Jul 16, 2019 · 8 comments
Closed

Comments

@commonsensesoftware
Copy link
Collaborator

commonsensesoftware commented Jul 16, 2019

Symptoms

When OData is mixed and matched with non-OData controllers and actions, then [ApiExplorerSettings] are not honored.

Triage

After investigation, the issue doesn't seem to be specific to OData, but rather any controller or action that uses implicit versioning (e.g. no explicit attributes or conventions). This should be allowed, but is missed do to the underlying ControllerModel not being attached to implicitly versioned ActionModel instances.

Workaround

There are two simple workarounds to this problem until the next patch.

  • Apply an explicit API version with an attribute or convention (ex: [ApiVersion("1.0")])
  • Apply the [ApiExplorerSettings(IgnoreApi=false)] directly on actions rather than the controller
@commonsensesoftware commonsensesoftware self-assigned this Jul 16, 2019
@commonsensesoftware commonsensesoftware changed the title API Explorer Settings Not Honored When Mixing and Matching OData API Explorer Settings Not Honored with Implicit API Version Jul 17, 2019
@commonsensesoftware
Copy link
Collaborator Author

@ckuykendall81 I've updated the title and details, which should immediately unblock you. The simplest path for you would be to use an explicit API version. This can easily be done with an attribute, but if you have a lot of controllers, you can address that using a convention instead.

You should also know that you'll probably be snagged by OData/WebApi#1750 as well. I've updated your example to address this external problem as well as a few other minor tweaks. I was able to get things working with these changes in place.

Let me know if you have any questions.

WebApplication2.zip (Revised)

@ckuykendall81
Copy link

Couldn't get api version to work for me, but the adding the other attribute to the actions did work and satisfied my need. Thank you.

@manisha201301
Copy link

Hi @commonsensesoftware , I am facing the same error here and I tried your WebApplication2.zip . I still face the problem,
When I try to have my Startup.cs like below:
app.UseMvc(routeBuilder => { routeBuilder.Select().Expand().Filter().OrderBy(QueryOptionSetting.Allowed).MaxTop(200).Count(); routeBuilder.MapVersionedODataRoutes("odata", "odata", modelBuilder.GetEdmModels()); routeBuilder.EnableDependencyInjection(); });
I get the error:

Microsoft.OData.UriParser.ODataUnrecognizedPathException HResult=0x80131509 Message=Resource not found for the segment 'ControllerName2'. Source=Microsoft.OData.Core StackTrace: at Microsoft.OData.UriParser.ODataPathParser.CreateDynamicPathSegment(ODataPathSegment previous, String identifier, String parenthesisExpression) at Microsoft.OData.UriParser.ODataPathParser.CreateFirstSegment(String segmentText) at Microsoft.OData.UriParser.ODataPathParser.ParsePath(ICollection1 segments)`

and when I try to create my own edm-model like
app.UseMvc(routeBuilder => { var builder = new ODataConventionModelBuilder(); builder.EntitySet<Class1>("Class1"); builder.EntitySet<Class2>("Class2"); model = builder.GetEdmModel(); routeBuilder.Select().Expand().Filter().OrderBy().MaxTop(200).Count(); routeBuilder.MapVersionedODataRoute("odata", "odata", model, new ApiVersion(1, 0)); routeBuilder.EnableDependencyInjection(); });

I get the error:
Microsoft.OData.ODataException HResult=0x80131509 Message=Key template value 'ControllerName2' for key segment 'Class2-Id' does not start with '{' or ends with '}'. Source=Microsoft.AspNetCore.OData StackTrace: at Microsoft.AspNet.OData.Routing.Template.KeySegmentTemplate.BuildKeyMappings(IEnumerable1 keys)
at Microsoft.AspNet.OData.Routing.Template.KeySegmentTemplate..ctor(KeySegment segment)`

I am really confused on where I am going wrong here. Any leads here?

-Thanks

@commonsensesoftware
Copy link
Collaborator Author

@manisha201301 I don't think I have enough context to give you an answer. Are you using attributes or pure conventions? It looks like the routes, explicit or implicit, are not matching up

@manisha201301
Copy link

manisha201301 commented Jul 9, 2020

Hi,
Thanks for replying,

I am using the Startup.cs from the sample, the only change is the customized EdmModel, which I created like this:

var builder = new ODataConventionModelBuilder(); builder.EntitySet<Class-Model1>("Controller1"); builder.EntitySet<Class-Model2>("Controller2"); _edmModel = builder.GetEdmModel();

My Controller is exactly similar to TestController of the Sample

and My Class-Model1/Class-Model-2 are POCOs like this:
public class Class-Model1 { [DataMember, Key] public string Id { get; set; } [DataMember] public string Name { get; set; } }
I don't understand what do you mean by attributes/pure-conventions.

@commonsensesoftware
Copy link
Collaborator Author

@manisha201301 if you're using [ODataRoutePrefix("Controller1")] or [ODataRoute("Controller1")], then you're using attributes. If you're not, you're using conventions. I'm a bit confused because the code you've posted shouldn't even compile. The - character is not valid in identifiers. If you're not using attributes, the name of the entity set must match the name of the controller; that's just how OData works. When using attributes, the route segment corresponding to the entity set still has to match what you registered in the EDM, even if the controller type name is different.

I'd also point out that you can use pure POCOs with OData. If you follow it's conventions, you need no attribution at all. If you don't follow the convention in your models, you can still apply conventions in the builder so that no attributes are required (but that's obviously a choice between A or B).

@manisha201301
Copy link

manisha201301 commented Jul 9, 2020

Thank you for replying,
I am using attributes and I am using ApiVersioning just to make it work.. I figured out my problem, my edmModel is not constructed properly, I am not able to use edmModel here:

            app.UseMvc(routeBuilder =>
            {
                ODataModelBuilder builder = new ODataModelBuilder();
                builder.EntitySet<Test>("Tests");

                routeBuilder.Select().Expand().Filter().OrderBy(QueryOptionSetting.Allowed).MaxTop(200).Count();
                routeBuilder.MapVersionedODataRoute("odata", "api", builder.GetEdmModel(), new ApiVersion(1, 0));
                //routeBuilder.MapVersionedODataRoutes("odata", "api", modelBuilder.GetEdmModels());
                //routeBuilder.MapODataServiceRoute(routeName: "odata", routePrefix: "api", model: model);
                routeBuilder.EnableDependencyInjection();
            });

I get the following error:

System.InvalidOperationException
  HResult=0x80131509
  Message=The entity set 'Tests' is based on type 'WebApplication2.Test' that has no keys defined.
  Source=Microsoft.AspNetCore.OData
  StackTrace:
   at Microsoft.AspNet.OData.Builder.ODataModelBuilder.ValidateModel(IEdmModel model)
   at Microsoft.AspNet.OData.Builder.ODataModelBuilder.GetEdmModel()
   at WebApplication2.Startup.<>c.<Configure>b__5_0(IRouteBuilder routeBuilder) in 

even after changing Tests to have key:

namespace WebApplication2
{
    public class Test
    {
        [Key]
        public int Id { get; set; }
    }
}

Any idea, what must have gone wrong?

@manisha201301
Copy link

Nevermind, I solved it. Thanks :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants