aspnet-api-versioning icon indicating copy to clipboard operation
aspnet-api-versioning copied to clipboard

[Bug] Properties generated by DefaultModelTypeBuilder have invalid setter: without parameter

Open AndreaCuneo opened this issue 1 year ago • 3 comments
trafficstars

Is there an existing issue for this?

  • [X] I have searched the existing issues

Describe the bug

See https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/3051 for details.

Calling NullabilityInfoContext.Create(PropertyInfo) on Properties obtained from the Type generated by DefaultModelTypeBuilder throws IndexOutOfBoundException.

https://github.com/dotnet/runtime/blob/c1a9f26efa4fcf2e3fdcd8557da19d358f51eb00/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs#L217-L222

The culprit is that the generated setter doesn't have a parameter. That's indeed the case in DefaultModelTypeBuilder.

https://github.com/dotnet/aspnet-api-versioning/blob/3fc071913dcded23eeb5ebe55bca44f3828488bf/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs#L420

The line should define e null return and a parameter. See documentation.

var setter = addTo.DefineMethod( "set_" + name, propertyMethodAttributes, null, new [] {shouldBeAdded} );

Expected Behavior

NullabilityInfoContext.Create() doesn't throw when called with a Property taken from the generated type from DefaultTypeModelBuilder.

Forwarding Nullability CustomDataAttributes from the original type to the generated type would be nice but not required.

Steps To Reproduce

I don't have a minimal repro. However the non-minimal repro demonstrates the issue.

Clone and checkout https://github.com/ARKlab/Ark.Tools/commit/8c747682219ef6f863b8dd5cfa9d68d4edbabb0d cd Ark.Tools\Samples\TestProject dotnet test A test will fail due to an HTTP 500 error, which is caused by the IndexOutOfRangeException inside the test server.

Exceptions (if any)

Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Failed to generate Operation for action - WebApplicationDemo.Controllers.V1.PeopleController.Get (WebApplicationDemo). See inner exception
 ---> Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Failed to generate schema for type - System.Linq.IQueryable`1[WebApplicationDemo.Dto.Person]. See inner exception
 ---> System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at NullabilityInfo System.Reflection.NullabilityInfoContext.Create(PropertyInfo propertyInfo)
   at NullabilityInfo Swashbuckle.AspNetCore.SwaggerGen.MemberInfoExtensions.GetNullabilityInfo(MemberInfo memberInfo)
   at bool Swashbuckle.AspNetCore.SwaggerGen.MemberInfoExtensions.IsNonNullableReferenceType(MemberInfo memberInfo)
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForMember(Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo, DataProperty dataProperty)
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.CreateObjectSchema(DataContract dataContract, SchemaRepository schemaRepository)
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository)+() => { } [3]
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateReferencedSchema(DataContract dataContract, SchemaRepository schemaRepository, Func<OpenApiSchema> definitionFactory)
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository)
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type modelType, SchemaRepository schemaRepository)
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo)
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.CreateArraySchema(DataContract dataContract, SchemaRepository schemaRepository)
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository)+() => { } [1]
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository)
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchemaForType(Type modelType, SchemaRepository schemaRepository)
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo)
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, PropertyInfo propertyInfo, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo)
   --- End of inner exception stack trace ---
   at OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateSchema(Type type, SchemaRepository schemaRepository, PropertyInfo propertyInfo, ParameterInfo parameterInfo, ApiParameterRouteInfo routeInfo)
   at OpenApiMediaType Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.CreateResponseMediaType(Type modelType, SchemaRepository schemaRespository)
   at OpenApiResponse Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateResponse(ApiDescription apiDescription, SchemaRepository schemaRepository, string statusCode, ApiResponseType apiResponseType)+(string contentType) => { } [2]
   at Dictionary<TKey, TElement> System.Linq.Enumerable.ToDictionary<TSource, TKey, TElement>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer) x 2
   at OpenApiResponse Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateResponse(ApiDescription apiDescription, SchemaRepository schemaRepository, string statusCode, ApiResponseType apiResponseType)
   at OpenApiResponses Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateResponses(ApiDescription apiDescription, SchemaRepository schemaRepository)
   at async Task<OpenApiOperation> Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperationAsync(ApiDescription apiDescription, SchemaRepository schemaRepository, Func<ApiDescription, SchemaRepository, Task<List<OpenApiParameter>>> parametersGenerator, Func<ApiDescription, SchemaRepository, Task<OpenApiRequestBody>> bodyGenerator, Func<OpenApiOperation, OperationFilterContext, Task> applyFilters)
   --- End of inner exception stack trace ---
   at async Task<OpenApiOperation> Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperationAsync(ApiDescription apiDescription, SchemaRepository schemaRepository, Func<ApiDescription, SchemaRepository, Task<List<OpenApiParameter>>> parametersGenerator, Func<ApiDescription, SchemaRepository, Task<OpenApiRequestBody>> bodyGenerator, Func<OpenApiOperation, OperationFilterContext, Task> applyFilters) x 2
   at async Task<Dictionary<OperationType, OpenApiOperation>> Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperationsAsync(IEnumerable<ApiDescription> apiDescriptions, SchemaRepository schemaRepository)
   at async Task<OpenApiPaths> Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GeneratePathsAsync(IEnumerable<ApiDescription> apiDescriptions, SchemaRepository schemaRepository, Func<IGrouping<string, ApiDescription>, SchemaRepository, Task<Dictionary<OperationType, OpenApiOperation>>> operationsGenerator) x 2
   at async Task<OpenApiDocument> Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwaggerAsync(string documentName, string host, string basePath)
   at async Task Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at async Task Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
   at async Task Hellang.Middleware.ProblemDetails.ProblemDetailsMiddleware.Invoke(HttpContext context)

.NET Version

net8.0

Anything else?

No response

AndreaCuneo avatar Aug 28 '24 07:08 AndreaCuneo

This is still quite problematic, since we cannot upgrade many of our references due to this conflict between Versioning + OData + Swashbuckle

spaasis avatar Feb 04 '25 09:02 spaasis

This is still quite problematic, since we cannot upgrade many of our references due to this conflict between Versioning + OData + Swashbuckle

Same here.

ovirta avatar Apr 04 '25 05:04 ovirta

@commonsensesoftware sorry for bumping this and being blunt, but other than reporting a bug and contributing a PR, I'm not sure how to do more here to support you in maintaining this great library and releasing fixes.

I'm more than willing to help you help me.

Is there a plan to release a fix? Or you plan to do it in .net 10?

AndreaCuneo avatar Jun 16 '25 07:06 AndreaCuneo

@commonsensesoftware I saw your update on #1140 - a huge thanks for your work and commitment is absolutely in order ❤️ I don't see a way to sponsor your open source work, but if you decide to set some up, I'll be sure to throw you a few pints' worth!

You asked for specific issues, and this one has been a thorn on many projects for a long time now and actually the only problem I've had with the library. If you can find the time, fixing this would be much appreciated!

spaasis avatar Oct 28 '25 07:10 spaasis

Apologies for the pain. You've done the work. Moving this issue up to the top of my queue and items to get pushed through. I will try to get this release this week - or sooner. Bad maintainer. No donut.

commonsensesoftware avatar Nov 15 '25 22:11 commonsensesoftware