Skip to content

Issue with OData Singleton entity controller actions #610

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

Closed
Graph2133 opened this issue Mar 18, 2020 · 7 comments
Closed

Issue with OData Singleton entity controller actions #610

Graph2133 opened this issue Mar 18, 2020 · 7 comments

Comments

@Graph2133
Copy link

Graph2133 commented Mar 18, 2020

Hello, could you please help me. I'm trying to configure endpoint in OData controller which is configured as Singleton in my edm model. I'm reusing this sample https://github.com/microsoft/aspnet-api-versioning/tree/master/samples/aspnetcore/SwaggerODataSample (release 4.0 because it's crucial for real project which I'm working on).

    public class PizzaConfiguration : IModelConfiguration
    {
        public void Apply(ODataModelBuilder builder, ApiVersion apiVersion)
        {
            builder.Singleton<Pizza>("Pizza");
            builder.EntityType<Pizza>().Select().Expand();
        }
    }

My controller has nothing extraordinary but just one simple action

    [ApiVersionNeutral]
    public class PizzaController : ODataController
    {
        [HttpGet]
        [Produces("application/json")]
        [EnableQuery(AllowedQueryOptions = AllowedQueryOptions.Select | 
             AllowedQueryOptions.Expand)]
        [ODataRoute("Pizza")]
        public SingleResult<Pizza> GetPizza(ODataQueryOptions<Pizza> options) 
            => SingleResult.Create(new[] { new Pizza { Name = "Test" } }.AsQueryable());
    }

But swashbuckle can not load api definitions and I'm getting this error:

System.Collections.Generic.KeyNotFoundException: The given key 'System.Reflection.TypeInfo' was not present in the dictionary.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at Swashbuckle.AspNetCore.SwaggerGen.PrimitiveSchemaGenerator.GenerateSchemaFor(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.ChainableSchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.ArraySchemaGenerator.GenerateSchemaFor(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.ChainableSchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateParameter(ApiDescription apiDescription, ApiParameterDescription apiParameter, SchemaRepository schemaRepository)
   at System.Linq.Enumerable.WhereSelectListIterator`2.ToList()
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperation(ApiDescription apiDescription, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperations(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GeneratePaths(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String documentName, String host, String basePath)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Server.IISIntegration.IISMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)

If I remove ODataQueryOptions all works but I need them for proper generation of expansion properties because I want load only needed data to my singleton result using query options (checking whether needed expansion property was requested).
This works with route "api/pizza" and swashbuckle generate proper schema:

    [ApiVersionNeutral]
    public class PizzaController : ODataController
    {
        [HttpGet]
        [Produces("application/json")]
        [EnableQuery(AllowedQueryOptions = AspNet.OData.Query.AllowedQueryOptions.Select | 
             AllowedQueryOptions.Expand)]
        [ODataRoute("Pizza")]
        public SingleResult<Pizza> GetPizza() 
            => SingleResult.Create(new[] { new Pizza { Name = "Test" } }.AsQueryable());
    }

And I've also question regarding generation of routes. Let's say iIve the same configuration I mentioned above but my controller will look like this (I will omit setting of ODataRoute and will use default routing convention)

    [ApiVersionNeutral]
    public class PizzaController : ODataController
    {
        [HttpGet]
        [Produces("application/json")]
        [EnableQuery(AllowedQueryOptions = AllowedQueryOptions.Select | 
            AllowedQueryOptions.Expand)]
        public SingleResult<Pizza> Get() 
            => SingleResult.Create(new[] { new Pizza { Name = "Test" } }.AsQueryable());
    }

Is it expected behavior that swagger action looks like this:

image

Shouldn't it be also api/pizza ?

@commonsensesoftware
Copy link
Collaborator

The first issue does not appear to be related to ODataQueryOptions. The issue is that there is no type information available. If you're not going to use SingleResult<Pizza>, then you need to use [ProducesResponseType(typeof(Pizza), StatusCodes.Status200OK)] or [ProducesResponseType(typeof(ODataValue<Pizza>), StatusCodes.Status200OK)]; otherwise, the API Explorer has no way to know what the model type is.

Reverse generation of route templates is difficult. There are no APIs that handle that. There are a number of complex OData routes. I'm not sure I have an example of a singleton. It's quite possible that scenario is broken. If the correct template is not being generated by convention, then the simplest workaround is to use attribute routing.

Do you have a repro you can share? Singleton route template generation should be supported. If it's not working correctly, I take it as a bug once verified.

@Graph2133
Copy link
Author

Graph2133 commented Mar 18, 2020

Oh, no I've tried with ProducesResponseType attribute (with typeof(T), typeof(ODataValue<T>) and typeof(SingleResult<T>)) and the result is the same, for example this one will also result in error:

    [ApiVersionNeutral]
    public class PizzaController : ODataController
    {
        [HttpGet]
        [Produces("application/json")]
        [EnableQuery(AllowedQueryOptions = AllowedQueryOptions.Filter | AllowedQueryOptions.Expand)]
        [ProducesResponseType(typeof(Pizza), StatusCodes.Status200OK)]
        [ODataRoute("Pizza")]
        public SingleResult<Pizza> GetPizza(ODataQueryOptions<Pizza> options) => SingleResult.Create(new[] { new Pizza { Name = "Test" } }.AsQueryable());
    }
System.Collections.Generic.KeyNotFoundException: The given key 'System.Reflection.TypeInfo' was not present in the dictionary.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at Swashbuckle.AspNetCore.SwaggerGen.PrimitiveSchemaGenerator.GenerateSchemaFor(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.ChainableSchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.ArraySchemaGenerator.GenerateSchemaFor(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.ChainableSchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateParameter(ApiDescription apiDescription, ApiParameterDescription apiParameter, SchemaRepository schemaRepository)
   at System.Linq.Enumerable.WhereSelectListIterator`2.ToList()
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperation(ApiDescription apiDescription, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperations(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GeneratePaths(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String documentName, String host, String basePath)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Server.IISIntegration.IISMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)

Sorry but no, I have no any prepared repository. I will need some time to prepare it.
Please give me up to one day.

@commonsensesoftware
Copy link
Collaborator

Hmmm ... interesting. While you're putting something together, I will attempt to investigate in parallel. Thanks.

@Graph2133
Copy link
Author

Quickly configured version of repository: https://github.com/Graph2133/ODataSingletonIssue

@pil0t
Copy link

pil0t commented Aug 4, 2020

Any news on this issue? Maybe an example of Singleton usage with Versioning?

@commonsensesoftware
Copy link
Collaborator

I'll categorize this as a bug since it's not what you expect (POLA); however, it's more of an enhancement. It seems I just missed the case for Singletons. I've personally never used them, but they should be supported if you want to use them.

The following top-level operations should be supported:

  • Entity sets
  • Operations (actions or functions + bounded or unbounded)
  • Singletons

There's currently no consideration for singleton generation at all. There's several other issues related to API exploration and route template generation. This fix will roll up into those. I'll use the repro for the Litmus test. Thanks.

@commonsensesoftware
Copy link
Collaborator

Singletons are properly supported in 5.0.0-RC.1. The official release will likely occur after 2 weeks of burn-in. The test suite and samples now include usage of singletons. If the issue returns, please reopen the issue or file a new one. 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