Background and Motivation
The Microsoft.AspNetCore.OpenAPI library provides a number of useful extensions for generating OpenAPI documents, but it does not work well with ASP.NET API Versioning out-of-the-box. ASP.NET API Versioning has provided a new library to bridge the gaps, but it is forced to use unwieldly Reflection techniques to make things work because all of the necessary extension points in Microsoft.AspNetCore.OpenAPI are internal.
While the Reflection workaround is a short-term fix, it is significantly slower, complicates or breaks the AOT story, and places the extensions at future risk of non-visible changes that may only trigger runtime failures. All of these reflect negatively upon developer and customer experience.
These changes are requested to be part of a .NET 10 servicing release. .NET 11 and beyond should consider an alternate design.
Proposed API
using System.ComponentModel;
namespace Microsoft.AspNetCore.OpenApi;
+ [EditorBrowsable(EditorBrowsableState.Never)]
- internal sealed class OpenApiSchemaService( /* ... */ )
+ public sealed class OpenApiSchemaService( /* ... */ )
{
}
+ [EditorBrowsable(EditorBrowsableState.Never)]
- internal interface IDocumentProvider
+ public interface IDocumentProvider
{
}
+ [EditorBrowsable(EditorBrowsableState.Never)]
- internal sealed class NamedService<TService>(string name)
+ public sealed class NamedService<TService>(string name)
{
}
+ [EditorBrowsable(EditorBrowsableState.Never)]
- internal sealed class OpenApiDocumentProvider(IServiceProvider serviceProvider) : IDocumentProvider
+ public sealed class OpenApiDocumentProvider(IServiceProvider serviceProvider) : IDocumentProvider
{
}
+ [EditorBrowsable(EditorBrowsableState.Never)]
- internal sealed class OpenApiDocumentService( /* .. */ ) : IOpenApiDocumentProvider
+ public sealed class OpenApiDocumentService( /* .. */ ) : IOpenApiDocumentProvider
{
- public async Task<OpenApiDocument> GetOpenApiDocumentAsync( /* .. */ )
+ internal async Task<OpenApiDocument> GetOpenApiDocumentAsync( /* .. */ )
{
}
}
public sealed class OpenApiOptions
{
- public string DocumentName { get; internal set; } = OpenApiConstants.DefaultDocumentName;
+ public string DocumentName
+ {
+ get => field ?? OpenApiConstants.DefaultDocumentName;
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ set;
+ }
}
The changes in this commit, which is a fork of the library, demonstrate the required changes. If this were accepted, the changes can be submitted as a pull request.
Usage Examples
The changes in this commit demonstrate how API Versioning would use these changes and supplant the need to use Reflection.
The necessary changes were privately built and consumed by API Versioning to confirm the proposed changes will indeed work if they are approved and released.
Alternative Designs
There is a potential alternate design that would take the existing design limitations into consideration, but this would require significantly more time and effort. A refactored, alternate design should be tracked in a separate issue and considered for a future major release in .NET 11 or beyond.
Risks
internal types are now public; however, risk is minimized by:
- Only the type and constructor are
public; all other members remain or become internal
- The
[EditorBrowsable(EditorBrowsableState.Never)] can be applied to reduce their accidental discovery
IDocumentProvider is a magical internal interface that is matched only by type name
- This interface has to become
public is it can be mapped by dependency injection
- There may be some other combination or design that limits unintended usage
Background and Motivation
The Microsoft.AspNetCore.OpenAPI library provides a number of useful extensions for generating OpenAPI documents, but it does not work well with ASP.NET API Versioning out-of-the-box. ASP.NET API Versioning has provided a new library to bridge the gaps, but it is forced to use unwieldly Reflection techniques to make things work because all of the necessary extension points in Microsoft.AspNetCore.OpenAPI are
internal.While the Reflection workaround is a short-term fix, it is significantly slower, complicates or breaks the AOT story, and places the extensions at future risk of non-visible changes that may only trigger runtime failures. All of these reflect negatively upon developer and customer experience.
These changes are requested to be part of a .NET 10 servicing release. .NET 11 and beyond should consider an alternate design.
Proposed API
The changes in this commit, which is a fork of the library, demonstrate the required changes. If this were accepted, the changes can be submitted as a pull request.
Usage Examples
The changes in this commit demonstrate how API Versioning would use these changes and supplant the need to use Reflection.
The necessary changes were privately built and consumed by API Versioning to confirm the proposed changes will indeed work if they are approved and released.
Alternative Designs
There is a potential alternate design that would take the existing design limitations into consideration, but this would require significantly more time and effort. A refactored, alternate design should be tracked in a separate issue and considered for a future major release in .NET 11 or beyond.
Risks
internaltypes are nowpublic; however, risk is minimized by:public; all other members remain or becomeinternal[EditorBrowsable(EditorBrowsableState.Never)]can be applied to reduce their accidental discoveryIDocumentProvideris a magicalinternalinterface that is matched only by type namepublicis it can be mapped by dependency injection