From 83c441924d9909595877f62045a65b83b8552adc Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Thu, 1 Dec 2022 18:31:26 -0800 Subject: [PATCH 001/131] Simplify conditional assignment --- .../OData/ODataOpenApiExample/SwaggerDefaultValues.cs | 5 +---- .../OData/SomeODataOpenApiExample/SwaggerDefaultValues.cs | 5 +---- .../WebApi/MinimalOpenApiExample/SwaggerDefaultValues.cs | 5 +---- .../AspNetCore/WebApi/OpenApiExample/SwaggerDefaultValues.cs | 5 +---- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/SwaggerDefaultValues.cs b/examples/AspNetCore/OData/ODataOpenApiExample/SwaggerDefaultValues.cs index 34369a0f..fbf06c2f 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/SwaggerDefaultValues.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/SwaggerDefaultValues.cs @@ -50,10 +50,7 @@ public void Apply( OpenApiOperation operation, OperationFilterContext context ) { var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.Name ); - if ( parameter.Description == null ) - { - parameter.Description = description.ModelMetadata?.Description; - } + parameter.Description ??= description.ModelMetadata?.Description; if ( parameter.Schema.Default == null && description.DefaultValue != null ) { diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/SwaggerDefaultValues.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/SwaggerDefaultValues.cs index 34369a0f..fbf06c2f 100644 --- a/examples/AspNetCore/OData/SomeODataOpenApiExample/SwaggerDefaultValues.cs +++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/SwaggerDefaultValues.cs @@ -50,10 +50,7 @@ public void Apply( OpenApiOperation operation, OperationFilterContext context ) { var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.Name ); - if ( parameter.Description == null ) - { - parameter.Description = description.ModelMetadata?.Description; - } + parameter.Description ??= description.ModelMetadata?.Description; if ( parameter.Schema.Default == null && description.DefaultValue != null ) { diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/SwaggerDefaultValues.cs b/examples/AspNetCore/WebApi/MinimalOpenApiExample/SwaggerDefaultValues.cs index 796cdd9d..0bcae0a8 100644 --- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/SwaggerDefaultValues.cs +++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/SwaggerDefaultValues.cs @@ -47,10 +47,7 @@ public void Apply( OpenApiOperation operation, OperationFilterContext context ) { var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.Name ); - if ( parameter.Description == null ) - { - parameter.Description = description.ModelMetadata?.Description; - } + parameter.Description ??= description.ModelMetadata?.Description; if ( parameter.Schema.Default == null && description.DefaultValue != null && diff --git a/examples/AspNetCore/WebApi/OpenApiExample/SwaggerDefaultValues.cs b/examples/AspNetCore/WebApi/OpenApiExample/SwaggerDefaultValues.cs index 796cdd9d..0bcae0a8 100644 --- a/examples/AspNetCore/WebApi/OpenApiExample/SwaggerDefaultValues.cs +++ b/examples/AspNetCore/WebApi/OpenApiExample/SwaggerDefaultValues.cs @@ -47,10 +47,7 @@ public void Apply( OpenApiOperation operation, OperationFilterContext context ) { var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.Name ); - if ( parameter.Description == null ) - { - parameter.Description = description.ModelMetadata?.Description; - } + parameter.Description ??= description.ModelMetadata?.Description; if ( parameter.Schema.Default == null && description.DefaultValue != null && From d4927a57758adfeed9a83e593146549f98bc2ba1 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Thu, 1 Dec 2022 18:35:06 -0800 Subject: [PATCH 002/131] Optimize IReportApiVersions DI resolution --- .../DefaultApiVersionReporter.cs | 3 ++- .../DependencyResolverExtensions.cs | 2 +- .../DefaultApiVersionReporterTest.cs | 7 +------ .../Builder/EndpointBuilderFinalizer.cs | 5 ++++- .../Routing/ReportApiVersionsDecorator.cs | 19 ++++++++----------- .../DefaultApiVersionReporterTest.cs | 3 +-- .../ReportApiVersionsAttributeTest.cs | 7 +++---- .../Common.Mvc/ReportApiVersionsAttribute.cs | 2 -- .../src/Common/DefaultApiVersionReporter.cs | 19 ++++++++++--------- 9 files changed, 30 insertions(+), 37 deletions(-) diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DefaultApiVersionReporter.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DefaultApiVersionReporter.cs index 357b5978..dc05e257 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DefaultApiVersionReporter.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DefaultApiVersionReporter.cs @@ -12,7 +12,8 @@ public partial class DefaultApiVersionReporter { private static DefaultApiVersionReporter? instance; - internal static IReportApiVersions Instance => instance ??= new(); + internal static IReportApiVersions GetOrCreate( ISunsetPolicyManager sunsetPolicyManager ) => + instance ??= new( sunsetPolicyManager ); private static void AddApiVersionHeader( HttpResponseHeaders headers, string headerName, IReadOnlyList versions ) { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs index 9e8c119a..f8b86bcc 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs @@ -13,7 +13,7 @@ internal static IApiVersionParser GetApiVersionParser( this IDependencyResolver resolver.GetService() ?? ApiVersionParser.Default; internal static IReportApiVersions GetApiVersionReporter( this IDependencyResolver resolver ) => - resolver.GetService() ?? DefaultApiVersionReporter.Instance; + resolver.GetService() ?? DefaultApiVersionReporter.GetOrCreate( resolver.GetSunsetPolicyManager() ); internal static IControllerNameConvention GetControllerNameConvention( this IDependencyResolver resolver ) => resolver.GetService() ?? ControllerNameConvention.Default; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/DefaultApiVersionReporterTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/DefaultApiVersionReporterTest.cs index dc3f2eb5..23e70e9e 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/DefaultApiVersionReporterTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/DefaultApiVersionReporterTest.cs @@ -5,7 +5,6 @@ namespace Asp.Versioning; using Asp.Versioning.Simulators; using System.Web.Http; using System.Web.Http.Controllers; -using System.Web.Http.Dependencies; using static System.Net.HttpStatusCode; public class DefaultApiVersionReporterTest @@ -14,9 +13,8 @@ public class DefaultApiVersionReporterTest public void report_should_add_expected_headers() { // arrange - var reporter = new DefaultApiVersionReporter(); var sunsetDate = DateTimeOffset.Now; - var dependencyResolver = new Mock(); + var reporter = new DefaultApiVersionReporter( new TestSunsetPolicyManager( sunsetDate ) ); var configuration = new HttpConfiguration(); var request = new HttpRequestMessage(); var response = new HttpResponseMessage( OK ) { RequestMessage = request }; @@ -34,9 +32,6 @@ public void report_should_add_expected_headers() deprecatedAdvertisedVersions: Enumerable.Empty() ); var metadata = new ApiVersionMetadata( apiModel, endpointModel, "Test" ); - dependencyResolver.Setup( dr => dr.GetService( typeof( ISunsetPolicyManager ) ) ) - .Returns( new TestSunsetPolicyManager( sunsetDate ) ); - configuration.DependencyResolver = dependencyResolver.Object; request.SetConfiguration( configuration ); request.ApiVersionProperties().RequestedApiVersion = new ApiVersion( 1.0 ); request.Properties["MS_HttpActionDescriptor"] = diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs index e3617913..baf0fd12 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs @@ -52,7 +52,10 @@ private static void Finialize( EndpointBuilder endpointBuilder, ApiVersionSet? v if ( reportApiVersions ) { requestDelegate = EnsureRequestDelegate( requestDelegate, endpointBuilder.RequestDelegate ); - requestDelegate = new ReportApiVersionsDecorator( requestDelegate, metadata ); + + var reporter = services.GetRequiredService(); + + requestDelegate = new ReportApiVersionsDecorator( requestDelegate, reporter, metadata ); endpointBuilder.RequestDelegate = requestDelegate; } diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ReportApiVersionsDecorator.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ReportApiVersionsDecorator.cs index 6e09df43..abd6ee83 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ReportApiVersionsDecorator.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ReportApiVersionsDecorator.cs @@ -3,35 +3,32 @@ namespace Asp.Versioning.Routing; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; internal sealed class ReportApiVersionsDecorator { private readonly RequestDelegate decorated; - private readonly ApiVersionMetadata metadata; + private readonly IReportApiVersions reporter; + private readonly ApiVersionModel model; - public ReportApiVersionsDecorator( RequestDelegate decorated, ApiVersionMetadata metadata ) + public ReportApiVersionsDecorator( RequestDelegate decorated, IReportApiVersions reporter, ApiVersionMetadata metadata ) { this.decorated = decorated; - this.metadata = metadata; + this.reporter = reporter; + model = metadata.Map( reporter.Mapping ); } public static implicit operator RequestDelegate( ReportApiVersionsDecorator decorator ) => ( context ) => { - var reporter = context.RequestServices.GetRequiredService(); - var model = decorator.metadata.Map( reporter.Mapping ); var response = context.Response; - - response.OnStarting( ReportApiVersions, (reporter, response, model) ); - + response.OnStarting( ReportApiVersions, (decorator, response) ); return decorator.decorated( context ); }; private static Task ReportApiVersions( object state ) { - var (reporter, response, model) = ((IReportApiVersions, HttpResponse, ApiVersionModel)) state; - reporter.Report( response, model ); + var (decorator, response) = ((ReportApiVersionsDecorator, HttpResponse)) state; + decorator.reporter.Report( response, decorator.model ); return Task.CompletedTask; } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/DefaultApiVersionReporterTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/DefaultApiVersionReporterTest.cs index 99b8a466..9125c9ae 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/DefaultApiVersionReporterTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/DefaultApiVersionReporterTest.cs @@ -12,8 +12,8 @@ public class DefaultApiVersionReporterTest public void report_should_add_expected_headers() { // arrange - var reporter = new DefaultApiVersionReporter(); var sunsetDate = DateTimeOffset.Now; + var reporter = new DefaultApiVersionReporter( new TestSunsetPolicyManager( sunsetDate ) ); var httpContext = new Mock(); var features = new Mock(); var query = new Mock(); @@ -50,7 +50,6 @@ public void report_should_add_expected_headers() response.SetupGet( r => r.HttpContext ).Returns( () => httpContext.Object ); serviceProvider.Setup( sp => sp.GetService( typeof( IApiVersionParser ) ) ).Returns( ApiVersionParser.Default ); serviceProvider.Setup( sp => sp.GetService( typeof( IApiVersionReader ) ) ).Returns( new QueryStringApiVersionReader() ); - serviceProvider.Setup( sp => sp.GetService( typeof( ISunsetPolicyManager ) ) ).Returns( new TestSunsetPolicyManager( sunsetDate ) ); httpContext.SetupGet( c => c.Features ).Returns( features.Object ); httpContext.SetupGet( c => c.Request ).Returns( request.Object ); httpContext.SetupProperty( c => c.RequestServices, serviceProvider.Object ); diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs index a42ee32c..a79476ef 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs @@ -81,15 +81,14 @@ private static ActionExecutingContext CreateContext( var actionArguments = new Dictionary(); var controller = default( object ); var endpoint = new Endpoint( c => Task.CompletedTask, new( new[] { metadata } ), "Test" ); + var options = Options.Create( new ApiVersioningOptions() ); + var reporter = new DefaultApiVersionReporter( new SunsetPolicyManager( options ) ); endpointFeature.SetupProperty( f => f.Endpoint, endpoint ); versioningFeature.SetupProperty( f => f.RequestedApiVersion, new ApiVersion( 1.0 ) ); features.Set( endpointFeature.Object ); features.Set( versioningFeature.Object ); - serviceProvider.Setup( sp => sp.GetService( typeof( IReportApiVersions ) ) ) - .Returns( new DefaultApiVersionReporter() ); - serviceProvider.Setup( sp => sp.GetService( typeof( ISunsetPolicyManager ) ) ) - .Returns( new SunsetPolicyManager( Options.Create( new ApiVersioningOptions() ) ) ); + serviceProvider.Setup( sp => sp.GetService( typeof( IReportApiVersions ) ) ).Returns( reporter ); response.SetupGet( r => r.Headers ).Returns( headers ); response.SetupGet( r => r.HttpContext ).Returns( () => httpContext.Object ); response.Setup( r => r.OnStarting( It.IsAny>(), It.IsAny() ) ) diff --git a/src/Common/src/Common.Mvc/ReportApiVersionsAttribute.cs b/src/Common/src/Common.Mvc/ReportApiVersionsAttribute.cs index 96efacdf..2093b4c5 100644 --- a/src/Common/src/Common.Mvc/ReportApiVersionsAttribute.cs +++ b/src/Common/src/Common.Mvc/ReportApiVersionsAttribute.cs @@ -27,7 +27,5 @@ public ReportApiVersionsAttribute() { } /// Initializes a new instance of the class. /// /// The object used to report API versions. -#pragma warning disable CA1019 // Define accessors for attribute arguments public ReportApiVersionsAttribute( IReportApiVersions reportApiVersions ) => this.reportApiVersions = reportApiVersions; -#pragma warning restore CA1019 // Define accessors for attribute arguments } \ No newline at end of file diff --git a/src/Common/src/Common/DefaultApiVersionReporter.cs b/src/Common/src/Common/DefaultApiVersionReporter.cs index 30d0e67d..02237016 100644 --- a/src/Common/src/Common/DefaultApiVersionReporter.cs +++ b/src/Common/src/Common/DefaultApiVersionReporter.cs @@ -7,7 +7,6 @@ namespace Asp.Versioning; using HttpResponse = System.Net.Http.HttpResponseMessage; #else using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; #endif using static Asp.Versioning.ApiVersionMapping; @@ -23,12 +22,14 @@ public sealed partial class DefaultApiVersionReporter : IReportApiVersions private const string ApiDeprecatedVersions = "api-deprecated-versions"; private const string Sunset = nameof( Sunset ); private const string Link = nameof( Link ); + private readonly ISunsetPolicyManager sunsetPolicyManager; private readonly string apiSupportedVersionsName; private readonly string apiDeprecatedVersionsName; /// /// Initializes a new instance of the class. /// + /// The manager used to resolve sunset policies. /// The HTTP header name used for supported API versions. /// The default value is "api-supported-versions". /// THe HTTP header name used for deprecated API versions. @@ -36,11 +37,12 @@ public sealed partial class DefaultApiVersionReporter : IReportApiVersions /// One or more of API versioning mappings. The default value is /// and . public DefaultApiVersionReporter( + ISunsetPolicyManager sunsetPolicyManager, string supportedHeaderName = ApiSupportedVersions, string deprecatedHeaderName = ApiDeprecatedVersions, ApiVersionMapping mapping = Explicit | Implicit ) { - Mapping = mapping; + this.sunsetPolicyManager = sunsetPolicyManager ?? throw new ArgumentNullException( nameof( sunsetPolicyManager ) ); if ( string.IsNullOrEmpty( apiSupportedVersionsName = supportedHeaderName ) ) { @@ -51,6 +53,8 @@ public DefaultApiVersionReporter( { throw new ArgumentNullException( nameof( deprecatedHeaderName ) ); } + + Mapping = mapping; } /// @@ -86,8 +90,6 @@ public void Report( HttpResponse response, ApiVersionModel apiVersionModel ) return; } - var name = metadata.Name; - var policyManager = request.GetConfiguration().DependencyResolver.GetSunsetPolicyManager(); var version = request.GetRequestedApiVersion(); #else var context = response.HttpContext; @@ -97,14 +99,13 @@ public void Report( HttpResponse response, ApiVersionModel apiVersionModel ) return; } - var name = metadata.Name; - var policyManager = context.RequestServices.GetRequiredService(); var version = context.GetRequestedApiVersion(); #endif + var name = metadata.Name; - if ( policyManager.TryGetPolicy( name, version, out var policy ) || - ( !string.IsNullOrEmpty( name ) && policyManager.TryGetPolicy( name, out policy ) ) || - ( version != null && policyManager.TryGetPolicy( version, out policy ) ) ) + if ( sunsetPolicyManager.TryGetPolicy( name, version, out var policy ) || + ( !string.IsNullOrEmpty( name ) && sunsetPolicyManager.TryGetPolicy( name, out policy ) ) || + ( version != null && sunsetPolicyManager.TryGetPolicy( version, out policy ) ) ) { response.WriteSunsetPolicy( policy ); } From f1e56be63e937db1e595bb36aff63ee14ffc5f31 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Thu, 1 Dec 2022 18:35:45 -0800 Subject: [PATCH 003/131] Optimize DI resolution --- .../ApiVersionDescriptionProviderFactory.cs | 24 +++++++++++-------- .../IApiVersioningBuilderExtensions.cs | 8 ++++++- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs index 7027d66e..cd6e09c4 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs @@ -10,26 +10,30 @@ namespace Microsoft.AspNetCore.Builder; internal sealed class ApiVersionDescriptionProviderFactory : IApiVersionDescriptionProviderFactory { - private readonly IServiceProvider serviceProvider; + private readonly ISunsetPolicyManager sunsetPolicyManager; + private readonly IApiVersionMetadataCollationProvider[] providers; + private readonly IOptions options; private readonly Func, ISunsetPolicyManager, IOptions, IApiVersionDescriptionProvider> activator; public ApiVersionDescriptionProviderFactory( - IServiceProvider serviceProvider, - Func, ISunsetPolicyManager, IOptions, IApiVersionDescriptionProvider> activator ) + Func, ISunsetPolicyManager, IOptions, IApiVersionDescriptionProvider> activator, + ISunsetPolicyManager sunsetPolicyManager, + IEnumerable providers, + IOptions options ) { - this.serviceProvider = serviceProvider; this.activator = activator; + this.sunsetPolicyManager = sunsetPolicyManager; + this.providers = providers.ToArray(); + this.options = options; } public IApiVersionDescriptionProvider Create( EndpointDataSource endpointDataSource ) { - var providers = serviceProvider.GetServices().ToList(); + var collators = new List( capacity: providers.Length + 1 ); - providers.Insert( 0, new EndpointApiVersionMetadataCollationProvider( endpointDataSource ) ); + collators.Add( new EndpointApiVersionMetadataCollationProvider( endpointDataSource ) ); + collators.AddRange( providers ); - var sunsetPolicyManager = serviceProvider.GetRequiredService(); - var options = serviceProvider.GetRequiredService>(); - - return activator( providers, sunsetPolicyManager, options ); + return activator( collators, sunsetPolicyManager, options ); } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs index 6f5659bc..f806121d 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -77,10 +77,16 @@ private static void AddApiExplorerServices( IApiVersioningBuilder builder ) private static IApiVersionDescriptionProviderFactory ResolveApiVersionDescriptionProviderFactory( IServiceProvider serviceProvider ) { + var sunsetPolicyManager = serviceProvider.GetRequiredService(); + var providers = serviceProvider.GetServices(); var options = serviceProvider.GetRequiredService>(); var mightUseCustomGroups = options.Value.FormatGroupName is not null; - return new ApiVersionDescriptionProviderFactory( serviceProvider, mightUseCustomGroups ? NewGroupedProvider : NewDefaultProvider ); + return new ApiVersionDescriptionProviderFactory( + mightUseCustomGroups ? NewGroupedProvider : NewDefaultProvider, + sunsetPolicyManager, + providers, + options ); static IApiVersionDescriptionProvider NewDefaultProvider( IEnumerable providers, From 0d1b9fa3ae13d5f51cc24546d55b583448212579 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Thu, 1 Dec 2022 18:35:50 -0800 Subject: [PATCH 004/131] Code clean up --- .../OData/ODataOpenApiExample/Program.cs | 2 +- .../V3/SuppliersController.cs | 3 +- .../MinimalOpenApiExample.csproj | 1 + .../WebApi/MinimalOpenApiExample/Program.cs | 1 + .../ApiVersioningFeature.cs | 31 ++++++++++++------- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs index 1fd344e5..2a03865c 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs @@ -16,7 +16,7 @@ .AddOData( options => { - options.Count().Select().OrderBy(); + options.Count().Select().OrderBy().SetMaxTop( 3 ); options.RouteOptions.EnableKeyInParenthesis = false; options.RouteOptions.EnableNonParenthesisForEmptyParameterFunction = true; options.RouteOptions.EnablePropertyNameCaseInsensitive = true; diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs b/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs index 401e1a59..60f13940 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs @@ -29,7 +29,8 @@ public class SuppliersController : ODataController /// All available suppliers. /// Suppliers successfully retrieved. [HttpGet] - [EnableQuery] + //[EnableQuery] + [EnableQuery( MaxTop = 2 )] [Produces( "application/json" )] [ProducesResponseType( typeof( ODataValue> ), Status200OK )] public IQueryable Get() => suppliers; diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/MinimalOpenApiExample.csproj b/examples/AspNetCore/WebApi/MinimalOpenApiExample/MinimalOpenApiExample.csproj index 47611fa4..f0039464 100644 --- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/MinimalOpenApiExample.csproj +++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/MinimalOpenApiExample.csproj @@ -5,6 +5,7 @@ + diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs b/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs index ec48818e..e4821852 100644 --- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs +++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs @@ -56,6 +56,7 @@ .HasApiVersion( 1.0 ); ordersV1.MapGet( "/{id:int}", ( int id ) => new OrderV1() { Id = id, Customer = "John Doe" } ) + .WithOpenApi() .Produces() .Produces( 404 ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs index 5c515096..8dd49c7d 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs @@ -5,6 +5,7 @@ namespace Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using System.Globalization; +using System.Runtime.CompilerServices; /// /// Represents the API versioning feature. @@ -33,11 +34,10 @@ public IReadOnlyList RawRequestedApiVersions { if ( rawApiVersions is null ) { - var reader = - context.RequestServices.GetService() ?? - ApiVersionReader.Combine( - new QueryStringApiVersionReader(), - new UrlSegmentApiVersionReader() ); + var reader = context.RequestServices.GetService() + ?? ApiVersionReader.Combine( + new QueryStringApiVersionReader(), + new UrlSegmentApiVersionReader() ); rawApiVersions = reader.Read( context.Request ); } @@ -58,11 +58,7 @@ public string? RawRequestedApiVersion { 0 => default, 1 => values[0], -#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations; existing behavior via IApiVersionReader.Read - _ => throw new AmbiguousApiVersionException( - string.Format( CultureInfo.CurrentCulture, CommonSR.MultipleDifferentApiVersionsRequested, string.Join( ", ", values ) ), - values ), -#pragma warning restore CA1065 + _ => throw NewAmbiguousApiVersionException( values ), }; } set @@ -88,7 +84,8 @@ public ApiVersion? RequestedApiVersion return apiVersion; } - var parser = context.RequestServices.GetRequiredService(); + var parser = context.RequestServices.GetService() + ?? ApiVersionParser.Default; try { @@ -105,10 +102,20 @@ public ApiVersion? RequestedApiVersion { apiVersion = value; - if ( apiVersion is not null && ( rawApiVersions is null || rawApiVersions.Count == 0 ) ) + if ( apiVersion is not null && + ( rawApiVersions is null || rawApiVersions.Count == 0 ) ) { rawApiVersions = new[] { apiVersion.ToString() }; } } } + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static AmbiguousApiVersionException NewAmbiguousApiVersionException( IReadOnlyList values ) => + new( + string.Format( + CultureInfo.CurrentCulture, + CommonSR.MultipleDifferentApiVersionsRequested, + string.Join( ", ", values.ToArray(), 0, values.Count ) ), + values ); } \ No newline at end of file From 0e0092fab4e5b6d9099fb0c2b4b8a9fe40c0054e Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Thu, 1 Dec 2022 19:00:31 -0800 Subject: [PATCH 005/131] Revert "Code clean up" This reverts commit 0d1b9fa3ae13d5f51cc24546d55b583448212579. --- .../OData/ODataOpenApiExample/Program.cs | 2 +- .../V3/SuppliersController.cs | 3 +- .../MinimalOpenApiExample.csproj | 1 - .../WebApi/MinimalOpenApiExample/Program.cs | 1 - .../ApiVersioningFeature.cs | 31 +++++++------------ 5 files changed, 14 insertions(+), 24 deletions(-) diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs index 2a03865c..1fd344e5 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs @@ -16,7 +16,7 @@ .AddOData( options => { - options.Count().Select().OrderBy().SetMaxTop( 3 ); + options.Count().Select().OrderBy(); options.RouteOptions.EnableKeyInParenthesis = false; options.RouteOptions.EnableNonParenthesisForEmptyParameterFunction = true; options.RouteOptions.EnablePropertyNameCaseInsensitive = true; diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs b/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs index 60f13940..401e1a59 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs @@ -29,8 +29,7 @@ public class SuppliersController : ODataController /// All available suppliers. /// Suppliers successfully retrieved. [HttpGet] - //[EnableQuery] - [EnableQuery( MaxTop = 2 )] + [EnableQuery] [Produces( "application/json" )] [ProducesResponseType( typeof( ODataValue> ), Status200OK )] public IQueryable Get() => suppliers; diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/MinimalOpenApiExample.csproj b/examples/AspNetCore/WebApi/MinimalOpenApiExample/MinimalOpenApiExample.csproj index f0039464..47611fa4 100644 --- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/MinimalOpenApiExample.csproj +++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/MinimalOpenApiExample.csproj @@ -5,7 +5,6 @@ - diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs b/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs index e4821852..ec48818e 100644 --- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs +++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs @@ -56,7 +56,6 @@ .HasApiVersion( 1.0 ); ordersV1.MapGet( "/{id:int}", ( int id ) => new OrderV1() { Id = id, Customer = "John Doe" } ) - .WithOpenApi() .Produces() .Produces( 404 ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs index 8dd49c7d..5c515096 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs @@ -5,7 +5,6 @@ namespace Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using System.Globalization; -using System.Runtime.CompilerServices; /// /// Represents the API versioning feature. @@ -34,10 +33,11 @@ public IReadOnlyList RawRequestedApiVersions { if ( rawApiVersions is null ) { - var reader = context.RequestServices.GetService() - ?? ApiVersionReader.Combine( - new QueryStringApiVersionReader(), - new UrlSegmentApiVersionReader() ); + var reader = + context.RequestServices.GetService() ?? + ApiVersionReader.Combine( + new QueryStringApiVersionReader(), + new UrlSegmentApiVersionReader() ); rawApiVersions = reader.Read( context.Request ); } @@ -58,7 +58,11 @@ public string? RawRequestedApiVersion { 0 => default, 1 => values[0], - _ => throw NewAmbiguousApiVersionException( values ), +#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations; existing behavior via IApiVersionReader.Read + _ => throw new AmbiguousApiVersionException( + string.Format( CultureInfo.CurrentCulture, CommonSR.MultipleDifferentApiVersionsRequested, string.Join( ", ", values ) ), + values ), +#pragma warning restore CA1065 }; } set @@ -84,8 +88,7 @@ public ApiVersion? RequestedApiVersion return apiVersion; } - var parser = context.RequestServices.GetService() - ?? ApiVersionParser.Default; + var parser = context.RequestServices.GetRequiredService(); try { @@ -102,20 +105,10 @@ public ApiVersion? RequestedApiVersion { apiVersion = value; - if ( apiVersion is not null && - ( rawApiVersions is null || rawApiVersions.Count == 0 ) ) + if ( apiVersion is not null && ( rawApiVersions is null || rawApiVersions.Count == 0 ) ) { rawApiVersions = new[] { apiVersion.ToString() }; } } } - - [MethodImpl( MethodImplOptions.AggressiveInlining )] - private static AmbiguousApiVersionException NewAmbiguousApiVersionException( IReadOnlyList values ) => - new( - string.Format( - CultureInfo.CurrentCulture, - CommonSR.MultipleDifferentApiVersionsRequested, - string.Join( ", ", values.ToArray(), 0, values.Count ) ), - values ); } \ No newline at end of file From 88d2cd4ee416f20171200f2009621e3bf85d3313 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Thu, 1 Dec 2022 19:02:06 -0800 Subject: [PATCH 006/131] Code clean up --- .../ApiVersioningFeature.cs | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs index 5c515096..8dd49c7d 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs @@ -5,6 +5,7 @@ namespace Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using System.Globalization; +using System.Runtime.CompilerServices; /// /// Represents the API versioning feature. @@ -33,11 +34,10 @@ public IReadOnlyList RawRequestedApiVersions { if ( rawApiVersions is null ) { - var reader = - context.RequestServices.GetService() ?? - ApiVersionReader.Combine( - new QueryStringApiVersionReader(), - new UrlSegmentApiVersionReader() ); + var reader = context.RequestServices.GetService() + ?? ApiVersionReader.Combine( + new QueryStringApiVersionReader(), + new UrlSegmentApiVersionReader() ); rawApiVersions = reader.Read( context.Request ); } @@ -58,11 +58,7 @@ public string? RawRequestedApiVersion { 0 => default, 1 => values[0], -#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations; existing behavior via IApiVersionReader.Read - _ => throw new AmbiguousApiVersionException( - string.Format( CultureInfo.CurrentCulture, CommonSR.MultipleDifferentApiVersionsRequested, string.Join( ", ", values ) ), - values ), -#pragma warning restore CA1065 + _ => throw NewAmbiguousApiVersionException( values ), }; } set @@ -88,7 +84,8 @@ public ApiVersion? RequestedApiVersion return apiVersion; } - var parser = context.RequestServices.GetRequiredService(); + var parser = context.RequestServices.GetService() + ?? ApiVersionParser.Default; try { @@ -105,10 +102,20 @@ public ApiVersion? RequestedApiVersion { apiVersion = value; - if ( apiVersion is not null && ( rawApiVersions is null || rawApiVersions.Count == 0 ) ) + if ( apiVersion is not null && + ( rawApiVersions is null || rawApiVersions.Count == 0 ) ) { rawApiVersions = new[] { apiVersion.ToString() }; } } } + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static AmbiguousApiVersionException NewAmbiguousApiVersionException( IReadOnlyList values ) => + new( + string.Format( + CultureInfo.CurrentCulture, + CommonSR.MultipleDifferentApiVersionsRequested, + string.Join( ", ", values.ToArray(), 0, values.Count ) ), + values ); } \ No newline at end of file From e40495fc83992f6844af42fb5d6339e6cf646c05 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 5 Dec 2022 09:47:19 -0800 Subject: [PATCH 007/131] Refactor to make registering model configurations public --- .../IApiVersioningBuilderExtensions.cs | 42 +--------- .../IServiceCollectionExtensions.cs | 76 +++++++++++++++++++ ...ODataMultiModelApplicationModelProvider.cs | 1 - 3 files changed, 79 insertions(+), 40 deletions(-) create mode 100644 src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IServiceCollectionExtensions.cs diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs index 3af0b775..79bba8dd 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -9,7 +9,6 @@ namespace Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc.ApplicationModels; -using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.OData; using Microsoft.AspNetCore.OData.Routing.Template; using Microsoft.AspNetCore.Routing; @@ -66,8 +65,7 @@ private static void AddServices( IServiceCollection services ) services.TryRemoveODataService( typeof( IApplicationModelProvider ), ODataRoutingApplicationModelProviderType ); var partManager = services.GetOrCreateApplicationPartManager(); - - ConfigureDefaultFeatureProviders( partManager ); + var configured = partManager.ConfigureDefaultFeatureProviders(); services.AddHttpContextAccessor(); services.TryAddSingleton(); @@ -84,24 +82,11 @@ private static void AddServices( IServiceCollection services ) services.TryAddEnumerable( Singleton() ); services.TryAddEnumerable( Transient() ); services.TryAddEnumerable( Transient() ); - services.AddModelConfigurationsAsServices( partManager ); - } - - private static T GetService( this IServiceCollection services ) => - (T) services.LastOrDefault( d => d.ServiceType == typeof( T ) )?.ImplementationInstance!; - - private static ApplicationPartManager GetOrCreateApplicationPartManager( this IServiceCollection services ) - { - var partManager = services.GetService(); - if ( partManager == null ) + if ( configured ) { - partManager = new ApplicationPartManager(); - services.TryAddSingleton( partManager ); + services.AddModelConfigurationsAsServices( partManager ); } - - partManager.ApplicationParts.Add( new AssemblyPart( typeof( ODataApiVersioningOptions ).Assembly ) ); - return partManager; } [MethodImpl( MethodImplOptions.AggressiveInlining )] @@ -148,27 +133,6 @@ private static void TryReplaceODataService( } } - private static void AddModelConfigurationsAsServices( this IServiceCollection services, ApplicationPartManager partManager ) - { - var feature = new ModelConfigurationFeature(); - var modelConfigurationType = typeof( IModelConfiguration ); - - partManager.PopulateFeature( feature ); - - foreach ( var modelConfiguration in feature.ModelConfigurations ) - { - services.TryAddEnumerable( Transient( modelConfigurationType, modelConfiguration ) ); - } - } - - private static void ConfigureDefaultFeatureProviders( ApplicationPartManager partManager ) - { - if ( !partManager.FeatureProviders.OfType().Any() ) - { - partManager.FeatureProviders.Add( new ModelConfigurationFeatureProvider() ); - } - } - private static object CreateInstance( this IServiceProvider services, ServiceDescriptor descriptor ) { if ( descriptor.ImplementationInstance != null ) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IServiceCollectionExtensions.cs new file mode 100644 index 00000000..cb86af84 --- /dev/null +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IServiceCollectionExtensions.cs @@ -0,0 +1,76 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Microsoft.Extensions.DependencyInjection; + +using Asp.Versioning.OData; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Microsoft.Extensions.DependencyInjection.Extensions; +using System.Runtime.CompilerServices; +using static Microsoft.Extensions.DependencyInjection.ServiceDescriptor; + +/// +/// Provides extension methods for . +/// +public static class IServiceCollectionExtensions +{ + [MethodImpl( MethodImplOptions.AggressiveInlining )] + internal static T GetService( this IServiceCollection services ) => + (T) services.LastOrDefault( d => d.ServiceType == typeof( T ) )?.ImplementationInstance!; + + internal static ApplicationPartManager GetOrCreateApplicationPartManager( this IServiceCollection services ) + { + var partManager = services.GetService(); + + if ( partManager == null ) + { + partManager = new ApplicationPartManager(); + services.TryAddSingleton( partManager ); + } + + partManager.ApplicationParts.Add( new AssemblyPart( typeof( ODataApiVersioningOptions ).Assembly ) ); + return partManager; + } + + internal static void AddModelConfigurationsAsServices( this IServiceCollection services, ApplicationPartManager partManager ) + { + var feature = new ModelConfigurationFeature(); + var modelConfigurationType = typeof( IModelConfiguration ); + + partManager.PopulateFeature( feature ); + + foreach ( var modelConfiguration in feature.ModelConfigurations ) + { + services.TryAddEnumerable( Transient( modelConfigurationType, modelConfiguration ) ); + } + } + + internal static bool ConfigureDefaultFeatureProviders( this ApplicationPartManager partManager ) + { + if ( partManager.FeatureProviders.OfType().Any() ) + { + return false; + } + + partManager.FeatureProviders.Add( new ModelConfigurationFeatureProvider() ); + return true; + } + + /// + /// Registers discovered model configurations as services in the . + /// + /// The extended . + public static void AddModelConfigurationsAsServices( this IServiceCollection services ) + { + if ( services == null ) + { + throw new ArgumentNullException( nameof( services ) ); + } + + var partManager = services.GetOrCreateApplicationPartManager(); + + if ( ConfigureDefaultFeatureProviders( partManager ) ) + { + services.AddModelConfigurationsAsServices( partManager ); + } + } +} \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs index c8fc3fe0..f0189cb0 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs @@ -4,7 +4,6 @@ namespace Asp.Versioning.OData; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.OData; using Microsoft.AspNetCore.OData.Routing.Conventions; From 756f21bdc9cf9b792597e69d1759262046ffe330 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 5 Dec 2022 19:22:40 -0800 Subject: [PATCH 008/131] Fix NETFX reference assemblies --- asp.sln | 3 --- build/test.targets | 2 +- .../Asp.Versioning.Abstractions.Tests.csproj | 2 +- src/AspNet/Directory.Build.targets | 10 ---------- src/Directory.Build.targets | 4 ++++ 5 files changed, 6 insertions(+), 15 deletions(-) delete mode 100644 src/AspNet/Directory.Build.targets diff --git a/asp.sln b/asp.sln index 57868d94..0df9e4ed 100644 --- a/asp.sln +++ b/asp.sln @@ -26,9 +26,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Abstractions", "Abstractions", "{7B0FA6C2-47BA-4C34-90E0-B75DF44F2124}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNet", "AspNet", "{34A0373F-12C9-44A8-9A1C-5EEE7218C877}" - ProjectSection(SolutionItems) = preProject - src\AspNet\Directory.Build.targets = src\AspNet\Directory.Build.targets - EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNetCore", "AspNetCore", "{EBC9F217-E8BC-4DCE-9C67-F22150959EAF}" EndProject diff --git a/build/test.targets b/build/test.targets index 286e53f6..8616ce63 100644 --- a/build/test.targets +++ b/build/test.targets @@ -3,7 +3,7 @@ 6.8.0 - 4.18.2 + 4.18.3 2.4.5 diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/Asp.Versioning.Abstractions.Tests.csproj b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/Asp.Versioning.Abstractions.Tests.csproj index a7c04d59..668e879c 100644 --- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/Asp.Versioning.Abstractions.Tests.csproj +++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/Asp.Versioning.Abstractions.Tests.csproj @@ -1,6 +1,6 @@  - net7.0;net472;net452 + net7.0;net452;net472 Asp.Versioning diff --git a/src/AspNet/Directory.Build.targets b/src/AspNet/Directory.Build.targets deleted file mode 100644 index 6a993f01..00000000 --- a/src/AspNet/Directory.Build.targets +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 15a2e2dc..468f1f01 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -21,6 +21,10 @@ + + + + From 71d4ab3fb495f1424bd9d5350b453562753d2b38 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 5 Dec 2022 19:26:47 -0800 Subject: [PATCH 009/131] Add ad hoc annotation and resolve annotations via extension method --- .../OData/EdmModelSelector.cs | 6 +- .../OData/VersionedODataModelBuilderTest.cs | 2 +- .../ODataApiDescriptionProvider.cs | 2 +- .../ODataQueryOptionDescriptionContext.cs | 2 +- ...ODataMultiModelApplicationModelProvider.cs | 6 +- .../OData/VersionedODataTemplateTranslator.cs | 2 +- .../VersionedAttributeRoutingConvention.cs | 2 +- .../OData/VersionedODataModelBuilderTest.cs | 2 +- .../OData/DefaultModelTypeBuilder.cs | 55 ++++++++++++++----- .../OData/TypeExtensions.cs | 33 +++++------ .../OData/TypeSubstitutionContext.cs | 2 +- .../IEdmModelExtensions.cs | 28 ++++++++++ .../src/Common.OData/OData/AdHocAnnotation.cs | 15 +++++ 13 files changed, 113 insertions(+), 44 deletions(-) create mode 100644 src/Common/src/Common.OData/Microsoft.OData.Edm/IEdmModelExtensions.cs create mode 100644 src/Common/src/Common.OData/OData/AdHocAnnotation.cs diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs index cba6623f..f8f7e17b 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs @@ -130,16 +130,12 @@ public EdmModelSelector( IEnumerable models, IApiVersionSelector apiV private static void AddVersionFromModel( IEdmModel model, IList versions, IDictionary collection ) { - var annotation = model.GetAnnotationValue( model ); - - if ( annotation == null ) + if ( model.GetApiVersion() is not ApiVersion version ) { var message = string.Format( CultureInfo.CurrentCulture, SR.MissingAnnotation, typeof( ApiVersionAnnotation ).Name ); throw new ArgumentException( message ); } - var version = annotation.ApiVersion; - collection.Add( version, model ); versions.Add( version ); } diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/OData/VersionedODataModelBuilderTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/OData/VersionedODataModelBuilderTest.cs index 7bd0781e..31845519 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/OData/VersionedODataModelBuilderTest.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/OData/VersionedODataModelBuilderTest.cs @@ -34,7 +34,7 @@ public void get_edm_models_should_return_expected_results() var model = builder.GetEdmModels().Single(); // assert - model.GetAnnotationValue( model ).ApiVersion.Should().Be( apiVersion ); + model.GetApiVersion().Should().Be( apiVersion ); modelCreated.Verify( f => f( It.IsAny(), model ), Times.Once() ); } diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs index 0084cd01..06611d5b 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs @@ -221,7 +221,7 @@ private static bool TryMatchModelVersion( for ( var i = 0; i < items.Count; i++ ) { var item = items[i]; - var otherApiVersion = item.Model.GetAnnotationValue( item.Model )?.ApiVersion; + var otherApiVersion = item.Model.GetApiVersion(); if ( apiVersion.Equals( otherApiVersion ) ) { diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs index aef2fdbf..ae3ad97b 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs @@ -34,7 +34,7 @@ public partial class ODataQueryOptionDescriptionContext foreach ( var item in items ) { var model = item.Model; - var otherVersion = model.GetAnnotationValue( model )?.ApiVersion; + var otherVersion = model.GetApiVersion(); if ( version.Equals( otherVersion ) ) { diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs index f0189cb0..7fb591e2 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs @@ -152,7 +152,11 @@ private void AddRouteComponents( for ( var i = 0; i < models.Count; i++ ) { var model = models[i]; - var version = model.GetAnnotationValue( model ).ApiVersion; + + if ( model.GetApiVersion() is not ApiVersion version ) + { + continue; + } if ( !mappings.TryGetValue( version, out var options ) ) { diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs index 5d58b2b5..8f29de7b 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs @@ -39,7 +39,7 @@ public sealed class VersionedODataTemplateTranslator : IODataTemplateTranslator else { var model = context.Model; - var otherApiVersion = model.GetAnnotationValue( model )?.ApiVersion; + var otherApiVersion = model.GetApiVersion(); // HACK: a version-neutral endpoint can fail to match here because odata tries to match the // first endpoint metadata when there could be multiple. such an endpoint is expected to be diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs index 5dd11990..8ea4c5a2 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs @@ -55,7 +55,7 @@ public override bool AppliesToAction( ODataControllerActionContext context ) return false; } - var apiVersion = edm.GetAnnotationValue( edm )?.ApiVersion; + var apiVersion = edm.GetApiVersion(); if ( apiVersion == null || !metadata.IsMappedTo( apiVersion ) ) { diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/VersionedODataModelBuilderTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/VersionedODataModelBuilderTest.cs index 29be43a2..b40917a9 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/VersionedODataModelBuilderTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/VersionedODataModelBuilderTest.cs @@ -29,7 +29,7 @@ public void get_edm_models_should_return_expected_results() var model = builder.GetEdmModels().Single(); // assert - model.GetAnnotationValue( model ).ApiVersion.Should().Be( apiVersion ); + model.GetApiVersion().Should().Be( apiVersion ); modelCreated.Verify( f => f( It.IsAny(), model ), Once() ); } diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs index 461d6cf0..62df945a 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs @@ -25,10 +25,19 @@ namespace Asp.Versioning.OData; public sealed class DefaultModelTypeBuilder : IModelTypeBuilder { private static Type? ienumerableOfT; + private readonly bool adHoc; + private DefaultModelTypeBuilder? adHocBuilder; private ConcurrentDictionary? modules; private ConcurrentDictionary>? generatedEdmTypesPerVersion; private ConcurrentDictionary>? generatedActionParamsPerVersion; + private DefaultModelTypeBuilder( bool adHoc ) => this.adHoc = adHoc; + + /// + /// Initializes a new instance of the class. + /// + public DefaultModelTypeBuilder() { } + /// public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredType, Type clrType, ApiVersion apiVersion ) { @@ -37,6 +46,12 @@ public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredTyp throw new ArgumentNullException( nameof( model ) ); } + if ( !adHoc && model.IsAdHoc() ) + { + adHocBuilder ??= new( adHoc: true ); + return adHocBuilder.NewStructuredType( model, structuredType, clrType, apiVersion ); + } + if ( structuredType == null ) { throw new ArgumentNullException( nameof( structuredType ) ); @@ -54,10 +69,9 @@ public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredTyp generatedEdmTypesPerVersion ??= new(); - var typeKey = new EdmTypeKey( structuredType, apiVersion ); var edmTypes = generatedEdmTypesPerVersion.GetOrAdd( apiVersion, key => GenerateTypesForEdmModel( model, key ) ); - return edmTypes[typeKey]; + return edmTypes[new( structuredType, apiVersion )]; } /// @@ -68,6 +82,12 @@ public Type NewActionParameters( IEdmModel model, IEdmAction action, string cont throw new ArgumentNullException( nameof( model ) ); } + if ( !adHoc && model.IsAdHoc() ) + { + adHocBuilder ??= new( adHoc: true ); + return adHocBuilder.NewActionParameters( model, action, controllerName, apiVersion ); + } + if ( action == null ) { throw new ArgumentNullException( nameof( action ) ); @@ -85,7 +105,7 @@ public Type NewActionParameters( IEdmModel model, IEdmAction action, string cont generatedActionParamsPerVersion ??= new(); - var paramTypes = generatedActionParamsPerVersion.GetOrAdd( apiVersion, _ => new ConcurrentDictionary() ); + var paramTypes = generatedActionParamsPerVersion.GetOrAdd( apiVersion, _ => new() ); var fullTypeName = $"{controllerName}.{action.Namespace}.{controllerName}{action.Name}Parameters"; var key = new EdmTypeKey( fullTypeName, apiVersion ); var type = paramTypes.GetOrAdd( key, _ => @@ -322,8 +342,7 @@ private static Type ResolveType( } [MethodImpl( MethodImplOptions.AggressiveInlining )] - private static Type MakeEnumerable( Type itemType ) => - ( ienumerableOfT ??= typeof( IEnumerable<> ) ).MakeGenericType( itemType ); + private static Type MakeEnumerable( Type itemType ) => ( ienumerableOfT ??= typeof( IEnumerable<> ) ).MakeGenericType( itemType ); [MethodImpl( MethodImplOptions.AggressiveInlining )] private static Type CreateTypeFromSignature( ModuleBuilder moduleBuilder, ClassSignature @class ) => @@ -389,7 +408,11 @@ private static IDictionary ResolveDependencies( BuilderContext return edmTypes; } - private static PropertyBuilder AddProperty( TypeBuilder addTo, Type shouldBeAdded, string name, IReadOnlyList customAttributes ) + private static PropertyBuilder AddProperty( + TypeBuilder addTo, + Type shouldBeAdded, + string name, + IReadOnlyList customAttributes ) { const MethodAttributes propertyMethodAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; var field = addTo.DefineField( "_" + name, shouldBeAdded, FieldAttributes.Private ); @@ -418,7 +441,7 @@ private static PropertyBuilder AddProperty( TypeBuilder addTo, Type shouldBeAdde return propertyBuilder; } - private static AssemblyName NewAssemblyName(ApiVersion apiVersion) + private static AssemblyName NewAssemblyName( ApiVersion apiVersion, bool adHoc ) { // this is not strictly necessary, but it makes debugging a bit easier as each // assembly-qualified type name provides visibility as to which api version a @@ -455,20 +478,26 @@ private static AssemblyName NewAssemblyName(ApiVersion apiVersion) } name.Insert( 0, 'V' ) - .Append( NewGuid().ToString( "n", InvariantCulture ) ) - .Append( ".DynamicModels" ); + .Append( NewGuid().ToString( "n", InvariantCulture ) ); + + if ( adHoc ) + { + name.Append( ".AdHoc" ); + } + + name.Append( ".DynamicModels" ); return new( name.ToString() ); } [MethodImpl( MethodImplOptions.AggressiveInlining )] - private static ModuleBuilder CreateModuleForApiVersion( ApiVersion apiVersion ) + private ModuleBuilder CreateModuleForApiVersion( ApiVersion apiVersion ) { - var name = NewAssemblyName( apiVersion ); + var assemblyName = NewAssemblyName( apiVersion, adHoc ); #if NETFRAMEWORK - var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( name, Run ); + var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( assemblyName, Run ); #else - var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly( name, Run ); + var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly( assemblyName, Run ); #endif return assemblyBuilder.DefineDynamicModule( "" ); } diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs b/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs index fb13bbe8..bca4a961 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs @@ -3,11 +3,13 @@ namespace Asp.Versioning.OData; #if NETFRAMEWORK +using Microsoft.OData.Edm; using System.Net.Http; using System.Web.Http; #else using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OData.Results; +using Microsoft.OData.Edm; #endif using System.Reflection; using System.Reflection.Emit; @@ -54,12 +56,11 @@ public static Type SubstituteIfNecessary( this Type type, TypeSubstitutionContex var openTypes = new Stack(); var apiVersion = context.ApiVersion; var resolver = new StructuredTypeResolver( context.Model ); + IEdmStructuredType? structuredType; if ( IsSubstitutableGeneric( type, openTypes, out var innerType ) ) { - var structuredType = resolver.GetStructuredType( innerType! ); - - if ( structuredType == null ) + if ( ( structuredType = resolver.GetStructuredType( innerType! ) ) == null ) { return type; } @@ -74,14 +75,9 @@ public static Type SubstituteIfNecessary( this Type type, TypeSubstitutionContex return CloseGeneric( openTypes, newType ); } - if ( CanBeSubstituted( type ) ) + if ( CanBeSubstituted( type ) && ( structuredType = resolver.GetStructuredType( type ) ) != null ) { - var structuredType = resolver.GetStructuredType( type ); - - if ( structuredType != null ) - { - type = context.ModelTypeBuilder.NewStructuredType( context.Model, structuredType, type, apiVersion ); - } + type = context.ModelTypeBuilder.NewStructuredType( context.Model, structuredType, type, apiVersion ); } return type; @@ -242,16 +238,15 @@ private static Type CloseGeneric( Stack openTypes, Type innerType ) return type; } - private static bool CanBeSubstituted( Type type ) - { - return Type.GetTypeCode( type ) == TypeCode.Object && - !type.IsValueType && - !type.Equals( ActionResultType ) && + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static bool CanBeSubstituted( Type type ) => + Type.GetTypeCode( type ) == TypeCode.Object && + !type.IsValueType && + !type.Equals( ActionResultType ) && #if NETFRAMEWORK - !type.Equals( HttpResponseType ) && + !type.Equals( HttpResponseType ) && #endif - !type.IsODataActionParameters(); - } + !type.IsODataActionParameters(); internal static bool IsEnumerable( this Type type, @@ -295,6 +290,7 @@ internal static bool IsEnumerable( return false; } + [MethodImpl( MethodImplOptions.AggressiveInlining )] private static bool IsSingleResult( this Type type ) => type.Is( SingleResultOfT ); private static bool IsODataValue( this Type? type ) @@ -323,6 +319,7 @@ private static bool IsODataValue( this Type? type ) private static bool Is( this Type type, Type typeDefinition ) => type.IsGenericType && type.GetGenericTypeDefinition().Equals( typeDefinition ); + [MethodImpl( MethodImplOptions.AggressiveInlining )] private static bool ShouldExtractInnerType( this Type type ) => type.IsDelta() || #if !NETFRAMEWORK diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/TypeSubstitutionContext.cs b/src/Common/src/Common.OData.ApiExplorer/OData/TypeSubstitutionContext.cs index baad2af6..b47ae724 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/TypeSubstitutionContext.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/TypeSubstitutionContext.cs @@ -36,7 +36,7 @@ public class TypeSubstitutionContext /// Gets API version associated with the source model. /// /// The associated API version. - public ApiVersion ApiVersion => apiVersion ??= Model.GetAnnotationValue( Model )?.ApiVersion ?? ApiVersion.Neutral; + public ApiVersion ApiVersion => apiVersion ??= Model.GetApiVersion() ?? ApiVersion.Neutral; /// /// Gets the model type builder used to create substitution types. diff --git a/src/Common/src/Common.OData/Microsoft.OData.Edm/IEdmModelExtensions.cs b/src/Common/src/Common.OData/Microsoft.OData.Edm/IEdmModelExtensions.cs new file mode 100644 index 00000000..04d14dd6 --- /dev/null +++ b/src/Common/src/Common.OData/Microsoft.OData.Edm/IEdmModelExtensions.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Microsoft.OData.Edm; + +using Asp.Versioning; +using Asp.Versioning.OData; + +/// +/// Provides extension methods for . +/// +public static class IEdmModelExtensions +{ + /// + /// Gets the API version associated with the Entity Data Model (EDM). + /// + /// The extended EDM. + /// The associated API version or null. + public static ApiVersion? GetApiVersion( this IEdmModel model ) => + model.GetAnnotationValue( model )?.ApiVersion; + + /// + /// Gets a value indicating whether the Entity Data Model (EDM) is for defined ad hoc usage. + /// + /// The extended EDM. + /// True if the EDM is defined for ad hoc usage; otherwise, false. + public static bool IsAdHoc( this IEdmModel model ) => + model.GetAnnotationValue( model ) is not null; +} \ No newline at end of file diff --git a/src/Common/src/Common.OData/OData/AdHocAnnotation.cs b/src/Common/src/Common.OData/OData/AdHocAnnotation.cs new file mode 100644 index 00000000..c787bacc --- /dev/null +++ b/src/Common/src/Common.OData/OData/AdHocAnnotation.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.OData; + +/// +/// Represents an annotation for ad hoc usage. +/// +public sealed class AdHocAnnotation +{ + /// + /// Gets a singleton instance of the annotation. + /// + /// A singleton annotation instance. + public static AdHocAnnotation Instance { get; } = new(); +} \ No newline at end of file From a668bde80e78e62be7b59930565fef9d072d7271 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 6 Dec 2022 10:52:30 -0800 Subject: [PATCH 010/131] Define option to opt out of ad hoc model type substitution --- .../OData/DefaultModelTypeBuilder.cs | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs index 62df945a..37c3bebc 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs @@ -24,6 +24,18 @@ namespace Asp.Versioning.OData; /// public sealed class DefaultModelTypeBuilder : IModelTypeBuilder { + /* design: there is typically a 1:1 relationship between an edm and api version. odata model bound settings + * are realized as an annotation in the edm. this can result in two sets of pairs where one edm is the + * standard mapping and the other is ad hoc for the purposes of query option settings. aside for the bucket + * they land in, there is no difference in how types will be mapped; however if the wrong edm from the + * incorrect bucket is picked, then the type mapping will fail. the model type builder detects if a model + * is ad hoc. if it is, then it will recursively create a private instance of itself to handle the ad hoc + * bucket. normal odata cannot opt out of this process because the explored type must match the edm. a type + * mapped via an ad hoc edm is not really odata so it can opt out if desired. the opt out process is more + * of a failsafe and optimization. if the ad hoc edm wasn't customized, then the meta model and type should + * be exactly the same, which will result in no substitution. + */ + private static Type? ienumerableOfT; private readonly bool adHoc; private DefaultModelTypeBuilder? adHocBuilder; @@ -38,6 +50,14 @@ public sealed class DefaultModelTypeBuilder : IModelTypeBuilder /// public DefaultModelTypeBuilder() { } + /// + /// Gets or sets a value indicating whether types from an ad hoc Entity Data Model + /// (EDM) should be excluded. + /// + /// True if types from an ad hoc EDM are excluded; otherwise, false. The + /// default value is false. + public bool ExcludeAdHocModels { get; set; } + /// public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredType, Type clrType, ApiVersion apiVersion ) { @@ -46,10 +66,17 @@ public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredTyp throw new ArgumentNullException( nameof( model ) ); } - if ( !adHoc && model.IsAdHoc() ) + if ( model.IsAdHoc() ) { - adHocBuilder ??= new( adHoc: true ); - return adHocBuilder.NewStructuredType( model, structuredType, clrType, apiVersion ); + if ( ExcludeAdHocModels ) + { + return clrType; + } + else if ( !adHoc ) + { + adHocBuilder ??= new( adHoc: true ); + return adHocBuilder.NewStructuredType( model, structuredType, clrType, apiVersion ); + } } if ( structuredType == null ) From b893ad9b21022e5daf6f4e995bfb1fd52e3cec82 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 6 Dec 2022 10:53:36 -0800 Subject: [PATCH 011/131] Refactor to use OptionsFactory base implementation --- .../ApiExplorerOptionsFactory{T}.cs | 71 +++++++------------ 1 file changed, 26 insertions(+), 45 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiExplorerOptionsFactory{T}.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiExplorerOptionsFactory{T}.cs index 810f3a25..b2d866c9 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiExplorerOptionsFactory{T}.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiExplorerOptionsFactory{T}.cs @@ -3,14 +3,13 @@ namespace Asp.Versioning.ApiExplorer; using Microsoft.Extensions.Options; -using Option = Microsoft.Extensions.Options.Options; /// /// Represents a factory to create API explorer options. /// /// The type of options to create. [CLSCompliant( false )] -public class ApiExplorerOptionsFactory : IOptionsFactory where T : ApiExplorerOptions, new() +public class ApiExplorerOptionsFactory : OptionsFactory where T : ApiExplorerOptions { private readonly IOptions optionsHolder; @@ -27,60 +26,42 @@ public ApiExplorerOptionsFactory( IOptions options, IEnumerable> setups, IEnumerable> postConfigures ) - { - optionsHolder = options; - Setups = setups; - PostConfigures = postConfigures; - } + : base( setups, postConfigures ) => optionsHolder = options; /// - /// Gets the API versioning options associated with the factory. - /// - /// The API versioning options used to create API explorer options. - protected ApiVersioningOptions Options => optionsHolder.Value; - - /// - /// Gets the associated configuration actions to run. + /// Initializes a new instance of the class. /// - /// The sequence of - /// configuration actions to run. - protected IEnumerable> Setups { get; } + /// The API versioning options + /// used to create API explorer options. + /// The sequence of + /// configuration actions to run. + /// The sequence of + /// initialization actions to run. + /// The sequence of + /// validations to run. + public ApiExplorerOptionsFactory( + IOptions options, + IEnumerable> setups, + IEnumerable> postConfigures, + IEnumerable> validations ) + : base( setups, postConfigures, validations ) => optionsHolder = options; /// - /// Gets the associated initialization actions to run. + /// Gets the API versioning options associated with the factory. /// - /// The sequence of - /// initialization actions to run. - protected IEnumerable> PostConfigures { get; } + /// The API versioning options used to create API explorer options. + protected ApiVersioningOptions Options => optionsHolder.Value; /// - public virtual T Create( string name ) + protected override T CreateInstance( string name ) { var apiVersioningOptions = Options; - var options = new T() - { - AssumeDefaultVersionWhenUnspecified = apiVersioningOptions.AssumeDefaultVersionWhenUnspecified, - ApiVersionParameterSource = apiVersioningOptions.ApiVersionReader, - DefaultApiVersion = apiVersioningOptions.DefaultApiVersion, - RouteConstraintName = apiVersioningOptions.RouteConstraintName, - }; - - foreach ( var setup in Setups ) - { - if ( setup is IConfigureNamedOptions namedSetup ) - { - namedSetup.Configure( name, options ); - } - else if ( name == Option.DefaultName ) - { - setup.Configure( options ); - } - } + var options = base.CreateInstance( name ); - foreach ( var post in PostConfigures ) - { - post.PostConfigure( name, options ); - } + options.AssumeDefaultVersionWhenUnspecified = apiVersioningOptions.AssumeDefaultVersionWhenUnspecified; + options.ApiVersionParameterSource = apiVersioningOptions.ApiVersionReader; + options.DefaultApiVersion = apiVersioningOptions.DefaultApiVersion; + options.RouteConstraintName = apiVersioningOptions.RouteConstraintName; return options; } From 3f65e5b948c2484bcbbd74196880b4210931662b Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 6 Dec 2022 10:55:45 -0800 Subject: [PATCH 012/131] Add support for exploring OData query options using an ad hoc EDM. Related #928 --- .../ApiExplorer/AdHocEdmScope.cs | 100 ++++++++ .../ApiExplorer/ODataApiExplorer.cs | 58 ++++- .../ApiExplorer/ODataApiExplorerOptions.cs | 3 +- .../ImplicitModelBoundSettingsConvention.cs | 31 +++ .../ODataQueryOptionsConventionBuilder.cs | 17 -- .../Description/ApiDescriptionExtensions.cs | 154 ++++++------ .../Description/ODataApiExplorerTest.cs | 34 +++ .../Simulators/Models/Book.cs | 3 + .../Simulators/V1/BooksController.cs | 3 + .../ApiExplorer/ApiDescriptionExtensions.cs | 21 ++ .../ApiExplorer/ODataApiExplorerOptions.cs | 19 ++ .../ODataApiExplorerOptionsFactory.cs | 112 +++++++++ .../PartialODataDescriptionProvider.cs | 220 ++++++++++++++++++ .../ImplicitModelBoundSettingsConvention.cs | 49 ++++ .../IApiVersioningBuilderExtensions.cs | 9 +- .../ODataApiDescriptionProviderTest.cs | 27 +++ .../Simulators/Models/Book.cs | 3 + .../ApiExplorer/ODataApiExplorerOptions.cs | 10 + .../ImplicitModelBoundSettingsConvention.cs | 84 +++++++ .../ODataQueryOptionsConventionBuilder.cs | 2 +- 20 files changed, 857 insertions(+), 102 deletions(-) create mode 100644 src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/AdHocEdmScope.cs create mode 100644 src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs create mode 100644 src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ApiDescriptionExtensions.cs create mode 100644 src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs create mode 100644 src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs create mode 100644 src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs create mode 100644 src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs create mode 100644 src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/AdHocEdmScope.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/AdHocEdmScope.cs new file mode 100644 index 00000000..2bcf1549 --- /dev/null +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/AdHocEdmScope.cs @@ -0,0 +1,100 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer; + +using Asp.Versioning.Conventions; +using Asp.Versioning.Description; +using Asp.Versioning.OData; +using Microsoft.OData.Edm; +using System.Collections.Generic; +using System.Web.Http; +using System.Web.Http.Description; + +internal sealed class AdHocEdmScope : IDisposable +{ + private readonly IReadOnlyList results; + private bool disposed; + + internal AdHocEdmScope( + IReadOnlyList apiDescriptions, + VersionedODataModelBuilder builder ) + { + var conventions = builder.ModelConfigurations.OfType().ToArray(); + + results = FilterResults( apiDescriptions, conventions ); + + if ( results.Count > 0 ) + { + ApplyAdHocEdm( builder.GetEdmModels(), results ); + } + } + + public void Dispose() + { + if ( disposed ) + { + return; + } + + disposed = true; + + for ( var i = 0; i < results.Count; i++ ) + { + results[i].SetProperty( default( IEdmModel ) ); + } + } + + private static IReadOnlyList FilterResults( + IReadOnlyList apiDescriptions, + IReadOnlyList conventions ) + { + if ( conventions.Count == 0 ) + { + return Array.Empty(); + } + + var results = default( List ); + + for ( var i = 0; i < apiDescriptions.Count; i++ ) + { + var apiDescription = apiDescriptions[i]; + + if ( apiDescription.EdmModel() != null || !apiDescription.IsODataLike() ) + { + continue; + } + + results ??= new(); + results.Add( apiDescription ); + + for ( var j = 0; j < conventions.Count; j++ ) + { + conventions[j].ApplyTo( apiDescription ); + } + } + + return results?.ToArray() ?? Array.Empty(); + } + + private static void ApplyAdHocEdm( + IReadOnlyList models, + IReadOnlyList results ) + { + for ( var i = 0; i < models.Count; i++ ) + { + var model = models[i]; + var version = model.GetApiVersion(); + + for ( var j = 0; j < results.Count; j++ ) + { + var result = results[j]; + var metadata = result.ActionDescriptor.GetApiVersionMetadata(); + + if ( metadata.IsMappedTo( version ) ) + { + result.SetProperty( model ); + } + } + } + } +} \ No newline at end of file diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs index f6fda7f9..4c45eb70 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs @@ -7,6 +7,7 @@ namespace Asp.Versioning.ApiExplorer; using Asp.Versioning.OData; using Asp.Versioning.Routing; using Microsoft.AspNet.OData; +using Microsoft.AspNet.OData.Builder; using Microsoft.AspNet.OData.Extensions; using Microsoft.AspNet.OData.Formatter; using Microsoft.AspNet.OData.Routing; @@ -16,6 +17,7 @@ namespace Asp.Versioning.ApiExplorer; using Microsoft.OData.UriParser; using System.Collections.ObjectModel; using System.Net.Http.Formatting; +using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using System.Web.Http; using System.Web.Http.Controllers; @@ -50,7 +52,11 @@ public ODataApiExplorer( HttpConfiguration configuration ) /// The current HTTP configuration. /// The associated API explorer options. public ODataApiExplorer( HttpConfiguration configuration, ODataApiExplorerOptions options ) - : base( configuration, options ) => this.options = options; + : base( configuration, options ) + { + this.options = options; + options.AdHocModelBuilder.OnModelCreated += MarkAsAdHoc; + } /// /// Gets the options associated with the API explorer. @@ -172,7 +178,20 @@ protected override Collection ExploreRouteControllers( if ( route is not ODataRoute ) { apiDescriptions = base.ExploreRouteControllers( controllerMappings, route, apiVersion ); - return ExploreQueryOptions( route, apiDescriptions ); + + if ( Options.AdHocModelBuilder.ModelConfigurations.Count == 0 ) + { + ExploreQueryOptions( route, apiDescriptions ); + } + else if ( apiDescriptions.Count > 0 ) + { + using ( new AdHocEdmScope( apiDescriptions, Options.AdHocModelBuilder ) ) + { + ExploreQueryOptions( route, apiDescriptions ); + } + } + + return apiDescriptions; } apiDescriptions = new(); @@ -199,7 +218,8 @@ protected override Collection ExploreRouteControllers( } } - return ExploreQueryOptions( route, apiDescriptions ); + ExploreQueryOptions( route, apiDescriptions ); + return apiDescriptions; } /// @@ -210,7 +230,25 @@ protected override Collection ExploreDirectRouteControl ApiVersion apiVersion ) { var apiDescriptions = base.ExploreDirectRouteControllers( controllerDescriptor, candidateActionDescriptors, route, apiVersion ); - return ExploreQueryOptions( route, apiDescriptions ); + + if ( apiDescriptions.Count == 0 ) + { + return apiDescriptions; + } + + if ( Options.AdHocModelBuilder.ModelConfigurations.Count == 0 ) + { + ExploreQueryOptions( route, apiDescriptions ); + } + else if ( apiDescriptions.Count > 0 ) + { + using ( new AdHocEdmScope( apiDescriptions, Options.AdHocModelBuilder ) ) + { + ExploreQueryOptions( route, apiDescriptions ); + } + } + + return apiDescriptions; } /// @@ -238,20 +276,20 @@ protected virtual void ExploreQueryOptions( queryOptions.ApplyTo( apiDescriptions, settings ); } - private Collection ExploreQueryOptions( - IHttpRoute route, - Collection apiDescriptions ) + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static void MarkAsAdHoc( ODataModelBuilder builder, IEdmModel model ) => + model.SetAnnotationValue( model, AdHocAnnotation.Instance ); + + private void ExploreQueryOptions( IHttpRoute route, Collection apiDescriptions ) { if ( apiDescriptions.Count == 0 ) { - return apiDescriptions; + return; } var uriResolver = Configuration.GetODataRootContainer( route ).GetRequiredService(); ExploreQueryOptions( apiDescriptions, uriResolver ); - - return apiDescriptions; } private ResponseDescription CreateResponseDescriptionWithRoute( diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs index b9d34d6b..4bf5b113 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs @@ -15,7 +15,8 @@ public partial class ODataApiExplorerOptions : ApiExplorerOptions /// Initializes a new instance of the class. /// /// The current configuration associated with the options. - public ODataApiExplorerOptions( HttpConfiguration configuration ) : base( configuration ) { } + public ODataApiExplorerOptions( HttpConfiguration configuration ) + : base( configuration ) => AdHocModelBuilder = new( configuration ); /// /// Gets or sets a value indicating whether the API explorer settings are honored. diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs new file mode 100644 index 00000000..771af4de --- /dev/null +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.Conventions; + +using Asp.Versioning.OData; +using System.Web.Http.Description; + +/// +/// Provides additional implementation specific to ASP.NET Web API. +/// +public partial class ImplicitModelBoundSettingsConvention : IModelConfiguration, IODataQueryOptionsConvention +{ + /// + public void ApplyTo( ApiDescription apiDescription ) + { + var response = apiDescription.ResponseDescription; + var type = response.ResponseType ?? response.DeclaredType; + + if ( type == null ) + { + return; + } + + if ( type.IsEnumerable( out var itemType ) ) + { + type = itemType; + } + + types.Add( type! ); + } +} \ No newline at end of file diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs index 04f8cf6d..8b297ebb 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs @@ -2,7 +2,6 @@ namespace Asp.Versioning.Conventions; -using Microsoft.AspNet.OData; using System.Runtime.CompilerServices; using System.Web.Http.Description; @@ -14,20 +13,4 @@ public partial class ODataQueryOptionsConventionBuilder [MethodImpl( MethodImplOptions.AggressiveInlining )] private static Type GetController( ApiDescription apiDescription ) => apiDescription.ActionDescriptor.ControllerDescriptor.ControllerType; - - [MethodImpl( MethodImplOptions.AggressiveInlining )] - private static bool IsODataLike( ApiDescription description ) - { - var parameters = description.ParameterDescriptions; - - for ( var i = 0; i < parameters.Count; i++ ) - { - if ( parameters[i].ParameterDescriptor.ParameterType.IsODataQueryOptions() ) - { - return true; - } - } - - return false; - } } \ No newline at end of file diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/System.Web.Http/Description/ApiDescriptionExtensions.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/System.Web.Http/Description/ApiDescriptionExtensions.cs index 3c19916d..cba8d13e 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/System.Web.Http/Description/ApiDescriptionExtensions.cs +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/System.Web.Http/Description/ApiDescriptionExtensions.cs @@ -1,100 +1,114 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -namespace System.Web.Http.Description -{ - using Asp.Versioning.Description; - using Microsoft.AspNet.OData.Routing; - using Microsoft.OData.Edm; +namespace System.Web.Http.Description; + +using Asp.Versioning.Description; +using Microsoft.AspNet.OData.Routing; +using Microsoft.OData.Edm; +/// +/// Provides extension methods for the class. +/// +public static class ApiDescriptionExtensions +{ /// - /// Provides extension methods for the class. + /// Gets the entity data model (EDM) associated with the API description. /// - public static class ApiDescriptionExtensions + /// The API description to get the model for. + /// The associated EDM model or null if there is no associated model. + public static IEdmModel? EdmModel( this ApiDescription apiDescription ) { - /// - /// Gets the entity data model (EDM) associated with the API description. - /// - /// The API description to get the model for. - /// The associated EDM model or null if there is no associated model. - public static IEdmModel? EdmModel( this ApiDescription apiDescription ) + if ( apiDescription is VersionedApiDescription description ) { - if ( apiDescription is VersionedApiDescription description ) - { - return description.GetProperty(); - } + return description.GetProperty(); + } + return default; + } + + /// + /// Gets the entity set associated with the API description. + /// + /// The API description to get the entity set for. + /// The associated entity set or null if there is no associated entity set. + public static IEdmEntitySet? EntitySet( this ApiDescription apiDescription ) + { + if ( apiDescription is not VersionedApiDescription description ) + { return default; } - /// - /// Gets the entity set associated with the API description. - /// - /// The API description to get the entity set for. - /// The associated entity set or null if there is no associated entity set. - public static IEdmEntitySet? EntitySet( this ApiDescription apiDescription ) + var key = typeof( IEdmEntitySet ); + + if ( description.Properties.TryGetValue( key, out var value ) ) { - if ( apiDescription is not VersionedApiDescription description ) - { - return default; - } + return (IEdmEntitySet) value; + } - var key = typeof( IEdmEntitySet ); + var container = description.EdmModel()?.EntityContainer; - if ( description.Properties.TryGetValue( key, out var value ) ) - { - return (IEdmEntitySet) value; - } + if ( container == null ) + { + return default; + } - var container = description.EdmModel()?.EntityContainer; + var entitySetName = description.ActionDescriptor.ControllerDescriptor.ControllerName; + var entitySet = container.FindEntitySet( entitySetName ); - if ( container == null ) - { - return default; - } + description.Properties[key] = entitySet; - var entitySetName = description.ActionDescriptor.ControllerDescriptor.ControllerName; - var entitySet = container.FindEntitySet( entitySetName ); + return entitySet; + } - description.Properties[key] = entitySet; + /// + /// Gets the entity type associated with the API description. + /// + /// The API description to get the entity type for. + /// The associated entity type or null if there is no associated entity type. + public static IEdmEntityType? EntityType( this ApiDescription apiDescription ) => apiDescription.EntitySet()?.EntityType(); - return entitySet; + /// + /// Gets the operation associated with the API description. + /// + /// The API description to get the operation for. + /// The associated EDM operation or null if there is no associated operation. + public static IEdmOperation? Operation( this ApiDescription apiDescription ) + { + if ( apiDescription is VersionedApiDescription description ) + { + return description.GetProperty(); } - /// - /// Gets the entity type associated with the API description. - /// - /// The API description to get the entity type for. - /// The associated entity type or null if there is no associated entity type. - public static IEdmEntityType? EntityType( this ApiDescription apiDescription ) => apiDescription.EntitySet()?.EntityType(); - - /// - /// Gets the operation associated with the API description. - /// - /// The API description to get the operation for. - /// The associated EDM operation or null if there is no associated operation. - public static IEdmOperation? Operation( this ApiDescription apiDescription ) - { - if ( apiDescription is VersionedApiDescription description ) - { - return description.GetProperty(); - } + return default; + } - return default; + /// + /// Gets the route prefix associated with the API description. + /// + /// The API description to get the route prefix for. + /// The associated route prefix or null. + public static string? RoutePrefix( this ApiDescription apiDescription ) + { + if ( apiDescription == null ) + { + throw new ArgumentNullException( nameof( apiDescription ) ); } - /// - /// Gets the route prefix associated with the API description. - /// - /// The API description to get the route prefix for. - /// The associated route prefix or null. - public static string? RoutePrefix( this ApiDescription apiDescription ) + return apiDescription.Route is ODataRoute route ? route.RoutePrefix : default; + } + + internal static bool IsODataLike( this ApiDescription description ) + { + var parameters = description.ParameterDescriptions; + + for ( var i = 0; i < parameters.Count; i++ ) { - if ( apiDescription == null ) + if ( parameters[i].ParameterDescriptor.ParameterType.IsODataQueryOptions() ) { - throw new ArgumentNullException( nameof( apiDescription ) ); + return true; } - - return apiDescription.Route is ODataRoute route ? route.RoutePrefix : default; } + + return false; } } \ No newline at end of file diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Description/ODataApiExplorerTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Description/ODataApiExplorerTest.cs index 534d10a3..34540a00 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Description/ODataApiExplorerTest.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Description/ODataApiExplorerTest.cs @@ -3,9 +3,13 @@ namespace Asp.Versioning.Description; using Asp.Versioning.ApiExplorer; +using Asp.Versioning.Controllers; +using Asp.Versioning.Conventions; using Microsoft.AspNet.OData; +using Microsoft.AspNet.OData.Extensions; using System.Net.Http; using System.Web.Http; +using System.Web.Http.Dispatcher; using static System.Net.Http.HttpMethod; public class ODataApiExplorerTest @@ -210,4 +214,34 @@ public void api_description_group_should_explore_navigation_properties() }, options => options.ExcludingMissingMembers() ); } + + [Fact] + public void api_description_group_should_explore_model_bound_settings() + { + // arrange + var configuration = new HttpConfiguration(); + var controllerTypeResolver = new ControllerTypeCollection( + typeof( VersionedMetadataController ), + typeof( Simulators.V1.BooksController ) ); + + configuration.Services.Replace( typeof( IHttpControllerTypeResolver ), controllerTypeResolver ); + configuration.EnableDependencyInjection(); + configuration.AddApiVersioning(); + configuration.MapHttpAttributeRoutes(); + + var apiVersion = new ApiVersion( 1.0 ); + var options = new ODataApiExplorerOptions( configuration ); + var apiExplorer = new ODataApiExplorer( configuration, options ); + + options.AdHocModelBuilder.ModelConfigurations.Add( new ImplicitModelBoundSettingsConvention() ); + + // act + var descriptionGroup = apiExplorer.ApiDescriptions[apiVersion]; + var description = descriptionGroup.ApiDescriptions[0]; + + // assert + var parameter = description.ParameterDescriptions.Single( p => p.Name == "$filter" ); + + parameter.Documentation.Should().EndWith( "author, published." ); + } } \ No newline at end of file diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Book.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Book.cs index 404bf071..72b12f9b 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Book.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Book.cs @@ -2,6 +2,9 @@ namespace Asp.Versioning.Simulators.Models; +using Microsoft.AspNet.OData.Query; + +[Filter( "author", "published" )] public class Book { public string Id { get; set; } diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs index e6e24820..24d7a24e 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs @@ -14,6 +14,7 @@ namespace Asp.Versioning.Simulators.V1; /// Represents a RESTful service of books. /// [ApiVersion( 1.0 )] +[RoutePrefix( "api/books" )] public class BooksController : ApiController { private static readonly Book[] books = new Book[] @@ -33,6 +34,7 @@ public class BooksController : ApiController /// All available books. /// The successfully retrieved books. [HttpGet] + [Route] [ResponseType( typeof( IEnumerable ) )] public IHttpActionResult Get( ODataQueryOptions options ) => Ok( options.ApplyTo( books.AsQueryable() ) ); @@ -46,6 +48,7 @@ public IHttpActionResult Get( ODataQueryOptions options ) => /// The book was successfully retrieved. /// The book does not exist. [HttpGet] + [Route( "{id}" )] [ResponseType( typeof( Book ) )] public IHttpActionResult Get( string id, ODataQueryOptions options ) { diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ApiDescriptionExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ApiDescriptionExtensions.cs new file mode 100644 index 00000000..29520295 --- /dev/null +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ApiDescriptionExtensions.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Microsoft.AspNetCore.Mvc.ApiExplorer; + +internal static class ApiDescriptionExtensions +{ + internal static bool IsODataLike( this ApiDescription description ) + { + var parameters = description.ActionDescriptor.Parameters; + + for ( var i = 0; i < parameters.Count; i++ ) + { + if ( parameters[i].ParameterType.IsODataQueryOptions() ) + { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs new file mode 100644 index 00000000..5c73744b --- /dev/null +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer; + +using Asp.Versioning.OData; + +/// +/// Provides additional implementation specific to ASP.NET Core. +/// +public partial class ODataApiExplorerOptions +{ + /// + /// Initializes a new instance of the class. + /// + /// The associated model builder. + [CLSCompliant( false )] + public ODataApiExplorerOptions( VersionedODataModelBuilder modelBuilder ) => + AdHocModelBuilder = modelBuilder ?? throw new ArgumentNullException( nameof( modelBuilder ) ); +} \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs new file mode 100644 index 00000000..ae057645 --- /dev/null +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs @@ -0,0 +1,112 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer; + +using Asp.Versioning.OData; +using Microsoft.Extensions.Options; +using static Asp.Versioning.ApiVersionMapping; + +/// +/// Represents a factory to create OData API explorer options. +/// +[CLSCompliant( false )] +public class ODataApiExplorerOptionsFactory : ApiExplorerOptionsFactory +{ + private readonly IApiVersionMetadataCollationProvider[] providers; + private readonly IEnumerable modelConfigurations; + + /// + /// Initializes a new instance of the class. + /// + /// A sequence of + /// providers used to collate API version metadata. + /// A sequence of + /// configurations used to configure Entity Data Models. + /// The API versioning options + /// used to create API explorer options. + /// The sequence of + /// configuration actions to run. + /// The sequence of + /// initialization actions to run. + public ODataApiExplorerOptionsFactory( + IEnumerable providers, + IEnumerable modelConfigurations, + IOptions options, + IEnumerable> setups, + IEnumerable> postConfigures ) + : base( options, setups, postConfigures ) + { + this.providers = ( providers ?? throw new ArgumentNullException( nameof( providers ) ) ).ToArray(); + this.modelConfigurations = modelConfigurations ?? throw new ArgumentNullException( nameof( modelConfigurations ) ); + } + + /// + /// Initializes a new instance of the class. + /// + /// A sequence of + /// providers used to collate API version metadata. + /// A sequence of + /// configurations used to configure Entity Data Models. + /// The API versioning options + /// used to create API explorer options. + /// The sequence of + /// configuration actions to run. + /// The sequence of + /// initialization actions to run. + /// The sequence of + /// validations to run. + public ODataApiExplorerOptionsFactory( + IEnumerable providers, + IEnumerable modelConfigurations, + IOptions options, + IEnumerable> setups, + IEnumerable> postConfigures, + IEnumerable> validations ) + : base( options, setups, postConfigures, validations ) + { + this.providers = ( providers ?? throw new ArgumentNullException( nameof( providers ) ) ).ToArray(); + this.modelConfigurations = modelConfigurations ?? throw new ArgumentNullException( nameof( modelConfigurations ) ); + } + + /// + protected override ODataApiExplorerOptions CreateInstance( string name ) => + new( new( CollateApiVersions( providers, Options ), modelConfigurations ) ); + + private static ODataApiVersionCollectionProvider CollateApiVersions( + IApiVersionMetadataCollationProvider[] providers, + ApiVersioningOptions options ) + { + var context = new ApiVersionMetadataCollationContext(); + + for ( var i = 0; i < providers.Length; i++ ) + { + providers[i].Execute( context ); + } + + var results = context.Results; + var versions = new SortedSet(); + + for ( var i = 0; i < results.Count; i++ ) + { + var model = results[i].Map( Implicit ); + var declared = model.DeclaredApiVersions; + + for ( var j = 0; j < declared.Count; j++ ) + { + versions.Add( declared[j] ); + } + } + + if ( versions.Count == 0 ) + { + versions.Add( options.DefaultApiVersion ); + } + + return new() { ApiVersions = versions.ToArray() }; + } + + private sealed class ODataApiVersionCollectionProvider : IODataApiVersionCollectionProvider + { + required public IReadOnlyList ApiVersions { get; set; } + } +} \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs new file mode 100644 index 00000000..562a8469 --- /dev/null +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs @@ -0,0 +1,220 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer; + +using Asp.Versioning; +using Asp.Versioning.Conventions; +using Asp.Versioning.OData; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.OData; +using Microsoft.AspNetCore.OData.Routing; +using Microsoft.AspNetCore.OData.Routing.Template; +using Microsoft.Extensions.Options; +using Microsoft.OData.Edm; +using Microsoft.OData.ModelBuilder; +using System.Runtime.CompilerServices; +using Opts = Microsoft.Extensions.Options.Options; + +/// +/// Reprensents an API description provider for partial OData support. +/// +[CLSCompliant( false )] +public class PartialODataDescriptionProvider : IApiDescriptionProvider +{ + private static int? beforeOData; + private readonly IOptionsFactory odataOptionsFactory; + private readonly IOptions options; + private bool markedAdHoc; + private IODataQueryOptionsConvention[]? conventions; + + /// + /// Initializes a new instance of the class. + /// + /// The factory used to create + /// OData options. + /// The container of configured + /// API explorer options. + public PartialODataDescriptionProvider( + IOptionsFactory odataOptionsFactory, + IOptions options ) + { + this.odataOptionsFactory = odataOptionsFactory ?? throw new ArgumentNullException( nameof( odataOptionsFactory ) ); + this.options = options ?? throw new ArgumentNullException( nameof( options ) ); + beforeOData ??= ODataOrder( odataOptionsFactory, options ) + 10; + Order = beforeOData.Value; + } + + /// + /// Gets the associated OData API explorer options. + /// + /// The current OData API explorer options. + protected ODataApiExplorerOptions Options + { + get + { + var value = options.Value; + + if ( !markedAdHoc ) + { + value.AdHocModelBuilder.OnModelCreated += MarkAsAdHoc; + markedAdHoc = true; + } + + return value; + } + } + + /// + /// Gets the builder used to create ad hoc Entity Data Models (EDMs). + /// + /// The associated model builder. + protected VersionedODataModelBuilder ModelBuilder => Options.AdHocModelBuilder; + + /// + /// Gets associated the OData query option conventions. + /// + /// A read-only list of + /// OData query option conventions. + protected IReadOnlyList Conventions => + conventions ??= Options.AdHocModelBuilder.ModelConfigurations.OfType().ToArray(); + + /// + /// Gets or sets the order precedence of the current API description provider. + /// + /// The order precedence of the current API description provider. + public int Order { get; protected set; } + + /// + public virtual void OnProvidersExecuting( ApiDescriptionProviderContext context ) + { + var results = FilterResults( context.Results, Conventions ); + + if ( results.Count == 0 ) + { + return; + } + + var models = ModelBuilder.GetEdmModels(); + + for ( var i = 0; i < models.Count; i++ ) + { + var model = models[i]; + var version = model.GetApiVersion(); + var options = odataOptionsFactory.Create( Opts.DefaultName ); + + options.AddRouteComponents( model ); + + for ( var j = 0; j < results.Count; j++ ) + { + var result = results[j]; + var metadata = result.ActionDescriptor.GetApiVersionMetadata(); + + if ( metadata.IsMappedTo( version ) ) + { + result.ActionDescriptor.EndpointMetadata.Add( ODataMetadata.New( model ) ); + } + } + } + } + + /// + public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context ) + { + var actions = context.Actions; + + for ( var i = 0; i < actions.Count; i++ ) + { + var metadata = actions[i].EndpointMetadata; + + for ( var j = metadata.Count - 1; j >= 0; j-- ) + { + if ( metadata[j] is IODataRoutingMetadata routing && routing.Model.IsAdHoc() ) + { + metadata.Remove( j ); + } + } + } + } + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static int ODataOrder( IOptionsFactory factory, IOptions options ) => + new ODataApiDescriptionProvider( + new StubModelMetadataProvider(), + new StubModelTypeBuilder(), + factory, + options ).Order; + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static void MarkAsAdHoc( ODataModelBuilder builder, IEdmModel model ) => + model.SetAnnotationValue( model, AdHocAnnotation.Instance ); + + private static IReadOnlyList FilterResults( + IList results, + IReadOnlyList conventions ) + { + var filtered = default( List ); + + for ( var i = 0; i < results.Count; i++ ) + { + var result = results[i]; + var metadata = result.ActionDescriptor.EndpointMetadata; + var odata = false; + + for ( var j = 0; j < metadata.Count; j++ ) + { + if ( metadata[j] is IODataRoutingMetadata ) + { + odata = true; + break; + } + } + + if ( odata || !result.IsODataLike() ) + { + continue; + } + + filtered ??= new( capacity: results.Count ); + filtered.Add( result ); + + for ( var j = 0; j < conventions.Count; j++ ) + { + conventions[j].ApplyTo( result ); + } + } + + return filtered?.ToArray() ?? Array.Empty(); + } + + private sealed class StubModelMetadataProvider : IModelMetadataProvider + { + public IEnumerable GetMetadataForProperties( Type modelType ) => + throw new NotImplementedException(); + + public ModelMetadata GetMetadataForType( Type modelType ) => + throw new NotImplementedException(); + } + + private sealed class StubModelTypeBuilder : IModelTypeBuilder + { + public Type NewActionParameters( IEdmModel model, IEdmAction action, string controllerName, ApiVersion apiVersion ) => + throw new NotImplementedException(); + + public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredType, Type clrType, ApiVersion apiVersion ) => + throw new NotImplementedException(); + } + + private static class ODataMetadata + { + private const string ArbitrarySegment = "52459ff8-bca1-4a26-b7f2-08c7da04472d"; + + // metadata (~/$metadata) and service (~/) doc have special handling. + // make sure we don't match the service doc + private static readonly ODataPathTemplate AdHocODataTemplate = + new( new DynamicSegmentTemplate( new( ArbitrarySegment ) ) ); + + public static ODataRoutingMetadata New( IEdmModel model ) => new( string.Empty, model, AdHocODataTemplate ); + } +} \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs new file mode 100644 index 00000000..3800afd6 --- /dev/null +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs @@ -0,0 +1,49 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.Conventions; + +using Asp.Versioning; +using Asp.Versioning.OData; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.OData.ModelBuilder; + +/// +/// Provides additional implementation specific to ASP.NET Core. +/// +[CLSCompliant( false )] +public partial class ImplicitModelBoundSettingsConvention +{ + /// + public void ApplyTo( ApiDescription apiDescription ) + { + if ( apiDescription == null ) + { + throw new ArgumentNullException( nameof( apiDescription ) ); + } + + var responses = apiDescription.SupportedResponseTypes; + + for ( var j = 0; j < responses.Count; j++ ) + { + var response = responses[j]; + var notForSuccess = response.StatusCode < 200 || response.StatusCode >= 300; + + if ( notForSuccess ) + { + continue; + } + + var model = response.ModelMetadata; + var type = model == null + ? response.Type + : model.IsEnumerableType + ? model.ElementType + : model.UnderlyingOrModelType; + + if ( type != null ) + { + types.Add( type ); + } + } + } +} \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs index 27283ec7..d19c8ed5 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -4,6 +4,7 @@ namespace Microsoft.Extensions.DependencyInjection; using Asp.Versioning; using Asp.Versioning.ApiExplorer; +using Asp.Versioning.Conventions; using Asp.Versioning.OData; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -55,9 +56,12 @@ private static void AddApiExplorerServices( IApiVersioningBuilder builder ) var services = builder.Services; builder.AddApiExplorer(); + builder.Services.AddModelConfigurationsAsServices(); services.TryAddSingleton(); - services.TryAddSingleton, ApiExplorerOptionsFactory>(); + services.TryAddSingleton, ODataApiExplorerOptionsFactory>(); + services.TryAddEnumerable( Transient() ); services.TryAddEnumerable( Transient() ); + services.TryAddEnumerable( Transient() ); services.Replace( Singleton, ODataApiExplorerOptionsAdapter>() ); } @@ -67,8 +71,7 @@ private sealed class ODataApiExplorerOptionsAdapter : IOptionsFactory factory; - public ODataApiExplorerOptionsAdapter( IOptionsFactory factory ) => - this.factory = factory; + public ODataApiExplorerOptionsAdapter( IOptionsFactory factory ) => this.factory = factory; public ApiExplorerOptions Create( string name ) => factory.Create( name ); } diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/ApiExplorer/ODataApiDescriptionProviderTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/ApiExplorer/ODataApiDescriptionProviderTest.cs index 3c3f3136..511513b4 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/ApiExplorer/ODataApiDescriptionProviderTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/ApiExplorer/ODataApiDescriptionProviderTest.cs @@ -9,6 +9,7 @@ namespace Asp.Versioning.ApiExplorer; using Microsoft.AspNetCore.OData; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using System.Buffers; public class ODataApiDescriptionProviderTest { @@ -152,6 +153,8 @@ private void AssertVersion1( ApiDescriptionGroup group ) new { HttpMethod = "GET", GroupName, RelativePath = "api/People/{key}" }, }, options => options.ExcludingMissingMembers() ); + + AssertQueryOptionWithoutOData( items[0], "filter", "author", "published" ); } private void AssertVersion2( ApiDescriptionGroup group ) @@ -229,6 +232,30 @@ private void AssertVersion3( ApiDescriptionGroup group ) items.Should().BeEquivalentTo( expected, options => options.ExcludingMissingMembers() ); } + private static void AssertQueryOptionWithoutOData( ApiDescription description, string name, string property, params string[] otherProperties ) + { + var parameter = description.ParameterDescriptions.Single( p => p.Name == name ); + var count = otherProperties.Length + 1; + string suffix; + + if ( count == 1 ) + { + suffix = property; + } + else + { + var pool = ArrayPool.Shared; + var properties = pool.Rent( count ); + + properties[0] = property; + Array.Copy( otherProperties, 0, properties, 1, count - 1 ); + + suffix = string.Join( ", ", properties, 0, count ); + } + + parameter.ModelMetadata.Description.Should().EndWith( suffix + '.' ); + } + private void PrintGroup( IReadOnlyList items ) { for ( var i = 0; i < items.Count; i++ ) diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Book.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Book.cs index 00fec7f1..97610da6 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Book.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Book.cs @@ -2,9 +2,12 @@ namespace Asp.Versioning.Simulators.Models; +using Microsoft.OData.ModelBuilder; + /// /// Represents a book. /// +[Filter( "author", "published" )] public class Book { /// diff --git a/src/Common/src/Common.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs b/src/Common/src/Common.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs index c90cafb5..45acaa44 100644 --- a/src/Common/src/Common.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs +++ b/src/Common/src/Common.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptions.cs @@ -3,6 +3,7 @@ namespace Asp.Versioning.ApiExplorer; using Asp.Versioning.Conventions; +using Asp.Versioning.OData; /// /// Represents the possible API versioning options for an OData API explorer. @@ -44,4 +45,13 @@ public ODataQueryOptionsConventionBuilder QueryOptions /// /// One or more values. public ODataMetadataOptions MetadataOptions { get; set; } = ODataMetadataOptions.None; + + /// + /// Gets the builder used to create ad hoc versioned Entity Data Models (EDMs). + /// + /// The associated model builder. +#if !NETFRAMEWORK + [CLSCompliant( false )] +#endif + public VersionedODataModelBuilder AdHocModelBuilder { get; } } \ No newline at end of file diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs new file mode 100644 index 00000000..39dd9f3b --- /dev/null +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs @@ -0,0 +1,84 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.Conventions; + +using Asp.Versioning; +using Asp.Versioning.OData; +#if NETFRAMEWORK +using Microsoft.AspNet.OData.Builder; +#else +using Microsoft.OData.ModelBuilder; +#endif + +/// +/// Represents an OData model bound settings model configuration +/// that is also an OData query options convention. +/// +public sealed partial class ImplicitModelBoundSettingsConvention : IModelConfiguration, IODataQueryOptionsConvention +{ + private readonly HashSet types = new(); + + /// + public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string? routePrefix ) + { + if ( builder == null ) + { + throw new ArgumentNullException( nameof( builder ) ); + } + + if ( types.Count == 0 ) + { + return; + } + + if ( GetExistingTypes( builder ) is HashSet existingTypes ) + { + types.ExceptWith( existingTypes ); + } + + if ( types.Count == 0 ) + { + return; + } + + // model configurations are applied unordered, which could matter. + // defer implicit registrations in the model until all other model + // configurations have been applied, if possible + if ( builder is ODataConventionModelBuilder modelBuilder ) + { + modelBuilder.OnModelCreating += OnModelCreating; + } + else + { + OnModelCreating( builder ); + } + } + + private static HashSet? GetExistingTypes( ODataModelBuilder builder ) + { + var types = default( HashSet ); + + foreach ( var entitySet in builder.EntitySets ) + { + types ??= new(); + types.Add( entitySet.ClrType ); + } + + foreach ( var singleton in builder.Singletons ) + { + types ??= new(); + types.Add( singleton.ClrType ); + } + + return types; + } + + private void OnModelCreating( ODataModelBuilder builder ) + { + foreach ( var type in types ) + { + var entityType = builder.AddEntityType( type ); + builder.AddEntitySet( entityType.Name, entityType ); + } + } +} \ No newline at end of file diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs index bf2c4b6b..054f5641 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs @@ -135,7 +135,7 @@ public virtual void ApplyTo( IEnumerable apiDescriptions, ODataQ { var controller = GetController( description ); - if ( !controller.IsODataController() && !IsODataLike( description ) ) + if ( !controller.IsODataController() && !description.IsODataLike() ) { continue; } From 7c978b89d9814ec355e87853cf2ec59cbf570e88 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 6 Dec 2022 10:56:19 -0800 Subject: [PATCH 013/131] Add model bound settings to partial OData examples --- .../AspNet/OData/SomeOpenApiODataWebApiExample/Book.cs | 7 +++++++ .../AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs | 4 ++++ examples/AspNetCore/OData/SomeODataOpenApiExample/Book.cs | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Book.cs b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Book.cs index bd9fae58..a0a736b0 100644 --- a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Book.cs +++ b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Book.cs @@ -1,8 +1,15 @@ namespace ApiVersioning.Examples; +using Microsoft.AspNet.OData.Query; + +// TODO: Model Bound settings can be performed via attributes if the +// return type is known to the API Explorer or can be explicitly done +// via one or more IModelConfiguration implementations + /// /// Represents a book. /// +[Filter( "author", "published" )] public class Book { /// diff --git a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs index 73be263e..dca602ca 100644 --- a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs +++ b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs @@ -68,6 +68,10 @@ public void Configuration( IAppBuilder builder ) .Allow( Skip | Count ) .AllowTop( 100 ) .AllowOrderBy( "title", "published" ); + + // applies model bound settings implicitly using an ad hoc EDM. alternatively, you can create your own + // IModelConfiguration + IODataQueryOptionsConvention for full control over what goes in the ad hoc EDM. + options.AdHocModelBuilder.ModelConfigurations.Add( new ImplicitModelBoundSettingsConvention() ); } ); configuration.EnableSwagger( diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/Book.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/Book.cs index bd9fae58..c54dcba5 100644 --- a/examples/AspNetCore/OData/SomeODataOpenApiExample/Book.cs +++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/Book.cs @@ -1,8 +1,15 @@ namespace ApiVersioning.Examples; +using Microsoft.OData.ModelBuilder; + +// TODO: Model Bound settings can be performed via attributes if the +// return type is known to the API Explorer or can be explicitly done +// via one or more IModelConfiguration implementations + /// /// Represents a book. /// +[Filter( "author", "published" )] public class Book { /// From 1d5849528fe5d3695d99d2d0c07bce1c5d638b53 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 6 Dec 2022 13:55:12 -0800 Subject: [PATCH 014/131] Fix CodeQL violations --- .../PartialODataDescriptionProvider.cs | 31 +++++++++++++------ .../ImplicitModelBoundSettingsConvention.cs | 3 +- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs index 562a8469..31824b08 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs @@ -23,7 +23,7 @@ namespace Asp.Versioning.ApiExplorer; [CLSCompliant( false )] public class PartialODataDescriptionProvider : IApiDescriptionProvider { - private static int? beforeOData; + private static readonly int BeforeOData = ODataOrder() + 10; private readonly IOptionsFactory odataOptionsFactory; private readonly IOptions options; private bool markedAdHoc; @@ -42,8 +42,6 @@ public PartialODataDescriptionProvider( { this.odataOptionsFactory = odataOptionsFactory ?? throw new ArgumentNullException( nameof( odataOptionsFactory ) ); this.options = options ?? throw new ArgumentNullException( nameof( options ) ); - beforeOData ??= ODataOrder( odataOptionsFactory, options ) + 10; - Order = beforeOData.Value; } /// @@ -84,7 +82,7 @@ protected ODataApiExplorerOptions Options /// Gets or sets the order precedence of the current API description provider. /// /// The order precedence of the current API description provider. - public int Order { get; protected set; } + public int Order { get; protected set; } = BeforeOData; /// public virtual void OnProvidersExecuting( ApiDescriptionProviderContext context ) @@ -102,9 +100,9 @@ public virtual void OnProvidersExecuting( ApiDescriptionProviderContext context { var model = models[i]; var version = model.GetApiVersion(); - var options = odataOptionsFactory.Create( Opts.DefaultName ); + var odata = odataOptionsFactory.Create( Opts.DefaultName ); - options.AddRouteComponents( model ); + odata.AddRouteComponents( model ); for ( var j = 0; j < results.Count; j++ ) { @@ -139,12 +137,18 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context ) } [MethodImpl( MethodImplOptions.AggressiveInlining )] - private static int ODataOrder( IOptionsFactory factory, IOptions options ) => + private static int ODataOrder() => new ODataApiDescriptionProvider( new StubModelMetadataProvider(), new StubModelTypeBuilder(), - factory, - options ).Order; + new OptionsFactory( + Enumerable.Empty>(), + Enumerable.Empty>() ), + Opts.Create( + new ODataApiExplorerOptions( + new( + new StubODataApiVersionCollectionProvider(), + Enumerable.Empty() ) ) ) ).Order; [MethodImpl( MethodImplOptions.AggressiveInlining )] private static void MarkAsAdHoc( ODataModelBuilder builder, IEdmModel model ) => @@ -206,6 +210,15 @@ public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredTyp throw new NotImplementedException(); } + private sealed class StubODataApiVersionCollectionProvider : IODataApiVersionCollectionProvider + { + public IReadOnlyList ApiVersions + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + } + private static class ODataMetadata { private const string ArbitrarySegment = "52459ff8-bca1-4a26-b7f2-08c7da04472d"; diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs index 39dd9f3b..d9c6822f 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs @@ -75,9 +75,8 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string? rou private void OnModelCreating( ODataModelBuilder builder ) { - foreach ( var type in types ) + foreach ( var entityType in types.Select( builder.AddEntityType ) ) { - var entityType = builder.AddEntityType( type ); builder.AddEntitySet( entityType.Name, entityType ); } } From 33ec9e1ae4a4d1c6e9a5370e12c95716e5a0bd26 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 6 Dec 2022 15:47:49 -0800 Subject: [PATCH 015/131] Call base implementation. Fixes #932 --- .../AdvertiseApiVersionsAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs index eed5d62c..9d879940 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs @@ -97,5 +97,5 @@ public bool Deprecated } /// - public override int GetHashCode() => HashCode.Combine( GetHashCode(), Deprecated ); + public override int GetHashCode() => HashCode.Combine( base.GetHashCode(), Deprecated ); } \ No newline at end of file From 2c04584c864edcaffdc25792a0bb9911c7a1cbfe Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 6 Dec 2022 15:48:17 -0800 Subject: [PATCH 016/131] Run tests under 'en-US' locale --- .../ApiVersionFormatProviderTest.cs | 16 ++++++++ .../AssumeCultureAttribute.cs | 40 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/Abstractions/test/Asp.Versioning.Abstractions.Tests/AssumeCultureAttribute.cs diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionFormatProviderTest.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionFormatProviderTest.cs index 64b440a8..7b8d2245 100644 --- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionFormatProviderTest.cs +++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionFormatProviderTest.cs @@ -41,6 +41,7 @@ public void get_format_should_return_expected_format_provider() } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( FormatProvidersData ) )] public void format_should_allow_null_or_empty_format_string( ApiVersionFormatProvider provider ) { @@ -56,6 +57,7 @@ public void format_should_allow_null_or_empty_format_string( ApiVersionFormatPro } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( FormatProvidersData ) )] public void format_should_return_full_formatted_string_without_optional_components( ApiVersionFormatProvider provider ) { @@ -70,6 +72,7 @@ public void format_should_return_full_formatted_string_without_optional_componen } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( FormatProvidersData ) )] public void format_should_return_full_formatted_string_with_optional_components( ApiVersionFormatProvider provider ) { @@ -84,6 +87,7 @@ public void format_should_return_full_formatted_string_with_optional_components( } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( FormatProvidersData ) )] public void format_should_return_original_string_format_when_argument_cannot_be_formatted( ApiVersionFormatProvider provider ) { @@ -113,6 +117,7 @@ public void format_should_not_allow_malformed_literal_string( ApiVersionFormatPr } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( GroupVersionFormatData ) )] public void format_should_return_formatted_group_version_string( ApiVersionFormatProvider provider, string format ) { @@ -129,6 +134,7 @@ public void format_should_return_formatted_group_version_string( ApiVersionForma } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( FormatProvidersData ) )] public void format_should_return_formatted_minor_version_string( ApiVersionFormatProvider provider ) { @@ -143,6 +149,7 @@ public void format_should_return_formatted_minor_version_string( ApiVersionForma } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( FormatProvidersData ) )] public void format_should_return_formatted_major_version_string( ApiVersionFormatProvider provider ) { @@ -157,6 +164,7 @@ public void format_should_return_formatted_major_version_string( ApiVersionForma } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( FormatProvidersData ) )] public void format_should_return_formatted_major_and_minor_version_string( ApiVersionFormatProvider provider ) { @@ -171,6 +179,7 @@ public void format_should_return_formatted_major_and_minor_version_string( ApiVe } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( FormatProvidersData ) )] public void format_should_return_formatted_short_version_string( ApiVersionFormatProvider provider ) { @@ -185,6 +194,7 @@ public void format_should_return_formatted_short_version_string( ApiVersionForma } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( FormatProvidersData ) )] public void format_should_return_formatted_long_version_string( ApiVersionFormatProvider provider ) { @@ -199,6 +209,7 @@ public void format_should_return_formatted_long_version_string( ApiVersionFormat } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( FormatProvidersData ) )] public void format_should_return_formatted_status_string( ApiVersionFormatProvider provider ) { @@ -213,6 +224,7 @@ public void format_should_return_formatted_status_string( ApiVersionFormatProvid } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( PaddedMinorVersionFormatData ) )] public void format_should_return_formatted_minor_version_with_padding_string( ApiVersionFormatProvider provider, string format ) { @@ -233,6 +245,7 @@ public void format_should_return_formatted_minor_version_with_padding_string( Ap } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( PaddedMajorVersionFormatData ) )] public void format_should_return_formatted_major_version_with_padding_string( ApiVersionFormatProvider provider, string format ) { @@ -253,6 +266,7 @@ public void format_should_return_formatted_major_version_with_padding_string( Ap } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( CustomFormatData ) )] public void format_should_return_custom_format_string( Func format, string expected ) { @@ -268,6 +282,7 @@ public void format_should_return_custom_format_string( Func } [Theory] + [AssumeCulture( "en-us" )] [MemberData( nameof( MultipleFormatParameterData ) )] public void format_should_return_formatted_string_with_multiple_parameters( ApiVersionFormatProvider provider, string format, object secondArgument, string expected ) { @@ -284,6 +299,7 @@ public void format_should_return_formatted_string_with_multiple_parameters( ApiV } [Fact] + [AssumeCulture( "en-us" )] public void format_should_return_formatted_string_with_escape_sequence() { // arrange diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/AssumeCultureAttribute.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/AssumeCultureAttribute.cs new file mode 100644 index 00000000..9c956d87 --- /dev/null +++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/AssumeCultureAttribute.cs @@ -0,0 +1,40 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning; + +using System.Globalization; +using System.Reflection; +using Xunit.Sdk; +using static System.AttributeTargets; +using static System.Threading.Thread; + +/// +/// Allows a test method to assume that it is running in a specific locale. +/// +[AttributeUsage( Class | Method, AllowMultiple = false, Inherited = true )] +public sealed class AssumeCultureAttribute : BeforeAfterTestAttribute +{ + private CultureInfo originalCulture; + private CultureInfo originalUICulture; + + public AssumeCultureAttribute( string name ) => Name = name; + + public string Name { get; } + + public override void Before( MethodInfo methodUnderTest ) + { + originalCulture = CurrentThread.CurrentCulture; + originalUICulture = CurrentThread.CurrentUICulture; + + var culture = CultureInfo.CreateSpecificCulture( Name ); + + CurrentThread.CurrentCulture = culture; + CurrentThread.CurrentUICulture = culture; + } + + public override void After( MethodInfo methodUnderTest ) + { + CurrentThread.CurrentCulture = originalCulture; + CurrentThread.CurrentUICulture = originalUICulture; + } +} \ No newline at end of file From 26f80e58e854998885c6fe9959435e4748741e65 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 6 Dec 2022 18:56:34 -0800 Subject: [PATCH 017/131] Code cleanup from multiple, manual analysis runs --- .../SwaggerDefaultValues.cs | 10 ++-------- .../SwaggerDefaultValues.cs | 10 ++-------- .../OpenApiWebApiExample/SwaggerDefaultValues.cs | 10 ++-------- .../Configuration/OrderModelConfiguration.cs | 2 +- .../Configuration/PersonModelConfiguration.cs | 6 +++--- .../Configuration/OrderModelConfiguration.cs | 2 +- .../Configuration/PersonModelConfiguration.cs | 6 +++--- .../Configuration/OrderModelConfiguration.cs | 2 +- .../Configuration/PersonModelConfiguration.cs | 6 +++--- .../AdvertiseApiVersionsAttribute.cs | 5 ----- .../src/Asp.Versioning.Abstractions/ApiVersion.cs | 2 -- .../ApiVersionAttribute.cs | 5 ----- .../ApiVersionFormatProvider.cs | 2 +- .../ApiVersionModelDebugView.cs | 2 -- .../ApiVersionParser.cs | 1 - .../IApiVersionNeutral.cs | 1 - .../LinkHeaderValue.cs | 15 +++++++++------ .../MapToApiVersionAttribute.cs | 5 ----- .../NamespaceParser.cs | 2 +- .../src/Asp.Versioning.Abstractions/Str.cs | 2 +- .../net7.0/ApiVersion.cs | 2 -- .../Http/Basic/Controllers/OrdersController.cs | 2 ++ .../OverlappingRouteTemplateController.cs | 2 ++ .../Controllers/OrdersController.cs | 2 ++ .../Controllers/V1/OrdersController.cs | 2 ++ .../Controllers/WeatherForecastsController.cs | 1 - .../Configuration/CustomerModelConfiguration.cs | 2 -- .../Configuration/OrderModelConfiguration.cs | 2 -- .../Configuration/PersonModelConfiguration.cs | 2 -- .../WeatherForecastModelConfiguration.cs | 2 -- .../ODataValidationSettingsConventionTest.cs | 1 - .../Configuration/OrderModelConfiguration.cs | 2 -- .../Configuration/PersonModelConfiguration.cs | 2 -- .../Simulators/Models/Supplier.cs | 2 -- .../VersionedMetadataControllerTest.cs | 2 -- .../VersionedAttributeRoutingConventionTest.cs | 2 -- .../HttpConfigurationExtensionsTest.cs | 2 -- .../ApiVersionRequestProperties.cs | 2 -- .../Controllers/ActionSelectorCacheItem.cs | 3 --- .../MediaTypeApiVersionReaderBuilder.cs | 2 -- .../Routing/BoundRouteTemplateAdapter{T}.cs | 2 -- .../Routing/IBoundRouteTemplate.cs | 2 -- .../Asp.Versioning.WebApi/Routing/IPathSegment.cs | 2 -- .../Routing/IPathSeparatorSegment.cs | 2 -- .../Routing/IPathSubsegment.cs | 2 -- .../Routing/ParsedRouteAdapter{T}.cs | 2 -- .../Routing/PathContentSegmentAdapter{T}.cs | 2 -- .../Routing/PathLiteralSubsegmentAdapter{T}.cs | 2 -- .../Routing/PathParameterSubsegmentAdapter{T}.cs | 2 -- .../Routing/PathSeparatorSegmentAdapter{T}.cs | 2 -- .../HttpRequestMessageExtensions.cs | 4 ---- .../Simulators/ApiExplorerValuesController.cs | 1 - .../AttributeApiExplorerValuesController.cs | 1 - .../Simulators/AttributeValues2Controller.cs | 2 -- .../Simulators/AttributeValues3Controller.cs | 1 - .../Simulators/DuplicatedIdController.cs | 1 - .../Simulators/IgnoreApiValuesController.cs | 1 - .../Simulators/Values2Controller.cs | 3 +-- .../Simulators/Values3Controller.cs | 1 - ...ControllerSelectorTest.AmbiguousControllers.cs | 2 -- .../Simulators/ApiVersionedRoute2Controller.cs | 1 - .../Simulators/ApiVersionedRouteController.cs | 1 - .../Simulators/AttributeRoutedTest2Controller.cs | 1 - .../Simulators/AttributeRoutedTest4Controller.cs | 1 - .../Simulators/AttributeRoutedTestController.cs | 2 +- .../Simulators/ConventionsController.cs | 1 - .../Simulators/NeutralController.cs | 1 - .../Simulators/OrdersController.cs | 1 - .../Simulators/TestController.cs | 1 - .../Simulators/TestVersion2Controller.cs | 1 - .../Simulators/TestVersionNeutralController.cs | 1 - .../Controllers/OrdersController.cs | 2 ++ .../OverlappingRouteTemplateController.cs | 3 ++- .../Controllers/OrdersController.cs | 2 ++ .../Controllers/V1/OrdersController.cs | 1 - .../Controllers/V2/OrdersController.cs | 1 - .../when using a query string.cs | 1 - .../when using a url segment.cs | 1 - .../Configuration/CustomerModelConfiguration.cs | 2 -- .../Configuration/OrderModelConfiguration.cs | 2 -- .../Configuration/PersonModelConfiguration.cs | 2 -- .../WeatherForecastModelConfiguration.cs | 2 -- .../ODataQueryOptionsConventionBuilder.cs | 15 --------------- .../ODataValidationSettingsConventionTest.cs | 1 - .../Simulators/Configuration/AllConfigurations.cs | 2 -- .../Configuration/OrderModelConfiguration.cs | 2 -- .../Configuration/PersonModelConfiguration.cs | 2 -- .../Configuration/ProductConfiguration.cs | 2 -- .../Configuration/SupplierConfiguration.cs | 2 -- .../Simulators/Models/Supplier.cs | 2 -- .../ApiVersionMetadataCollationCollection.cs | 2 -- .../MediaTypeApiVersionReaderBuilder.cs | 2 -- .../Routing/ApiVersionMatcherPolicy.cs | 8 ++++++-- .../ApiVersionDescriptionProviderFactory.cs | 7 ++++--- .../Routing/ApiVersionUrlHelper.cs | 4 ---- .../ApiVersionEnumerator.cs | 2 -- .../ApiVersionHeaderEnumerable.cs | 1 - .../IHttpClientBuilderExtensionsTest.cs | 2 -- src/Common/src/Common.Backport/HashCode.cs | 9 --------- .../ActionApiVersionConventionBuilder{T}.cs | 2 -- .../ControllerApiVersionConventionBuilder{T}.cs | 2 -- .../DefaultODataQueryOptionDescriptionProvider.cs | 4 ---- ...ODataActionQueryOptionsConventionBuilder{T}.cs | 5 ++--- ...tionQueryOptionsConventionBuilderExtensions.cs | 4 ++-- .../ODataValidationSettingsConvention.cs | 2 -- .../Microsoft.OData.Edm/EdmExtensions.cs | 2 +- .../Common/MediaTypeApiVersionReaderBuilder.cs | 2 -- .../ActionApiVersionConventionBuilderTTest.cs | 1 - .../ActionApiVersionConventionBuilderTest.cs | 1 - .../ActionConventionBuilderExtensionsTest.cs | 5 ++--- .../ApiVersionConventionBuilderTest.cs | 2 -- .../ControllerApiVersionConventionBuilderTTest.cs | 1 - .../ControllerApiVersionConventionBuilderTest.cs | 1 - .../ControllerConventionBuilderExtensionsTest.cs | 5 ++--- ...QueryOptionsConventionBuilderExtensionsTest.cs | 6 +++--- ...ataActionQueryOptionsConventionBuilderTTest.cs | 2 -- ...DataActionQueryOptionsConventionBuilderTest.cs | 2 -- .../ODataQueryOptionsConventionBuilderTest.cs | 2 -- .../OData/Company.cs | 3 --- .../OData/Contact.cs | 3 --- .../OData/Employer.cs | 2 -- 121 files changed, 68 insertions(+), 259 deletions(-) diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/SwaggerDefaultValues.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/SwaggerDefaultValues.cs index 67cd7607..2ce56dcf 100644 --- a/examples/AspNet/OData/OpenApiODataWebApiExample/SwaggerDefaultValues.cs +++ b/examples/AspNet/OData/OpenApiODataWebApiExample/SwaggerDefaultValues.cs @@ -30,17 +30,11 @@ public void Apply( Operation operation, SchemaRegistry schemaRegistry, ApiDescri var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.name ); // REF: https://github.com/domaindrivendev/Swashbuckle/issues/1101 - if ( parameter.description == null ) - { - parameter.description = description.Documentation; - } + parameter.description ??= description.Documentation; // REF: https://github.com/domaindrivendev/Swashbuckle/issues/1089 // REF: https://github.com/domaindrivendev/Swashbuckle/pull/1090 - if ( parameter.@default == null ) - { - parameter.@default = description.ParameterDescriptor?.DefaultValue; - } + parameter.@default ??= description.ParameterDescriptor?.DefaultValue; } } } \ No newline at end of file diff --git a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/SwaggerDefaultValues.cs b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/SwaggerDefaultValues.cs index 67cd7607..2ce56dcf 100644 --- a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/SwaggerDefaultValues.cs +++ b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/SwaggerDefaultValues.cs @@ -30,17 +30,11 @@ public void Apply( Operation operation, SchemaRegistry schemaRegistry, ApiDescri var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.name ); // REF: https://github.com/domaindrivendev/Swashbuckle/issues/1101 - if ( parameter.description == null ) - { - parameter.description = description.Documentation; - } + parameter.description ??= description.Documentation; // REF: https://github.com/domaindrivendev/Swashbuckle/issues/1089 // REF: https://github.com/domaindrivendev/Swashbuckle/pull/1090 - if ( parameter.@default == null ) - { - parameter.@default = description.ParameterDescriptor?.DefaultValue; - } + parameter.@default ??= description.ParameterDescriptor?.DefaultValue; } } } \ No newline at end of file diff --git a/examples/AspNet/WebApi/OpenApiWebApiExample/SwaggerDefaultValues.cs b/examples/AspNet/WebApi/OpenApiWebApiExample/SwaggerDefaultValues.cs index 2fa156c5..dc26ff66 100644 --- a/examples/AspNet/WebApi/OpenApiWebApiExample/SwaggerDefaultValues.cs +++ b/examples/AspNet/WebApi/OpenApiWebApiExample/SwaggerDefaultValues.cs @@ -30,17 +30,11 @@ public void Apply( Operation operation, SchemaRegistry schemaRegistry, ApiDescri var description = apiDescription.ParameterDescriptions.First( p => p.Name == parameter.name ); // REF: https://github.com/domaindrivendev/Swashbuckle/issues/1101 - if ( parameter.description == null ) - { - parameter.description = description.Documentation; - } + parameter.description ??= description.Documentation; // REF: https://github.com/domaindrivendev/Swashbuckle/issues/1089 // REF: https://github.com/domaindrivendev/Swashbuckle/pull/1090 - if ( parameter.@default == null ) - { - parameter.@default = description.ParameterDescriptor?.DefaultValue; - } + parameter.@default ??= description.ParameterDescriptor?.DefaultValue; } } } \ No newline at end of file diff --git a/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/OrderModelConfiguration.cs b/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/OrderModelConfiguration.cs index af356c02..b0a79db7 100644 --- a/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/OrderModelConfiguration.cs +++ b/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/OrderModelConfiguration.cs @@ -9,7 +9,7 @@ public class OrderModelConfiguration : IModelConfiguration { private static readonly ApiVersion V2 = new( 2, 0 ); - private EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder ) + private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder ) { var order = builder.EntitySet( "Orders" ).EntityType; diff --git a/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/PersonModelConfiguration.cs b/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/PersonModelConfiguration.cs index 0ab0aed8..f462cb9a 100644 --- a/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/PersonModelConfiguration.cs +++ b/examples/AspNetCore/OData/ODataAdvancedExample/Configuration/PersonModelConfiguration.cs @@ -7,16 +7,16 @@ public class PersonModelConfiguration : IModelConfiguration { - private void ConfigureV1( ODataModelBuilder builder ) + private static void ConfigureV1( ODataModelBuilder builder ) { var person = ConfigureCurrent( builder ); person.Ignore( p => p.Email ); person.Ignore( p => p.Phone ); } - private void ConfigureV2( ODataModelBuilder builder ) => ConfigureCurrent( builder ).Ignore( p => p.Phone ); + private static void ConfigureV2( ODataModelBuilder builder ) => ConfigureCurrent( builder ).Ignore( p => p.Phone ); - private EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder ) + private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder ) { var person = builder.EntitySet( "People" ).EntityType; diff --git a/examples/AspNetCore/OData/ODataBasicExample/Configuration/OrderModelConfiguration.cs b/examples/AspNetCore/OData/ODataBasicExample/Configuration/OrderModelConfiguration.cs index 26573aec..c757921a 100644 --- a/examples/AspNetCore/OData/ODataBasicExample/Configuration/OrderModelConfiguration.cs +++ b/examples/AspNetCore/OData/ODataBasicExample/Configuration/OrderModelConfiguration.cs @@ -9,7 +9,7 @@ public class OrderModelConfiguration : IModelConfiguration { private static readonly ApiVersion V1 = new( 1, 0 ); - private EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder ) + private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder ) { var order = builder.EntitySet( "Orders" ).EntityType; diff --git a/examples/AspNetCore/OData/ODataBasicExample/Configuration/PersonModelConfiguration.cs b/examples/AspNetCore/OData/ODataBasicExample/Configuration/PersonModelConfiguration.cs index a0d2c5f8..5ebaa0db 100644 --- a/examples/AspNetCore/OData/ODataBasicExample/Configuration/PersonModelConfiguration.cs +++ b/examples/AspNetCore/OData/ODataBasicExample/Configuration/PersonModelConfiguration.cs @@ -7,16 +7,16 @@ public class PersonModelConfiguration : IModelConfiguration { - private void ConfigureV1( ODataModelBuilder builder ) + private static void ConfigureV1( ODataModelBuilder builder ) { var person = ConfigureCurrent( builder ); person.Ignore( p => p.Email ); person.Ignore( p => p.Phone ); } - private void ConfigureV2( ODataModelBuilder builder ) => ConfigureCurrent( builder ).Ignore( p => p.Phone ); + private static void ConfigureV2( ODataModelBuilder builder ) => ConfigureCurrent( builder ).Ignore( p => p.Phone ); - private EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder ) + private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder ) { var person = builder.EntitySet( "People" ).EntityType; diff --git a/examples/AspNetCore/OData/ODataConventionsExample/Configuration/OrderModelConfiguration.cs b/examples/AspNetCore/OData/ODataConventionsExample/Configuration/OrderModelConfiguration.cs index 26573aec..c757921a 100644 --- a/examples/AspNetCore/OData/ODataConventionsExample/Configuration/OrderModelConfiguration.cs +++ b/examples/AspNetCore/OData/ODataConventionsExample/Configuration/OrderModelConfiguration.cs @@ -9,7 +9,7 @@ public class OrderModelConfiguration : IModelConfiguration { private static readonly ApiVersion V1 = new( 1, 0 ); - private EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder ) + private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder ) { var order = builder.EntitySet( "Orders" ).EntityType; diff --git a/examples/AspNetCore/OData/ODataConventionsExample/Configuration/PersonModelConfiguration.cs b/examples/AspNetCore/OData/ODataConventionsExample/Configuration/PersonModelConfiguration.cs index a0d2c5f8..5ebaa0db 100644 --- a/examples/AspNetCore/OData/ODataConventionsExample/Configuration/PersonModelConfiguration.cs +++ b/examples/AspNetCore/OData/ODataConventionsExample/Configuration/PersonModelConfiguration.cs @@ -7,16 +7,16 @@ public class PersonModelConfiguration : IModelConfiguration { - private void ConfigureV1( ODataModelBuilder builder ) + private static void ConfigureV1( ODataModelBuilder builder ) { var person = ConfigureCurrent( builder ); person.Ignore( p => p.Email ); person.Ignore( p => p.Phone ); } - private void ConfigureV2( ODataModelBuilder builder ) => ConfigureCurrent( builder ).Ignore( p => p.Phone ); + private static void ConfigureV2( ODataModelBuilder builder ) => ConfigureCurrent( builder ).Ignore( p => p.Phone ); - private EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder ) + private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilder builder ) { var person = builder.EntitySet( "People" ).EntityType; diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs index 9d879940..195fa7ea 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs @@ -4,9 +4,6 @@ namespace Asp.Versioning; using static System.AttributeTargets; -#pragma warning disable CA1019 -#pragma warning disable CA1813 - /// /// Represents the metadata that describes the advertised API versions. /// @@ -71,9 +68,7 @@ public AdvertiseApiVersionsAttribute( string version ) : base( version ) { } public AdvertiseApiVersionsAttribute( string version, params string[] otherVersions ) : base( version, otherVersions ) { } -#pragma warning disable CA1033 // Interface methods should be callable by child types ApiVersionProviderOptions IApiVersionProvider.Options => options; -#pragma warning restore CA1033 // Interface methods should be callable by child types /// /// Gets or sets a value indicating whether the specified set of API versions are deprecated. diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersion.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersion.cs index 3edda7f0..e7fe068d 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersion.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersion.cs @@ -341,9 +341,7 @@ public virtual int CompareTo( ApiVersion? other ) public virtual string ToString( string? format, IFormatProvider? formatProvider ) { var provider = ApiVersionFormatProvider.GetInstance( formatProvider ); -#pragma warning disable CA1062 // Validate arguments of public methods return provider.Format( format, this, formatProvider ); -#pragma warning restore CA1062 // Validate arguments of public methods } private static string? ValidateStatus( string? status, Func isValid ) diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionAttribute.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionAttribute.cs index 2d56db41..bbdff81b 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionAttribute.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionAttribute.cs @@ -4,9 +4,6 @@ namespace Asp.Versioning; using static System.AttributeTargets; -#pragma warning disable CA1019 -#pragma warning disable CA1813 - /// /// Represents the metadata that describes the versions associated with an API. /// @@ -42,9 +39,7 @@ protected ApiVersionAttribute( IApiVersionParser parser, string version ) : base /// The API version string. public ApiVersionAttribute( string version ) : base( version ) { } -#pragma warning disable CA1033 // Interface methods should be callable by child types ApiVersionProviderOptions IApiVersionProvider.Options => options; -#pragma warning restore CA1033 // Interface methods should be callable by child types /// /// Gets or sets a value indicating whether the specified set of API versions are deprecated. diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionFormatProvider.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionFormatProvider.cs index 92e472ab..9879d007 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionFormatProvider.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionFormatProvider.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. #pragma warning disable IDE0079 -#pragma warning disable SA1121 // Use built-in type alias +#pragma warning disable SA1121 namespace Asp.Versioning; diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelDebugView.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelDebugView.cs index 4d0c3ddb..632500d0 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelDebugView.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelDebugView.cs @@ -2,8 +2,6 @@ namespace Asp.Versioning; -#pragma warning disable CA1812 - using static System.String; internal sealed class ApiVersionModelDebugView diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs index 8bd12675..f35292f9 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs @@ -2,7 +2,6 @@ #pragma warning disable IDE0079 #pragma warning disable SA1121 -#pragma warning disable SA1114 // Parameter list should follow declaration namespace Asp.Versioning; diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionNeutral.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionNeutral.cs index a3ba5fd2..cc4c27a3 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionNeutral.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionNeutral.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. namespace Asp.Versioning; -#pragma warning disable CA1040 /// /// Defines the behavior of an API that is version-neutral. diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs index fed3d08e..eeec87a0 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs @@ -1,5 +1,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 +#pragma warning disable SA1121 + namespace Asp.Versioning; #if !NETSTANDARD1_0 @@ -13,7 +16,6 @@ namespace Asp.Versioning; #endif #pragma warning disable IDE0079 -#pragma warning disable SA1121 /// /// Represents a HTTP Link header value. @@ -570,13 +572,11 @@ public IEnumerator> GetEnumerator() } // REF: https://datatracker.ietf.org/doc/html/rfc8288#appendix-B.3 #9 -#pragma warning disable CA1308 // Normalize strings to uppercase #if NETSTANDARD1_0 var key = remaining.Substring( start, end - start ).ToLowerInvariant(); #else var key = new StringSegment( remaining.Substring( start, end - start ).ToLowerInvariant() ); #endif -#pragma warning restore CA1308 // Normalize strings to uppercase start = end; ConsumeWhitespace(); @@ -649,10 +649,13 @@ private static StringSegment RemoveQuotes( StringSegment input ) return input; } +#pragma warning disable IDE0056 // Use index operator private static bool IsQuoted( StringSegment input ) => -#pragma warning disable IDE0056 - !StringSegment.IsNullOrEmpty( input ) && input.Length >= 2 && input[0] == '"' && input[input.Length - 1] == '"'; -#pragma warning restore IDE0056 + !StringSegment.IsNullOrEmpty( input ) && + input.Length >= 2 && + input[0] == '"' && + input[input.Length - 1] == '"'; +#pragma warning restore IDE0056 // Use index operator private static StringSegment UnescapeAsQuotedString( StringSegment input ) { diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/MapToApiVersionAttribute.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/MapToApiVersionAttribute.cs index 35c9aeb9..3d7050cc 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/MapToApiVersionAttribute.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/MapToApiVersionAttribute.cs @@ -4,9 +4,6 @@ namespace Asp.Versioning; using static System.AttributeTargets; -#pragma warning disable CA1019 -#pragma warning disable CA1813 - /// /// Represents the metadata that describes the version-specific implementation of an API. /// @@ -38,7 +35,5 @@ public MapToApiVersionAttribute( double version ) : base( version ) { } /// The API version string. public MapToApiVersionAttribute( string version ) : base( version ) { } -#pragma warning disable CA1033 ApiVersionProviderOptions IApiVersionProvider.Options => ApiVersionProviderOptions.Mapped; -#pragma warning restore CA1033 } \ No newline at end of file diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/NamespaceParser.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/NamespaceParser.cs index 46d64d23..d857297b 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/NamespaceParser.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/NamespaceParser.cs @@ -1,8 +1,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. #pragma warning disable IDE0079 +#pragma warning disable SA1114 #pragma warning disable SA1121 -#pragma warning disable SA1114 // Parameter list should follow declaration namespace Asp.Versioning; diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Str.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/Str.cs index 850e5049..0c899a4b 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/Str.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Str.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. #pragma warning disable IDE0079 -#pragma warning disable SA1121 // Use built-in type alias +#pragma warning disable SA1121 namespace Asp.Versioning; diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs index 2a2db484..bd7c0682 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs @@ -11,8 +11,6 @@ public partial class ApiVersion : ISpanFormattable public virtual bool TryFormat( Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider ) { var instance = ApiVersionFormatProvider.GetInstance( provider ); -#pragma warning disable CA1062 // Validate arguments of public methods return instance.TryFormat( destination, out charsWritten, format, this, provider ); -#pragma warning restore CA1062 // Validate arguments of public methods } } \ No newline at end of file diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OrdersController.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OrdersController.cs index 84f14bd6..d9ffd452 100644 --- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OrdersController.cs +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OrdersController.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0060 + namespace Asp.Versioning.Http.Basic.Controllers; using Asp.Versioning.Http.Basic.Models; diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OverlappingRouteTemplateController.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OverlappingRouteTemplateController.cs index 320ec366..4907c19e 100644 --- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OverlappingRouteTemplateController.cs +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/Controllers/OverlappingRouteTemplateController.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0060 + namespace Asp.Versioning.Http.Basic.Controllers; using System.Web.Http; diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingConventions/Controllers/OrdersController.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingConventions/Controllers/OrdersController.cs index 7e6d3e27..1b52ff0d 100644 --- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingConventions/Controllers/OrdersController.cs +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingConventions/Controllers/OrdersController.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0060 + namespace Asp.Versioning.Http.UsingConventions.Controllers; using Asp.Versioning.Http.UsingConventions.Models; diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/Controllers/V1/OrdersController.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/Controllers/V1/OrdersController.cs index 8e19bda5..d547a271 100644 --- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/Controllers/V1/OrdersController.cs +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/Controllers/V1/OrdersController.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0060 + namespace Asp.Versioning.Http.UsingNamespace.Controllers.V1; using Asp.Versioning.Http.UsingNamespace.Models; diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs index 26c45c91..3d05e49f 100644 --- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. #pragma warning disable IDE0060 // Remove unused parameter -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.OData.Basic.Controllers; diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs index 1515dd37..27b3e2d9 100644 --- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.OData.Configuration; using Asp.Versioning.OData.Models; diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs index ed7eb5a7..05ec159e 100644 --- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.OData.Configuration; using Asp.Versioning.OData.Models; diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs index 1ebc086a..302e5c38 100644 --- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.OData.Configuration; using Asp.Versioning.OData.Models; diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs index 83609324..357bb455 100644 --- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.OData.Configuration; using Asp.Versioning.OData.Models; diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs index 1a255dad..b31d7496 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs @@ -604,7 +604,6 @@ private static ApiDescription NewApiDescription( Type controllerType, Type respo } #pragma warning disable IDE0060 // Remove unused parameter -#pragma warning disable CA1034 // Nested types should not be visible public class SinglePartController : ODataController { diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs index 7d892736..2e0352bb 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.Simulators.Configuration; using Asp.Versioning.OData; diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs index 6dd8b03a..9b04a783 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.Simulators.Configuration; using Asp.Versioning.OData; diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs index 83a08d66..19470142 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA2227 // Collection properties should be read only - namespace Asp.Versioning.Simulators.Models; public class Supplier diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs index 236fa169..60292852 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs @@ -81,8 +81,6 @@ public IEnumerable GetServices( Type serviceType ) } } -#pragma warning disable CA1812 - [ApiVersion( "1.0" )] [ApiVersion( "2.0" )] private sealed class Controller1 : ODataController diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs index 0756895b..a42188cc 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs @@ -44,8 +44,6 @@ public void should_map_controller_should_return_expected_result_for_controller_v result.Should().BeTrue(); } -#pragma warning disable CA1812 - [ApiVersionNeutral] private sealed class NeutralController : ODataController { } diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs index 37fece37..4544139c 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs @@ -94,8 +94,6 @@ private static IReadOnlyList GetRoutingConventions( Htt return routingConventions.ToArray(); } -#pragma warning disable CA1812 - [ApiVersion( "1.0" )] private sealed class ControllerV1 : ODataController { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ApiVersionRequestProperties.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ApiVersionRequestProperties.cs index 98adf3be..284c66ba 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ApiVersionRequestProperties.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ApiVersionRequestProperties.cs @@ -55,11 +55,9 @@ public string? RawRequestedApiVersion { 0 => default, 1 => values[0], -#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations; existing behavior via IApiVersionReader.Read _ => throw new AmbiguousApiVersionException( string.Format( CultureInfo.CurrentCulture, CommonSR.MultipleDifferentApiVersionsRequested, string.Join( ", ", values ) ), values ), -#pragma warning restore CA1065 }; } set diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs index 28f69ac4..0acd241f 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs @@ -153,8 +153,6 @@ private ActionSelectionResult FindAction( bool ignoreSubRoutes ) { var selectedCandidates = FindMatchingActions( controllerContext, ignoreSubRoutes ); - -#pragma warning disable CA2000 // Dispose objects before losing scope if ( selectedCandidates.Count == 0 ) { return new( new HttpResponseException( CreateSelectionError( controllerContext ) ) ); @@ -170,7 +168,6 @@ private ActionSelectionResult FindAction( { return new( new HttpResponseException( CreateSelectionError( controllerContext ) ) ); } -#pragma warning restore CA2000 // Dispose objects before losing scope var ambiguityList = CreateAmbiguousMatchList( selectedCandidates ); var message = string.Format( CultureInfo.CurrentCulture, SR.ApiControllerActionSelector_AmbiguousMatch, ambiguityList ); diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs index 11f0fcb3..78f24891 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs @@ -20,9 +20,7 @@ public partial class MediaTypeApiVersionReaderBuilder /// If a value is not specified, there is expected to be a single template parameter. /// The current . /// The template syntax is the same used by route templates; however, constraints are not supported. -#pragma warning disable CA1716 // Identifiers should not match keywords public virtual MediaTypeApiVersionReaderBuilder Template( string template, string? parameterName = default ) -#pragma warning restore CA1716 // Identifiers should not match keywords { if ( string.IsNullOrEmpty( template ) ) { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/BoundRouteTemplateAdapter{T}.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/BoundRouteTemplateAdapter{T}.cs index a5c6446b..8d5de8fb 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/BoundRouteTemplateAdapter{T}.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/BoundRouteTemplateAdapter{T}.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1812 - namespace Asp.Versioning.Routing; using System.Web.Http.Routing; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IBoundRouteTemplate.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IBoundRouteTemplate.cs index 6bd7dced..c2ce56f5 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IBoundRouteTemplate.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IBoundRouteTemplate.cs @@ -15,11 +15,9 @@ public interface IBoundRouteTemplate /// The bound template. string BoundTemplate { get; set; } -#pragma warning disable CA2227 // Collection properties should be read only /// /// Gets or sets the template parameter values. /// /// The template route value dictionary. HttpRouteValueDictionary Values { get; set; } -#pragma warning restore CA2227 } \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSegment.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSegment.cs index 5a57da6d..d6e6e455 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSegment.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSegment.cs @@ -2,8 +2,6 @@ namespace Asp.Versioning.Routing; -#pragma warning disable CA1040 // Avoid empty interfaces - /// /// Defines the behavior of a path segment. /// diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSeparatorSegment.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSeparatorSegment.cs index d57a4a7b..ca2829d6 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSeparatorSegment.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSeparatorSegment.cs @@ -2,8 +2,6 @@ namespace Asp.Versioning.Routing; -#pragma warning disable CA1040 // Avoid empty interfaces - /// /// Defines the behavior of a path separator. /// diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSubsegment.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSubsegment.cs index 8a180b7c..49eec855 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSubsegment.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/IPathSubsegment.cs @@ -2,8 +2,6 @@ namespace Asp.Versioning.Routing; -#pragma warning disable CA1040 // Avoid empty interfaces - /// /// Defines the behavior of a path subsegment. /// diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ParsedRouteAdapter{T}.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ParsedRouteAdapter{T}.cs index 28148ce3..76ac7302 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ParsedRouteAdapter{T}.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ParsedRouteAdapter{T}.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1812 - namespace Asp.Versioning.Routing; using System.Reflection; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathContentSegmentAdapter{T}.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathContentSegmentAdapter{T}.cs index 77f350fa..7f62ae53 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathContentSegmentAdapter{T}.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathContentSegmentAdapter{T}.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1812 - namespace Asp.Versioning.Routing; using static System.Linq.Expressions.Expression; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathLiteralSubsegmentAdapter{T}.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathLiteralSubsegmentAdapter{T}.cs index 65e1bd05..a565ba0e 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathLiteralSubsegmentAdapter{T}.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathLiteralSubsegmentAdapter{T}.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1812 - namespace Asp.Versioning.Routing; using static System.Linq.Expressions.Expression; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathParameterSubsegmentAdapter{T}.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathParameterSubsegmentAdapter{T}.cs index f51c711c..5250bfe1 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathParameterSubsegmentAdapter{T}.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathParameterSubsegmentAdapter{T}.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1812 - namespace Asp.Versioning.Routing; using static System.Linq.Expressions.Expression; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathSeparatorSegmentAdapter{T}.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathSeparatorSegmentAdapter{T}.cs index 9c16ab59..4d94a78d 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathSeparatorSegmentAdapter{T}.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/PathSeparatorSegmentAdapter{T}.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1812 - namespace Asp.Versioning.Routing; internal sealed class PathSeparatorSegmentAdapter : IPathSeparatorSegment where T : notnull diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs index 25abca3a..cd9ac2a7 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs @@ -22,9 +22,7 @@ private static HttpResponseMessage CreateErrorResponse( this HttpRequestMessage if ( configuration == null ) { -#pragma warning disable CA2000 // Dispose objects before losing scope configuration = new HttpConfiguration(); -#pragma warning restore CA2000 // Dispose objects before losing scope request.RegisterForDispose( configuration ); request.SetConfiguration( configuration ); } @@ -60,9 +58,7 @@ public static ApiVersioningOptions GetApiVersioningOptions( this HttpRequestMess if ( configuration == null ) { -#pragma warning disable CA2000 // Dispose objects before losing scope configuration = new HttpConfiguration(); -#pragma warning restore CA2000 // Dispose objects before losing scope request.RegisterForDispose( configuration ); request.SetConfiguration( configuration ); } diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/ApiExplorerValuesController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/ApiExplorerValuesController.cs index 9a0218d1..c75b460b 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/ApiExplorerValuesController.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/ApiExplorerValuesController.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeApiExplorerValuesController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeApiExplorerValuesController.cs index 7e156d55..62240c77 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeApiExplorerValuesController.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeApiExplorerValuesController.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues2Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues2Controller.cs index a7bb3f20..709b16db 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues2Controller.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues2Controller.cs @@ -1,8 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. #pragma warning disable IDE0060 -#pragma warning disable CA1062 // Validate arguments of public methods -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues3Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues3Controller.cs index d25f7216..1c17d4bf 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues3Controller.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/AttributeValues3Controller.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. #pragma warning disable IDE0060 -#pragma warning disable CA1062 // Validate arguments of public methods namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/DuplicatedIdController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/DuplicatedIdController.cs index f6b40d55..1c401fc1 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/DuplicatedIdController.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/DuplicatedIdController.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. #pragma warning disable IDE0060 -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/IgnoreApiValuesController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/IgnoreApiValuesController.cs index 734d9990..2107688b 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/IgnoreApiValuesController.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/IgnoreApiValuesController.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values2Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values2Controller.cs index a1b8b3b5..796aba7c 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values2Controller.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values2Controller.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods -#pragma warning disable CA1822 // Mark members as static +#pragma warning disable IDE0060 namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values3Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values3Controller.cs index 69e8b09a..04f09f1c 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values3Controller.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Simulators/Values3Controller.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. #pragma warning disable IDE0060 -#pragma warning disable CA1062 // Validate arguments of public methods namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.AmbiguousControllers.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.AmbiguousControllers.cs index 80957cb1..15512777 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.AmbiguousControllers.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.AmbiguousControllers.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1812 -#pragma warning disable CA1822 // Mark members as static #pragma warning disable SA1601 // Partial elements should be documented namespace Asp.Versioning.Dispatcher; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRoute2Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRoute2Controller.cs index 51c53117..34fc582c 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRoute2Controller.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRoute2Controller.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRouteController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRouteController.cs index 39a16df1..1cc6df30 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRouteController.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ApiVersionedRouteController.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest2Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest2Controller.cs index c0af43b2..257bbc60 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest2Controller.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest2Controller.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest4Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest4Controller.cs index 84b21dae..f31b3581 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest4Controller.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTest4Controller.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTestController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTestController.cs index 11374a40..bf3e867c 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTestController.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AttributeRoutedTestController.cs @@ -1,6 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static +#pragma warning disable IDE0060 namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ConventionsController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ConventionsController.cs index 5053fc3d..c7cd7e91 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ConventionsController.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/ConventionsController.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/NeutralController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/NeutralController.cs index 18d1a70a..8101ce4f 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/NeutralController.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/NeutralController.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/OrdersController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/OrdersController.cs index cc482c4a..1d42a805 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/OrdersController.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/OrdersController.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1707 // Identifiers should not contain underscores namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestController.cs index a141a1e6..3c145fb1 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestController.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestController.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersion2Controller.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersion2Controller.cs index c6c87bdf..89d77a80 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersion2Controller.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersion2Controller.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersionNeutralController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersionNeutralController.cs index 89369fe3..d9ceb578 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersionNeutralController.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/TestVersionNeutralController.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.Simulators; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OrdersController.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OrdersController.cs index 2d94fbaa..5fc09eb7 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OrdersController.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OrdersController.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0060 + namespace Asp.Versioning.Mvc.UsingAttributes.Controllers; using Asp.Versioning.Mvc.UsingAttributes.Models; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OverlappingRouteTemplateController.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OverlappingRouteTemplateController.cs index da532780..f028c66f 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OverlappingRouteTemplateController.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/Controllers/OverlappingRouteTemplateController.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1822 // Mark members as static +#pragma warning disable CA1822 +#pragma warning disable IDE0060 namespace Asp.Versioning.Mvc.UsingAttributes.Controllers; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingConventions/Controllers/OrdersController.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingConventions/Controllers/OrdersController.cs index 8d1d1a34..8c481dc2 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingConventions/Controllers/OrdersController.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingConventions/Controllers/OrdersController.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0060 // Remove unused parameter + namespace Asp.Versioning.Mvc.UsingConventions.Controllers; using Asp.Versioning.Mvc.UsingConventions.Models; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V1/OrdersController.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V1/OrdersController.cs index 1006619f..8b935d1e 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V1/OrdersController.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V1/OrdersController.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1716 // Identifiers should not match keywords namespace Asp.Versioning.Mvc.UsingNamespace.Controllers.V1; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V2/OrdersController.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V2/OrdersController.cs index 0d7e007a..9804051b 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V2/OrdersController.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingNamespace/Controllers/V2/OrdersController.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1716 // Identifiers should not match keywords namespace Asp.Versioning.Mvc.UsingNamespace.Controllers.V2; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a query string.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a query string.cs index e85c1b9b..6b63fd64 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a query string.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a query string.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA2000 // Dispose objects before losing scope namespace given_versioned_batch_middleware; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a url segment.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a url segment.cs index cb9cc0fa..560cd298 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a url segment.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a url segment.cs @@ -1,6 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA2000 // Dispose objects before losing scope namespace given_versioned_batch_middleware; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs index e9f86835..4f48ea20 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.OData.Configuration; using Asp.Versioning.OData.Models; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs index 6cb4a514..5d5655a9 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.OData.Configuration; using Asp.Versioning.OData.Models; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs index 46e67287..b3fb9f01 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.OData.Configuration; using Asp.Versioning.OData.Models; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs index 62a23acc..c53de03f 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.OData.Configuration; using Asp.Versioning.OData.Models; diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs index 9a91c9fa..6986e816 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs @@ -20,19 +20,4 @@ private static Type GetController( ApiDescription apiDescription ) return typeof( object ); } - - private static bool IsODataLike( ApiDescription description ) - { - var parameters = description.ActionDescriptor.Parameters; - - for ( var i = 0; i < parameters.Count; i++ ) - { - if ( parameters[i].ParameterType.IsODataQueryOptions() ) - { - return true; - } - } - - return false; - } } \ No newline at end of file diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs index e5cd95ce..36ed3198 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs @@ -637,7 +637,6 @@ private static ApiDescription NewApiDescription( Type controllerType, Type respo } #pragma warning disable IDE0060 // Remove unused parameter -#pragma warning disable CA1034 // Nested types should not be visible public class SinglePartController : ODataController { diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs index d29fe190..fcbda180 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.Simulators.Configuration; using Asp.Versioning.OData; diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs index bfbe387d..6542f84f 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.Simulators.Configuration; using Asp.Versioning.OData; diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs index bd869bff..dd2f04ce 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.Simulators.Configuration; using Asp.Versioning.OData; diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs index b210dbdb..b1741080 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.Simulators.Configuration; using Asp.Versioning.OData; diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs index 7ce101b7..3b9d0f45 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1062 // Validate arguments of public methods - namespace Asp.Versioning.Simulators.Configuration; using Asp.Versioning.OData; diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs index 83a08d66..19470142 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA2227 // Collection properties should be read only - namespace Asp.Versioning.Simulators.Models; public class Supplier diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs index ee58aed9..0d728c19 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs @@ -47,9 +47,7 @@ ApiVersionMetadata IList.this[int index] /// public int Count => items.Count; -#pragma warning disable CA1033 // Interface methods should be callable by child types bool ICollection.IsReadOnly => ( (ICollection) items ).IsReadOnly; -#pragma warning restore CA1033 // Interface methods should be callable by child types /// public void Add( ApiVersionMetadata item ) => Insert( Count, item, default ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs index 45655770..22eeef58 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs @@ -22,9 +22,7 @@ public partial class MediaTypeApiVersionReaderBuilder /// If a value is not specified, there is expected to be a single template parameter. /// The current . /// The template syntax is the same used by route templates; however, constraints are not supported. -#pragma warning disable CA1716 // Identifiers should not match keywords public virtual MediaTypeApiVersionReaderBuilder Template( string template, string? parameterName = default ) -#pragma warning restore CA1716 // Identifiers should not match keywords { if ( string.IsNullOrEmpty( template ) ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs index f4cb96e9..1ba4358e 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs @@ -13,12 +13,13 @@ namespace Asp.Versioning.Routing; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using static Asp.Versioning.ApiVersionMapping; +using static System.Text.RegularExpressions.RegexOptions; /// /// Represents the matcher policy for API versions. /// [CLSCompliant( false )] -public sealed class ApiVersionMatcherPolicy : MatcherPolicy, IEndpointSelectorPolicy, INodeBuilderPolicy +public sealed partial class ApiVersionMatcherPolicy : MatcherPolicy, IEndpointSelectorPolicy, INodeBuilderPolicy { private readonly IOptions options; private readonly IApiVersionParser apiVersionParser; @@ -274,7 +275,7 @@ private static bool DifferByRouteConstraintsOnly( CandidateSet candidates ) // // but 3.0 is requested, 400 should be returned if we made it this far const string ReplacementPattern = "{$1}"; - var pattern = new Regex( "{([^:]+):[^}]+}", RegexOptions.Singleline | RegexOptions.IgnoreCase ); + var pattern = RouteConstraintRegex(); var comparer = StringComparer.OrdinalIgnoreCase; string? template = default; string? normalizedTemplate = default; @@ -597,4 +598,7 @@ private static int ComputeVersion( IApiVersionMetadataCollationProvider[] provid return hash.ToHashCode(); } } + + [GeneratedRegex( "{([^:]+):[^}]+}", IgnoreCase | Singleline )] + private static partial Regex RouteConstraintRegex(); } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs index cd6e09c4..4643fe39 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs @@ -5,7 +5,6 @@ namespace Microsoft.AspNetCore.Builder; using Asp.Versioning; using Asp.Versioning.ApiExplorer; using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; internal sealed class ApiVersionDescriptionProviderFactory : IApiVersionDescriptionProviderFactory @@ -29,9 +28,11 @@ public ApiVersionDescriptionProviderFactory( public IApiVersionDescriptionProvider Create( EndpointDataSource endpointDataSource ) { - var collators = new List( capacity: providers.Length + 1 ); + var collators = new List( capacity: providers.Length + 1 ) + { + new EndpointApiVersionMetadataCollationProvider( endpointDataSource ), + }; - collators.Add( new EndpointApiVersionMetadataCollationProvider( endpointDataSource ) ); collators.AddRange( providers ); return activator( collators, sunsetPolicyManager, options ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelper.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelper.cs index 18b95aac..420692b4 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelper.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelper.cs @@ -68,14 +68,10 @@ public ApiVersionUrlHelper( ActionContext actionContext, IUrlHelper url ) Url.Link( routeName, AddApiVersionRouteValueIfNecessary( values ) ); /// -#pragma warning disable CA1054 // URI-like parameters should not be strings public virtual bool IsLocalUrl( string? url ) => Url.IsLocalUrl( url ); -#pragma warning restore CA1054 /// -#pragma warning disable CA1055 // URI-like return values should not be strings public virtual string? RouteUrl( UrlRouteContext routeContext ) -#pragma warning restore CA1055 { if ( routeContext == null ) { diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs index 2e0582af..1fdd81e7 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs @@ -2,8 +2,6 @@ namespace Asp.Versioning.Http; -#pragma warning disable CA1815 // Override equals and operator equals on value types - using System.Collections; /// diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHeaderEnumerable.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHeaderEnumerable.cs index f1d7aa79..f06248aa 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHeaderEnumerable.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHeaderEnumerable.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. namespace Asp.Versioning.Http; -#pragma warning disable CA1815 // Override equals and operator equals on value types /// /// Represents the enumerable object used to create API version enumerators. diff --git a/src/Client/test/Asp.Versioning.Http.Client.Tests/net7.0/DependencyInjection/IHttpClientBuilderExtensionsTest.cs b/src/Client/test/Asp.Versioning.Http.Client.Tests/net7.0/DependencyInjection/IHttpClientBuilderExtensionsTest.cs index db8c33fa..afd2f19c 100644 --- a/src/Client/test/Asp.Versioning.Http.Client.Tests/net7.0/DependencyInjection/IHttpClientBuilderExtensionsTest.cs +++ b/src/Client/test/Asp.Versioning.Http.Client.Tests/net7.0/DependencyInjection/IHttpClientBuilderExtensionsTest.cs @@ -90,8 +90,6 @@ public void add_api_version_should_register_transient_header_enumerable() result1.Should().NotBeSameAs( result2 ); } -#pragma warning disable CA1812 - private sealed class LastHandler : DelegatingHandler { public HttpRequestMessage Request { get; private set; } diff --git a/src/Common/src/Common.Backport/HashCode.cs b/src/Common/src/Common.Backport/HashCode.cs index 1069aa84..eebe73d9 100644 --- a/src/Common/src/Common.Backport/HashCode.cs +++ b/src/Common/src/Common.Backport/HashCode.cs @@ -2,10 +2,6 @@ #pragma warning disable IDE0007 // Use implicit type #pragma warning disable IDE0079 // Remove unnecessary suppression -#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member -#pragma warning disable CA1815 // Override equals and operator equals on value types -#pragma warning disable CA2231 // Overload operator equals on overriding value type Equals #pragma warning disable SA1108 // Block statements should not contain embedded comments #pragma warning disable SA1132 // Do not combine fields #pragma warning disable SA1200 // Using directives should be placed correctly @@ -66,8 +62,6 @@ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT using System.Numerics; using System.Runtime.CompilerServices; -#pragma warning disable CA1066 // Implement IEquatable when overriding Object.Equals - namespace System { internal struct HashCode @@ -100,10 +94,7 @@ private static uint GenerateGlobalSeed() var epoch = new DateTime(2000, 1, 1); var seed = (int) Math.Ceiling(DateTime.Now.Subtract( epoch ).TotalSeconds); var random = new Random( seed ); - -#pragma warning disable CA5394 // Do not use insecure randomness random.NextBytes( data ); -#pragma warning restore CA5394 // Do not use insecure randomness #endif return BitConverter.ToUInt32( data, 0 ); } diff --git a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder{T}.cs b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder{T}.cs index 26a83013..f7994129 100644 --- a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder{T}.cs +++ b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder{T}.cs @@ -116,9 +116,7 @@ public virtual ActionApiVersionConventionBuilder AdvertisesDeprecatedApiVersi return this; } -#pragma warning disable CA1033 // Interface methods should be callable by child types Type IActionConventionBuilder.ControllerType => typeof( T ); -#pragma warning restore CA1033 // Interface methods should be callable by child types void IDeclareApiVersionConventionBuilder.IsApiVersionNeutral() => IsApiVersionNeutral(); diff --git a/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder{T}.cs b/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder{T}.cs index ea1719c0..765ca431 100644 --- a/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder{T}.cs +++ b/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder{T}.cs @@ -136,9 +136,7 @@ protected override bool TryGetConvention( return false; } -#pragma warning disable CA1033 // Interface methods should be callable by child types Type IControllerConventionBuilder.ControllerType => typeof( T ); -#pragma warning restore CA1033 // Interface methods should be callable by child types void IDeclareApiVersionConventionBuilder.IsApiVersionNeutral() => IsApiVersionNeutral(); diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs index 0e3edf1a..d8ec26bd 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs @@ -52,9 +52,7 @@ public virtual string Describe( AllowedQueryOptions queryOption, ODataQueryOptio string.Format( CurrentCulture, ODataExpSR.UnsupportedQueryOption, -#pragma warning disable CA1308 // Normalize strings to uppercase queryOption.ToString().ToLowerInvariant() ), -#pragma warning restore CA1308 nameof( queryOption ) ), }; } @@ -290,9 +288,7 @@ private static void AppendAllowedOptions( StringBuilder description, ODataQueryO .AppendFormat( CurrentCulture, ODataExpSR.AllowedFunctionsDesc, -#pragma warning disable CA1308 // Normalize strings to uppercase context.AllowedFunctions.ToString().ToLowerInvariant() ); -#pragma warning restore CA1308 } } diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/IODataActionQueryOptionsConventionBuilder{T}.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/IODataActionQueryOptionsConventionBuilder{T}.cs index 38bf2258..0a8835e7 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/IODataActionQueryOptionsConventionBuilder{T}.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/IODataActionQueryOptionsConventionBuilder{T}.cs @@ -1,10 +1,9 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable IDE0079 // Remove unnecessary suppression -#pragma warning disable SA1001 // Commas should be spaced correctly +#pragma warning disable IDE0079 +#pragma warning disable SA1001 namespace Asp.Versioning.Conventions; -#pragma warning restore IDE0079 // Remove unnecessary suppression using System.Reflection; #if NETFRAMEWORK diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderExtensions.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderExtensions.cs index 9804be5d..78073220 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderExtensions.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderExtensions.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable IDE0079 // Remove unnecessary suppression -#pragma warning disable SA1001 // Commas should be spaced correctly +#pragma warning disable IDE0079 +#pragma warning disable SA1001 namespace Asp.Versioning.Conventions; diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs index 3901386d..52759fb6 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs @@ -139,9 +139,7 @@ private static bool IsSupported( string? httpMethod ) private string GetName( AllowedQueryOptions option ) { -#pragma warning disable CA1308 // Normalize strings to uppercase var name = option.ToString().ToLowerInvariant(); -#pragma warning restore CA1308 return Settings.NoDollarPrefix ? name : name.Insert( 0, "$" ); } diff --git a/src/Common/src/Common.OData.ApiExplorer/Microsoft.OData.Edm/EdmExtensions.cs b/src/Common/src/Common.OData.ApiExplorer/Microsoft.OData.Edm/EdmExtensions.cs index 30a3796d..16ef9c9c 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Microsoft.OData.Edm/EdmExtensions.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Microsoft.OData.Edm/EdmExtensions.cs @@ -53,7 +53,7 @@ internal static class EdmExtensions #if NETFRAMEWORK private static string Requalify( string edmFullName, string @namespace ) => @namespace + edmFullName.Substring( 3 ); #else - private static string Requalify( string edmFullName, string @namespace ) => string.Concat( @namespace.AsSpan(), edmFullName.AsSpan().Slice( 3 ) ); + private static string Requalify( string edmFullName, string @namespace ) => string.Concat( @namespace.AsSpan(), edmFullName.AsSpan()[3..] ); #endif private static Type? GetTypeFromAssembly( string edmFullName, string assemblyName ) diff --git a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs index d1d34217..70a67ab8 100644 --- a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs +++ b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs @@ -113,9 +113,7 @@ public virtual MediaTypeApiVersionReaderBuilder Match( [StringSyntax( StringSynt #if !NETFRAMEWORK [CLSCompliant( false )] #endif -#pragma warning disable CA1716 // Identifiers should not match keywords public virtual MediaTypeApiVersionReaderBuilder Select( Func, IReadOnlyList> selector ) -#pragma warning restore CA1716 // Identifiers should not match keywords { select = selector; return this; diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs index a33ba8f9..eb4fc564 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs @@ -30,7 +30,6 @@ public void action_should_call_action_on_controller_builder() controllerBuilder.Verify( cb => cb.Action( method ), Once() ); } -#pragma warning disable CA1034 // Nested types should not be visible #if !NETFRAMEWORK [ApiController] #endif diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs index b3d912b5..1ab4dca2 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs @@ -31,7 +31,6 @@ public void action_should_call_action_on_controller_builder() controllerBuilder.Verify( cb => cb.Action( method ), Once() ); } -#pragma warning disable CA1034 // Nested types should not be visible #if !NETFRAMEWORK [ApiController] #endif diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ActionConventionBuilderExtensionsTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ActionConventionBuilderExtensionsTest.cs index 7b52367c..5d8f409d 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ActionConventionBuilderExtensionsTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ActionConventionBuilderExtensionsTest.cs @@ -104,11 +104,10 @@ public void action_should_throw_exception_when_method_does_not_exist() actionConvention.Should().Throw().And.Message.Should().Be( message ); } +#pragma warning disable CA1822 #pragma warning disable IDE0060 #pragma warning disable IDE0079 -#pragma warning disable CA1034 // Nested types should not be visible -#pragma warning disable CA1822 // Mark members as static -#pragma warning disable CA2234 // Pass system uri objects instead of strings + #if !NETFRAMEWORK [ApiController] #endif diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs index 1808af99..abe982a9 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs @@ -121,8 +121,6 @@ private sealed class TestApiVersionConventionBuilder : ApiVersionConventionBuild internal IDictionary ProtectedControllerConventionBuilders => ControllerConventionBuilders; } -#pragma warning disable CA1812 - private sealed class StubController : ControllerBase { public IActionResult Get() => Ok(); diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs index 237e600b..bed8a174 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs @@ -74,7 +74,6 @@ private sealed class TestControllerApiVersionConventionBuilder : ControllerApiVe internal ActionApiVersionConventionBuilderCollection ProtectedActionBuilders => ActionBuilders; } -#pragma warning disable CA1812 #if !NETFRAMEWORK [ApiController] diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs index 0f59fffb..9b5bf47f 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs @@ -78,7 +78,6 @@ internal TestControllerApiVersionConventionBuilder( Type controllerType ) : base internal ActionApiVersionConventionBuilderCollection ProtectedActionBuilders => ActionBuilders; } -#pragma warning disable CA1812 #if !NETFRAMEWORK [ApiController] diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerConventionBuilderExtensionsTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerConventionBuilderExtensionsTest.cs index c045c310..5463b05a 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerConventionBuilderExtensionsTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerConventionBuilderExtensionsTest.cs @@ -103,9 +103,8 @@ public void action_should_throw_exception_when_method_does_not_exist() #pragma warning disable IDE0060 #pragma warning disable IDE0079 -#pragma warning disable CA1034 // Nested types should not be visible -#pragma warning disable CA1822 // Mark members as static -#pragma warning disable CA2234 // Pass system uri objects instead of strings +#pragma warning disable CA1822 + #if !NETFRAMEWORK [ApiController] #endif diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderExtensionsTest.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderExtensionsTest.cs index f9c0d60b..f2161bbf 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderExtensionsTest.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderExtensionsTest.cs @@ -228,9 +228,9 @@ public void action_should_throw_exception_when_method_does_not_exist() actionConvention.Should().Throw().And.Message.Should().Be( message ); } -#pragma warning disable IDE0060 // Remove unused parameter -#pragma warning disable CA1034 // Nested types should not be visible -#pragma warning disable CA1822 // Mark members as static +#pragma warning disable IDE0060 +#pragma warning disable IDE0079 +#pragma warning disable CA1822 public sealed class StubController : ControllerBase { diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTTest.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTTest.cs index 6cb0b5af..03555e12 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTTest.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTTest.cs @@ -239,8 +239,6 @@ public void action_should_call_action_on_controller_builder() controllerBuilder.Verify( cb => cb.Action( method ), Once() ); } -#pragma warning disable CA1034 // Nested types should not be visible - public sealed class TestController : ControllerBase { public IActionResult Get() => Ok(); diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTest.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTest.cs index 69938e1f..8d27dd30 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTest.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTest.cs @@ -239,8 +239,6 @@ public void action_should_call_action_on_controller_builder() controllerBuilder.Verify( cb => cb.Action( method ), Once() ); } -#pragma warning disable CA1034 // Nested types should not be visible - public sealed class TestController : ControllerBase { public IActionResult Get() => Ok(); diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataQueryOptionsConventionBuilderTest.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataQueryOptionsConventionBuilderTest.cs index 6ca4f8a9..8bdbeb07 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataQueryOptionsConventionBuilderTest.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataQueryOptionsConventionBuilderTest.cs @@ -112,8 +112,6 @@ private sealed class TestODataQueryOptionsConventionBuilder : ODataQueryOptionsC internal new IList Conventions => base.Conventions; } -#pragma warning disable CA1034 // Nested types should not be visible - public sealed class StubController : ODataController { public IActionResult Get() => Ok(); diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Company.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Company.cs index 531309ca..e09502ba 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Company.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Company.cs @@ -1,8 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1002 // Do not expose generic lists -#pragma warning disable CA2227 // Collection properties should be read only - namespace Asp.Versioning.OData; public class Company diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Contact.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Contact.cs index c341bf5c..8f2ea637 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Contact.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Contact.cs @@ -1,8 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA1002 // Do not expose generic lists -#pragma warning disable CA2227 // Collection properties should be read only - namespace Asp.Versioning.OData; public class Contact diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Employer.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Employer.cs index b2391433..496be21c 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Employer.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Employer.cs @@ -1,7 +1,5 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable CA2227 // Collection properties should be read only - namespace Asp.Versioning.OData; public class Employer From b789e7e980e83a7d2f82ce3b75235dee5e0724b4 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 6 Dec 2022 19:43:25 -0800 Subject: [PATCH 018/131] Refactor MapApiGroup to NewVersionedApi (from ASP.NET team feedback) --- .../AspNetCore/WebApi/MinimalApiExample/Program.cs | 2 +- .../WebApi/MinimalOpenApiExample/Program.cs | 4 ++-- .../IEndpointConventionBuilderExtensions.cs | 2 +- .../Builder/IEndpointRouteBuilderExtensions.cs | 2 +- .../WebApi/src/Asp.Versioning.Http/SR.Designer.cs | 2 +- .../WebApi/src/Asp.Versioning.Http/SR.resx | 2 +- .../IEndpointConventionBuilderExtensionsTest.cs | 2 +- .../Builder/IEndpointRouteBuilderExtensionsTest.cs | 14 +++++++------- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/AspNetCore/WebApi/MinimalApiExample/Program.cs b/examples/AspNetCore/WebApi/MinimalApiExample/Program.cs index 33fa8af3..4282c9f5 100644 --- a/examples/AspNetCore/WebApi/MinimalApiExample/Program.cs +++ b/examples/AspNetCore/WebApi/MinimalApiExample/Program.cs @@ -17,7 +17,7 @@ "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; -var forecast = app.MapApiGroup(); +var forecast = app.NewVersionedApi(); // GET /weatherforecast?api-version=1.0 forecast.MapGet( "/weatherforecast", () => diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs b/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs index ec48818e..acf998ed 100644 --- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs +++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs @@ -47,8 +47,8 @@ // Configure the HTTP request pipeline. var app = builder.Build(); -var orders = app.MapApiGroup( "Orders" ); -var people = app.MapApiGroup( "People" ); +var orders = app.NewVersionedApi( "Orders" ); +var people = app.NewVersionedApi( "People" ); // 1.0 var ordersV1 = orders.MapGroup( "/api/orders" ) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs index 3be08fd2..97e53f42 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs @@ -444,7 +444,7 @@ private static void AddMetadata( EndpointBuilder builder, object item ) CultureInfo.CurrentCulture, SR.NoVersionSet, builder.DisplayName, - nameof( IEndpointRouteBuilderExtensions.MapApiGroup ), + nameof( IEndpointRouteBuilderExtensions.NewVersionedApi ), nameof( IEndpointRouteBuilderExtensions.WithApiVersionSet ) ) ); } diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointRouteBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointRouteBuilderExtensions.cs index 5ccff44a..2aad4196 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointRouteBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointRouteBuilderExtensions.cs @@ -69,7 +69,7 @@ public static class IEndpointRouteBuilderExtensions /// The extended . /// The optional name associated with the builder. /// A new instance. - public static IVersionedEndpointRouteBuilder MapApiGroup( this IEndpointRouteBuilder builder, string? name = default ) + public static IVersionedEndpointRouteBuilder NewVersionedApi( this IEndpointRouteBuilder builder, string? name = default ) { if ( builder is null ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.Designer.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.Designer.cs index bdfddbdd..afa465b1 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.Designer.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.Designer.cs @@ -70,7 +70,7 @@ internal static string ApiVersionUnspecified { } /// - /// Looks up a localized string similar to An API group cannot be mapped as a nested group.. + /// Looks up a localized string similar to A versioned API group cannot be mapped as a nested group.. /// internal static string CannotNestApiGroup { get { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.resx b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.resx index 8ebb518c..b3d9185d 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.resx +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.resx @@ -121,7 +121,7 @@ An API version is required, but was not specified. - An API group cannot be mapped as a nested group. + A versioned API group cannot be mapped as a nested group. A grouped API version set cannot be nested under another group. diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Builder/IEndpointConventionBuilderExtensionsTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Builder/IEndpointConventionBuilderExtensionsTest.cs index 44ee61ea..13907a8c 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Builder/IEndpointConventionBuilderExtensionsTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Builder/IEndpointConventionBuilderExtensionsTest.cs @@ -289,7 +289,7 @@ public void with_api_version_set_should_not_override_existing_metadata() var app = builder.Build(); var versionSet = new ApiVersionSetBuilder( default ).Build(); - var group = app.MapApiGroup(); + var group = app.NewVersionedApi(); var get = group.MapGet( "/", () => Results.Ok() ); IEndpointRouteBuilder endpoints = app; diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Builder/IEndpointRouteBuilderExtensionsTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Builder/IEndpointRouteBuilderExtensionsTest.cs index f2d91552..4a73b4ac 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Builder/IEndpointRouteBuilderExtensionsTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Builder/IEndpointRouteBuilderExtensionsTest.cs @@ -72,7 +72,7 @@ public void with_api_version_set_should_not_allow_nesting() } [Fact] - public void map_api_group_should_not_be_allowed_multiple_times() + public void new_versioned_api_should_not_be_allowed_multiple_times() { // arrange var builder = WebApplication.CreateBuilder(); @@ -84,14 +84,14 @@ public void map_api_group_should_not_be_allowed_multiple_times() var app = builder.Build(); // act - var mapApiGroup = () => app.MapApiGroup().MapApiGroup(); + var newVersionedApi = () => app.NewVersionedApi().NewVersionedApi(); // assert - mapApiGroup.Should().Throw(); + newVersionedApi.Should().Throw(); } [Fact] - public void map_api_group_should_not_allow_nesting() + public void new_versioned_api_should_not_allow_nesting() { // arrange var builder = WebApplication.CreateBuilder(); @@ -101,13 +101,13 @@ public void map_api_group_should_not_allow_nesting() services.AddApiVersioning(); var app = builder.Build(); - var g1 = app.MapApiGroup(); + var g1 = app.NewVersionedApi(); var g2 = g1.MapGroup( "Test" ); // act - var mapApiGroup = () => g2.MapApiGroup(); + var newVersionedApi = () => g2.NewVersionedApi(); // assert - mapApiGroup.Should().Throw(); + newVersionedApi.Should().Throw(); } } \ No newline at end of file From 89253e36c9628d52379987b2abbc6b01b24c8d1d Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 6 Dec 2022 19:54:21 -0800 Subject: [PATCH 019/131] Bump versions and release notes for Web API --- .../Asp.Versioning.WebApi.OData.ApiExplorer.csproj | 4 ++-- .../Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt | 2 +- .../Asp.Versioning.WebApi.OData.csproj | 4 ++-- .../OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt | 2 +- .../Asp.Versioning.WebApi.ApiExplorer.csproj | 4 ++-- .../src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt | 2 +- .../src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj | 4 ++-- src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj index 44cd9e06..0cd36a0c 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj @@ -1,8 +1,8 @@  - 6.2.1 - 6.2.0.0 + 7.0.0 + 7.0.0.0 net45;net472 Asp.Versioning ASP.NET Web API Versioning API Explorer for OData v4.0 diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt index 40001e18..5f282702 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ -Fix MediaTypeApiVersionReaderBuilder.AddParameters (#904) \ No newline at end of file + \ No newline at end of file diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj index 504275fe..87b75cd3 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj @@ -1,8 +1,8 @@  - 6.2.1 - 6.2.0.0 + 7.0.0 + 7.0.0.0 net45;net472 Asp.Versioning API Versioning for ASP.NET Web API with OData v4.0 diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt index 40001e18..5f282702 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt @@ -1 +1 @@ -Fix MediaTypeApiVersionReaderBuilder.AddParameters (#904) \ No newline at end of file + \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj index c04f1fe3..5ded1914 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj @@ -1,8 +1,8 @@  - 6.2.1 - 6.2.0.0 + 7.0.0 + 7.0.0.0 net45;net472 ASP.NET Web API Versioning API Explorer The API Explorer extensions for ASP.NET Web API Versioning. diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt index 40001e18..5f282702 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ -Fix MediaTypeApiVersionReaderBuilder.AddParameters (#904) \ No newline at end of file + \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj index b2f49ecd..5da0add5 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj @@ -1,8 +1,8 @@  - 6.2.1 - 6.2.0.0 + 7.0.0 + 7.0.0.0 net45;net472 ASP.NET Web API Versioning A service API versioning library for Microsoft ASP.NET Web API. diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt index 40001e18..5f282702 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt @@ -1 +1 @@ -Fix MediaTypeApiVersionReaderBuilder.AddParameters (#904) \ No newline at end of file + \ No newline at end of file From 09318481f9cf5cb77dc836bcff24b87d56f77f10 Mon Sep 17 00:00:00 2001 From: Marius W Nilsen Date: Wed, 7 Dec 2022 10:14:09 +0100 Subject: [PATCH 020/131] AssumeCulture en-us for month formatting test --- .../Asp.Versioning.Abstractions.Tests/net7.0/ApiVersionTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net7.0/ApiVersionTest.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net7.0/ApiVersionTest.cs index b650303b..b1b6f794 100644 --- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net7.0/ApiVersionTest.cs +++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net7.0/ApiVersionTest.cs @@ -6,6 +6,7 @@ public partial class ApiVersionTest { [Theory] [MemberData( nameof( FormatData ) )] + [AssumeCulture( "en-us" )] public void try_format_format_should_return_expected_string( string format, string text, string formattedString ) { // arrange From 9e43aa44531ced7c28b939f96f831b3e3fa60eee Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Thu, 8 Dec 2022 11:42:51 -0800 Subject: [PATCH 021/131] Default to excluding ad hoc model substitution (more common) --- .../OData/DefaultModelTypeBuilder.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs index 37c3bebc..4b969183 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs @@ -31,32 +31,32 @@ public sealed class DefaultModelTypeBuilder : IModelTypeBuilder * incorrect bucket is picked, then the type mapping will fail. the model type builder detects if a model * is ad hoc. if it is, then it will recursively create a private instance of itself to handle the ad hoc * bucket. normal odata cannot opt out of this process because the explored type must match the edm. a type - * mapped via an ad hoc edm is not really odata so it can opt out if desired. the opt out process is more - * of a failsafe and optimization. if the ad hoc edm wasn't customized, then the meta model and type should - * be exactly the same, which will result in no substitution. + * mapped via an ad hoc edm is not really odata so it should opt out by default because without an edm + * there is not away to control member serialization/deserialization easily. such cases will typically + * create a type-per-version, as is common for non-odata, which negates the need for model substitution. + * a user can opt into ad hoc model substitution if they have a way to deal with member filtering. */ private static Type? ienumerableOfT; private readonly bool adHoc; + private readonly bool excludeAdHocModels; private DefaultModelTypeBuilder? adHocBuilder; private ConcurrentDictionary? modules; private ConcurrentDictionary>? generatedEdmTypesPerVersion; private ConcurrentDictionary>? generatedActionParamsPerVersion; - private DefaultModelTypeBuilder( bool adHoc ) => this.adHoc = adHoc; + private DefaultModelTypeBuilder( bool excludeAdHocModels, bool adHoc ) + { + this.adHoc = adHoc; + this.excludeAdHocModels = excludeAdHocModels; + } /// /// Initializes a new instance of the class. /// - public DefaultModelTypeBuilder() { } - - /// - /// Gets or sets a value indicating whether types from an ad hoc Entity Data Model - /// (EDM) should be excluded. - /// - /// True if types from an ad hoc EDM are excluded; otherwise, false. The - /// default value is false. - public bool ExcludeAdHocModels { get; set; } + /// Indicates whether types from an ad hoc Entity + /// Data Model (EDM) should be included. + public DefaultModelTypeBuilder( bool includeAdHocModels = false ) => excludeAdHocModels = !includeAdHocModels; /// public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredType, Type clrType, ApiVersion apiVersion ) @@ -68,13 +68,13 @@ public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredTyp if ( model.IsAdHoc() ) { - if ( ExcludeAdHocModels ) + if ( excludeAdHocModels ) { return clrType; } else if ( !adHoc ) { - adHocBuilder ??= new( adHoc: true ); + adHocBuilder ??= new( excludeAdHocModels, adHoc: true ); return adHocBuilder.NewStructuredType( model, structuredType, clrType, apiVersion ); } } @@ -111,7 +111,7 @@ public Type NewActionParameters( IEdmModel model, IEdmAction action, string cont if ( !adHoc && model.IsAdHoc() ) { - adHocBuilder ??= new( adHoc: true ); + adHocBuilder ??= new( excludeAdHocModels, adHoc: true ); return adHocBuilder.NewActionParameters( model, action, controllerName, apiVersion ); } From 996dd65636d0378177799a6ad0c58f15e9d878e6 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 27 Dec 2022 13:17:35 -0800 Subject: [PATCH 022/131] Fix spelling --- .../ApiExplorer/PartialODataDescriptionProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs index 31824b08..a4fc7e6b 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs @@ -18,7 +18,7 @@ namespace Asp.Versioning.ApiExplorer; using Opts = Microsoft.Extensions.Options.Options; /// -/// Reprensents an API description provider for partial OData support. +/// Represents an API description provider for partial OData support. /// [CLSCompliant( false )] public class PartialODataDescriptionProvider : IApiDescriptionProvider From 968e417f53b7d6f6a22f47b70962a0005996cbf2 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 27 Dec 2022 17:52:20 -0800 Subject: [PATCH 023/131] Fix all code analysis issues generated by the .NET 7.0.101 SDK --- .../AdvertiseApiVersionsAttribute.cs | 5 ++ .../Asp.Versioning.Abstractions/ApiVersion.cs | 4 ++ .../ApiVersionAttribute.cs | 5 ++ .../IApiVersionNeutral.cs | 3 + .../LinkHeaderValue.cs | 2 + .../MapToApiVersionAttribute.cs | 5 ++ .../net7.0/ApiVersion.cs | 4 ++ .../Simulators/V3/SuppliersController.cs | 2 + .../HttpServerFixture.cs | 2 +- .../Controllers/WeatherForecastsController.cs | 1 + .../when using a query string.cs | 1 + .../when using a url segment.cs | 2 + .../CustomerModelConfiguration.cs | 10 ++++ .../Configuration/OrderModelConfiguration.cs | 5 ++ .../Configuration/PersonModelConfiguration.cs | 10 ++++ .../WeatherForecastModelConfiguration.cs | 5 ++ .../PartialODataDescriptionProvider.cs | 10 ++++ .../ApiExplorer/SubstitutedModelMetadata.cs | 1 + .../IApiVersioningBuilderExtensions.cs | 1 + .../ODataApiVersionCollectionProvider.cs | 1 + ...ODataMultiModelApplicationModelProvider.cs | 1 + .../ODataValidationSettingsConventionTest.cs | 1 + .../Configuration/AllConfigurations.cs | 5 ++ .../Configuration/OrderModelConfiguration.cs | 5 ++ .../Configuration/PersonModelConfiguration.cs | 5 ++ .../Configuration/ProductConfiguration.cs | 5 ++ .../Configuration/SupplierConfiguration.cs | 5 ++ .../Simulators/Models/Supplier.cs | 2 + .../ODataControllerSpecificationTest.cs | 1 + .../VersionedMetadataControllerTest.cs | 1 + .../ModelConfigurationFeatureProviderTest.cs | 7 ++- .../VersionedMetadataRoutingConventionTest.cs | 1 + .../ApiVersionMetadataCollationCollection.cs | 4 ++ .../ApiVersioningFeature.cs | 4 ++ .../Builder/EndpointBuilderFinalizer.cs | 56 ++++++++++--------- .../IEndpointConventionBuilderExtensions.cs | 4 ++ .../MediaTypeApiVersionReaderBuilder.cs | 4 ++ .../IEndpointRouteBuilderExtensions.cs | 2 +- .../ApplyContentTypeVersionActionFilter.cs | 1 + .../Routing/ApiVersionUrlHelper.cs | 8 +++ .../Routing/ApiVersionRouteConstraintTest.cs | 2 +- ...roupedApiVersionDescriptionProviderTest.cs | 2 +- .../TestEndpointDataSource.cs | 2 +- .../ApiBehaviorSpecificationTest.cs | 1 + .../ApiVersionEnumerator.cs | 3 + .../src/Common.Backport/BitOperations.cs | 2 + src/Common/src/Common.Backport/HashCode.cs | 1 + .../ActionApiVersionConventionBuilder{T}.cs | 4 ++ ...ontrollerApiVersionConventionBuilder{T}.cs | 4 ++ .../Common.Mvc/ReportApiVersionsAttribute.cs | 5 ++ ...aultODataQueryOptionDescriptionProvider.cs | 8 +++ .../ODataValidationSettingsConvention.cs | 4 ++ .../OData/PropertyDependency.cs | 2 +- .../ProblemDetailsDefaults.cs | 3 + .../MediaTypeApiVersionReaderBuilder.cs | 2 + .../ActionApiVersionConventionBuilderTTest.cs | 3 + .../ActionApiVersionConventionBuilderTest.cs | 3 + .../ActionConventionBuilderExtensionsTest.cs | 3 +- .../ApiVersionConventionBuilderTest.cs | 3 + ...trollerApiVersionConventionBuilderTTest.cs | 1 + ...ntrollerApiVersionConventionBuilderTest.cs | 2 + ...ntrollerConventionBuilderExtensionsTest.cs | 1 + ...yOptionsConventionBuilderExtensionsTest.cs | 1 + ...ctionQueryOptionsConventionBuilderTTest.cs | 2 + ...ActionQueryOptionsConventionBuilderTest.cs | 2 + .../ODataQueryOptionsConventionBuilderTest.cs | 2 + .../OData/Company.cs | 4 ++ .../OData/Contact.cs | 4 ++ .../OData/Employer.cs | 2 + 69 files changed, 244 insertions(+), 35 deletions(-) diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs index 195fa7ea..ca183af6 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/AdvertiseApiVersionsAttribute.cs @@ -1,5 +1,10 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 +#pragma warning disable CA1019 +#pragma warning disable CA1033 +#pragma warning disable CA1813 + namespace Asp.Versioning; using static System.AttributeTargets; diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersion.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersion.cs index e7fe068d..0a3018e0 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersion.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersion.cs @@ -341,7 +341,11 @@ public virtual int CompareTo( ApiVersion? other ) public virtual string ToString( string? format, IFormatProvider? formatProvider ) { var provider = ApiVersionFormatProvider.GetInstance( formatProvider ); +#pragma warning disable IDE0079 +#pragma warning disable CA1062 // Validate arguments of public methods return provider.Format( format, this, formatProvider ); +#pragma warning restore CA1062 // Validate arguments of public methods +#pragma warning restore IDE0079 } private static string? ValidateStatus( string? status, Func isValid ) diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionAttribute.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionAttribute.cs index bbdff81b..90657500 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionAttribute.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionAttribute.cs @@ -1,5 +1,10 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 +#pragma warning disable CA1019 +#pragma warning disable CA1033 +#pragma warning disable CA1813 + namespace Asp.Versioning; using static System.AttributeTargets; diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionNeutral.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionNeutral.cs index cc4c27a3..375541ef 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionNeutral.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/IApiVersionNeutral.cs @@ -1,5 +1,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 +#pragma warning disable CA1040 + namespace Asp.Versioning; /// diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs index eeec87a0..ab5507f5 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs @@ -572,11 +572,13 @@ public IEnumerator> GetEnumerator() } // REF: https://datatracker.ietf.org/doc/html/rfc8288#appendix-B.3 #9 +#pragma warning disable CA1308 // Normalize strings to uppercase (all ascii and should normalize to lowercase) #if NETSTANDARD1_0 var key = remaining.Substring( start, end - start ).ToLowerInvariant(); #else var key = new StringSegment( remaining.Substring( start, end - start ).ToLowerInvariant() ); #endif +#pragma warning restore CA1308 // Normalize strings to uppercase start = end; ConsumeWhitespace(); diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/MapToApiVersionAttribute.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/MapToApiVersionAttribute.cs index 3d7050cc..e90f5c58 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/MapToApiVersionAttribute.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/MapToApiVersionAttribute.cs @@ -1,5 +1,10 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 +#pragma warning disable CA1019 +#pragma warning disable CA1033 +#pragma warning disable CA1813 + namespace Asp.Versioning; using static System.AttributeTargets; diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs index bd7c0682..c9eb3b00 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs @@ -11,6 +11,10 @@ public partial class ApiVersion : ISpanFormattable public virtual bool TryFormat( Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider ) { var instance = ApiVersionFormatProvider.GetInstance( provider ); +#pragma warning disable IDE0079 +#pragma warning disable CA1062 // Validate arguments of public methods return instance.TryFormat( destination, out charsWritten, format, this, provider ); +#pragma warning restore CA1062 // Validate arguments of public methods +#pragma warning restore IDE0079 } } \ No newline at end of file diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V3/SuppliersController.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V3/SuppliersController.cs index f3264880..a27e95ff 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V3/SuppliersController.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V3/SuppliersController.cs @@ -1,9 +1,11 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. #pragma warning disable IDE0060 // Remove unused parameter +#pragma warning disable IDE0079 // Remove unnecessary suppression #pragma warning disable SA1625 // Element documentation should not be copied and pasted namespace Asp.Versioning.Simulators.V3; +#pragma warning restore IDE0079 // Remove unnecessary suppression using Asp.Versioning.OData; using Asp.Versioning.Simulators.Models; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/HttpServerFixture.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/HttpServerFixture.cs index e04031d1..22b3f0ad 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/HttpServerFixture.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/HttpServerFixture.cs @@ -18,7 +18,7 @@ public abstract partial class HttpServerFixture { private string visualizationUrl; - public string DirectedGraphVisualizationUrl => + internal string DirectedGraphVisualizationUrl => visualizationUrl ??= GenerateEndpointDirectedGraph( Server.Services ); protected virtual void OnConfigurePartManager( ApplicationPartManager partManager ) => diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs index e138f931..0294fe55 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/Controllers/WeatherForecastsController.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. #pragma warning disable IDE0060 // Remove unused parameter +#pragma warning disable IDE0079 #pragma warning disable CA1822 // Mark members as static namespace Asp.Versioning.OData.Basic.Controllers; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a query string.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a query string.cs index 6b63fd64..e85c1b9b 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a query string.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a query string.cs @@ -1,5 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable CA2000 // Dispose objects before losing scope namespace given_versioned_batch_middleware; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a url segment.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a url segment.cs index 560cd298..8d873512 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a url segment.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Basic/given versioned batch middleware/when using a url segment.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 +#pragma warning disable CA2000 // Dispose objects before losing scope namespace given_versioned_batch_middleware; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs index 4f48ea20..426272bc 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs @@ -25,6 +25,16 @@ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBui public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { + if ( builder == null ) + { + throw new ArgumentNullException( nameof( builder ) ); + } + + if ( apiVersion == null ) + { + throw new ArgumentNullException( nameof( apiVersion ) ); + } + switch ( apiVersion.MajorVersion ) { case 1: diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs index 5d5655a9..e125f126 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs @@ -22,6 +22,11 @@ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilde public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { + if ( builder == null ) + { + throw new ArgumentNullException( nameof( builder ) ); + } + if ( supportedApiVersion == null || supportedApiVersion == apiVersion ) { ConfigureCurrent( builder ); diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs index b3fb9f01..79646c06 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs @@ -25,6 +25,16 @@ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuild public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { + if ( builder == null ) + { + throw new ArgumentNullException( nameof( builder ) ); + } + + if ( apiVersion == null ) + { + throw new ArgumentNullException( nameof( apiVersion ) ); + } + switch ( apiVersion.MajorVersion ) { case 1: diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs index c53de03f..1d485b8b 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs @@ -22,6 +22,11 @@ private static EntityTypeConfiguration ConfigureCurrent( ODataM public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { + if ( builder == null ) + { + throw new ArgumentNullException( nameof( builder ) ); + } + if ( supportedApiVersion == null || supportedApiVersion == apiVersion ) { ConfigureCurrent( builder ); diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs index a4fc7e6b..84873316 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs @@ -87,6 +87,11 @@ protected ODataApiExplorerOptions Options /// public virtual void OnProvidersExecuting( ApiDescriptionProviderContext context ) { + if ( context == null ) + { + throw new ArgumentNullException( nameof( context ) ); + } + var results = FilterResults( context.Results, Conventions ); if ( results.Count == 0 ) @@ -120,6 +125,11 @@ public virtual void OnProvidersExecuting( ApiDescriptionProviderContext context /// public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context ) { + if ( context == null ) + { + throw new ArgumentNullException( nameof( context ) ); + } + var actions = context.Actions; for ( var i = 0; i < actions.Count; i++ ) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/SubstitutedModelMetadata.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/SubstitutedModelMetadata.cs index ec670cde..5ee9b575 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/SubstitutedModelMetadata.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/SubstitutedModelMetadata.cs @@ -1,5 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 #pragma warning disable CA1812 namespace Asp.Versioning.ApiExplorer; diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs index d19c8ed5..c2702f8f 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -65,6 +65,7 @@ private static void AddApiExplorerServices( IApiVersioningBuilder builder ) services.Replace( Singleton, ODataApiExplorerOptionsAdapter>() ); } +#pragma warning disable IDE0079 #pragma warning disable CA1812 private sealed class ODataApiExplorerOptionsAdapter : IOptionsFactory diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApiVersionCollectionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApiVersionCollectionProvider.cs index 76cada9f..0aa16562 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApiVersionCollectionProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApiVersionCollectionProvider.cs @@ -1,5 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 #pragma warning disable CA1812 namespace Asp.Versioning.OData; diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs index 7fb591e2..cc798280 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataMultiModelApplicationModelProvider.cs @@ -1,5 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 #pragma warning disable CA1812 namespace Asp.Versioning.OData; diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs index 36ed3198..e5cd95ce 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs @@ -637,6 +637,7 @@ private static ApiDescription NewApiDescription( Type controllerType, Type respo } #pragma warning disable IDE0060 // Remove unused parameter +#pragma warning disable CA1034 // Nested types should not be visible public class SinglePartController : ODataController { diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs index fcbda180..d28d6728 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs @@ -13,6 +13,11 @@ public class AllConfigurations : IModelConfiguration /// public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { + if ( builder == null ) + { + throw new ArgumentNullException( nameof( builder ) ); + } + builder.Function( "GetSalesTaxRate" ).Returns().Parameter( "PostalCode" ); } } \ No newline at end of file diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs index 6542f84f..5f16263e 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs @@ -14,6 +14,11 @@ public class OrderModelConfiguration : IModelConfiguration /// public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { + if ( builder == null ) + { + throw new ArgumentNullException( nameof( builder ) ); + } + var order = builder.EntitySet( "Orders" ).EntityType.HasKey( o => o.Id ); if ( apiVersion < ApiVersions.V2 ) diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs index dd2f04ce..19db9f7f 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs @@ -14,6 +14,11 @@ public class PersonModelConfiguration : IModelConfiguration /// public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { + if ( builder == null ) + { + throw new ArgumentNullException( nameof( builder ) ); + } + var person = builder.EntitySet( "People" ).EntityType.HasKey( p => p.Id ); if ( apiVersion < ApiVersions.V3 ) diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs index b1741080..dc988cc2 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs @@ -14,6 +14,11 @@ public class ProductConfiguration : IModelConfiguration /// public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { + if ( builder == null ) + { + throw new ArgumentNullException( nameof( builder ) ); + } + if ( apiVersion < ApiVersions.V3 ) { return; diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs index 3b9d0f45..0ec3e700 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs @@ -14,6 +14,11 @@ public class SupplierConfiguration : IModelConfiguration /// public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { + if ( builder == null ) + { + throw new ArgumentNullException( nameof( builder ) ); + } + if ( apiVersion < ApiVersions.V3 ) { return; diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs index 19470142..7b503c6b 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Models/Supplier.cs @@ -8,5 +8,7 @@ public class Supplier public string Name { get; set; } +#pragma warning disable CA2227 // Collection properties should be read only public ICollection Products { get; set; } +#pragma warning restore CA2227 // Collection properties should be read only } \ No newline at end of file diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/ApplicationModels/ODataControllerSpecificationTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/ApplicationModels/ODataControllerSpecificationTest.cs index 7855beca..9176a07e 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/ApplicationModels/ODataControllerSpecificationTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/ApplicationModels/ODataControllerSpecificationTest.cs @@ -29,6 +29,7 @@ public void is_satisfied_by_should_return_expected_value( Type controllerType, b result.Should().Be( expected ); } +#pragma warning disable IDE0079 #pragma warning disable CA1812 private sealed class NormalODataController : ODataController diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Controllers/VersionedMetadataControllerTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Controllers/VersionedMetadataControllerTest.cs index 516ef02c..e363fa4c 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Controllers/VersionedMetadataControllerTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Controllers/VersionedMetadataControllerTest.cs @@ -34,6 +34,7 @@ public async Task options_should_return_expected_headers() response.Content.Headers.Allow.Should().BeEquivalentTo( "GET", "OPTIONS" ); } +#pragma warning disable IDE0079 #pragma warning disable CA1812 #pragma warning disable CA1822 // Mark members as static diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs index e344a34d..8255590f 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs @@ -32,6 +32,7 @@ public void populate_feature_should_discover_valid_model_configurations() } } +#pragma warning disable IDE0079 #pragma warning disable CA1812 #pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1403 // File may only contain a single namespace @@ -43,7 +44,7 @@ internal struct ValueTypeModelConfiguration : IModelConfiguration public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { } } - internal class PrivateModelConfiguration : IModelConfiguration + internal sealed class PrivateModelConfiguration : IModelConfiguration { public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { } } @@ -53,12 +54,12 @@ public abstract class AbstractModelConfiguration : IModelConfiguration public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { } } - public class GenericModelConfiguration : IModelConfiguration + public sealed class GenericModelConfiguration : IModelConfiguration { public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { } } - public class PublicModelConfiguration : IModelConfiguration + public sealed class PublicModelConfiguration : IModelConfiguration { public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { } } diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedMetadataRoutingConventionTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedMetadataRoutingConventionTest.cs index 8559374e..0346cac2 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedMetadataRoutingConventionTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedMetadataRoutingConventionTest.cs @@ -52,6 +52,7 @@ public void applied_to_action_should_return_true() action.Selectors.Should().HaveCount( 1 ); } +#pragma warning disable IDE0079 #pragma warning disable CA1812 private sealed class AnotherVersionedMetadataController : VersionedMetadataController diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs index 0d728c19..62b9a374 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs @@ -47,7 +47,11 @@ ApiVersionMetadata IList.this[int index] /// public int Count => items.Count; +#pragma warning disable IDE0079 +#pragma warning disable CA1033 // Interface methods should be callable by child types bool ICollection.IsReadOnly => ( (ICollection) items ).IsReadOnly; +#pragma warning restore CA1033 // Interface methods should be callable by child types +#pragma warning restore IDE0079 /// public void Add( ApiVersionMetadata item ) => Insert( Count, item, default ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs index 8dd49c7d..d17375ac 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs @@ -58,7 +58,11 @@ public string? RawRequestedApiVersion { 0 => default, 1 => values[0], +#pragma warning disable IDE0079 +#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations _ => throw NewAmbiguousApiVersionException( values ), +#pragma warning restore CA1065 // Do not raise exceptions in unexpected locations +#pragma warning restore IDE0079 }; } set diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs index baf0fd12..470bb7b8 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs @@ -212,14 +212,8 @@ private static ApiVersionMetadata Build( IList metadata, ApiVersionSet v ApiVersion[] emptyVersions; var inheritedSupported = apiModel.SupportedApiVersions; var inheritedDeprecated = apiModel.DeprecatedApiVersions; - var (mapped, supported, deprecated, advertised, advertisedDeprecated) = buckets; - var isEmpty = mapped.Count == 0 && - supported.Count == 0 && - deprecated.Count == 0 && - advertised.Count == 0 && - advertisedDeprecated.Count == 0; - - if ( isEmpty ) + + if ( buckets.AreEmpty ) { var noInheritedApiVersions = inheritedSupported.Count == 0 && inheritedDeprecated.Count == 0; @@ -239,24 +233,29 @@ private static ApiVersionMetadata Build( IList metadata, ApiVersionSet v emptyVersions ); } } - else if ( mapped.Count == 0 ) - { - endpointModel = new( - declaredVersions: supported.Union( deprecated ), - supported.Union( inheritedSupported ), - deprecated.Union( inheritedDeprecated ), - advertised, - advertisedDeprecated ); - } else { - emptyVersions = Array.Empty(); - endpointModel = new( - declaredVersions: mapped, - supportedVersions: inheritedSupported, - deprecatedVersions: inheritedDeprecated, - advertisedVersions: emptyVersions, - deprecatedAdvertisedVersions: emptyVersions ); + var (mapped, supported, deprecated, advertised, advertisedDeprecated) = buckets; + + if ( mapped.Count == 0 ) + { + endpointModel = new( + declaredVersions: supported.Union( deprecated ), + supported.Union( inheritedSupported ), + deprecated.Union( inheritedDeprecated ), + advertised, + advertisedDeprecated ); + } + else + { + emptyVersions = Array.Empty(); + endpointModel = new( + declaredVersions: mapped, + supportedVersions: inheritedSupported, + deprecatedVersions: inheritedDeprecated, + advertisedVersions: emptyVersions, + deprecatedAdvertisedVersions: emptyVersions ); + } } return new( apiModel, endpointModel, name ); @@ -277,5 +276,12 @@ private record struct ApiVersionBuckets( IReadOnlyList Supported, IReadOnlyList Deprecated, IReadOnlyList Advertised, - IReadOnlyList AdvertisedDeprecated ); + IReadOnlyList AdvertisedDeprecated ) + { + internal bool AreEmpty = Mapped.Count == 0 + && Supported.Count == 0 + && Deprecated.Count == 0 + && Advertised.Count == 0 + && AdvertisedDeprecated.Count == 0; + } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs index 97e53f42..7fab3aa7 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs @@ -478,7 +478,11 @@ private sealed class SingleItemReadOnlyList : IReadOnlyList internal SingleItemReadOnlyList( ApiVersion item ) => this.item = item; +#pragma warning disable IDE0079 +#pragma warning disable CA2201 // Do not raise reserved exception types public ApiVersion this[int index] => index == 0 ? item : throw new IndexOutOfRangeException(); +#pragma warning restore CA2201 // Do not raise reserved exception types +#pragma warning restore IDE0079 public int Count => 1; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs index 22eeef58..6947cf0c 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs @@ -22,7 +22,11 @@ public partial class MediaTypeApiVersionReaderBuilder /// If a value is not specified, there is expected to be a single template parameter. /// The current . /// The template syntax is the same used by route templates; however, constraints are not supported. +#pragma warning disable IDE0079 +#pragma warning disable CA1716 // Identifiers should not match keywords public virtual MediaTypeApiVersionReaderBuilder Template( string template, string? parameterName = default ) +#pragma warning restore CA1716 // Identifiers should not match keywords +#pragma warning restore IDE0079 { if ( string.IsNullOrEmpty( template ) ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/IEndpointRouteBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/IEndpointRouteBuilderExtensions.cs index 545278fd..e42c66d6 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/IEndpointRouteBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/IEndpointRouteBuilderExtensions.cs @@ -26,8 +26,8 @@ public static IReadOnlyList DescribeApiVersions( this IEn } var services = endpoints.ServiceProvider; - var source = new CompositeEndpointDataSource( endpoints.DataSources ); var factory = services.GetRequiredService(); + using var source = new CompositeEndpointDataSource( endpoints.DataSources ); var provider = factory.Create( source ); return provider.ApiVersionDescriptions; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplyContentTypeVersionActionFilter.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplyContentTypeVersionActionFilter.cs index c4ae0a3c..20c64805 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplyContentTypeVersionActionFilter.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplyContentTypeVersionActionFilter.cs @@ -1,5 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 #pragma warning disable CA1812 namespace Asp.Versioning; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelper.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelper.cs index 420692b4..5752e107 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelper.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelper.cs @@ -68,10 +68,18 @@ public ApiVersionUrlHelper( ActionContext actionContext, IUrlHelper url ) Url.Link( routeName, AddApiVersionRouteValueIfNecessary( values ) ); /// +#pragma warning disable IDE0079 +#pragma warning disable CA1054 // URI-like parameters should not be strings public virtual bool IsLocalUrl( string? url ) => Url.IsLocalUrl( url ); +#pragma warning restore CA1054 // URI-like parameters should not be strings +#pragma warning restore IDE0079 /// +#pragma warning disable IDE0079 +#pragma warning disable CA1055 // URI-like return values should not be strings public virtual string? RouteUrl( UrlRouteContext routeContext ) +#pragma warning restore CA1055 // URI-like return values should not be strings +#pragma warning restore IDE0079 { if ( routeContext == null ) { diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Routing/ApiVersionRouteConstraintTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Routing/ApiVersionRouteConstraintTest.cs index 9c114f92..d522f4f4 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Routing/ApiVersionRouteConstraintTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Routing/ApiVersionRouteConstraintTest.cs @@ -137,7 +137,7 @@ public void url_helper_should_create_action_with_api_version_constraint() url.Should().Be( "/v1.1/Order/Place" ); } - private class PassThroughRouter : IRouter + private sealed class PassThroughRouter : IRouter { public VirtualPathData GetVirtualPath( VirtualPathContext context ) => null; diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/GroupedApiVersionDescriptionProviderTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/GroupedApiVersionDescriptionProviderTest.cs index f29b9139..9be0191f 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/GroupedApiVersionDescriptionProviderTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/GroupedApiVersionDescriptionProviderTest.cs @@ -41,7 +41,7 @@ public void api_version_descriptions_should_collate_expected_versions_with_custo { // arrange var provider = new TestActionDescriptorCollectionProvider(); - var source = new CompositeEndpointDataSource( Enumerable.Empty() ); + using var source = new CompositeEndpointDataSource( Enumerable.Empty() ); var data = new ApiDescriptionActionData() { GroupName = "Test" }; foreach ( var descriptor in provider.ActionDescriptors.Items ) diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestEndpointDataSource.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestEndpointDataSource.cs index dbe79bcf..a10a4b81 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestEndpointDataSource.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestEndpointDataSource.cs @@ -8,7 +8,7 @@ namespace Asp.Versioning.ApiExplorer; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; -internal class TestEndpointDataSource : EndpointDataSource +internal sealed class TestEndpointDataSource : EndpointDataSource { public override IReadOnlyList Endpoints { get; } = CreateEndpoints(); diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApplicationModels/ApiBehaviorSpecificationTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApplicationModels/ApiBehaviorSpecificationTest.cs index 8976977a..418dfa5b 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApplicationModels/ApiBehaviorSpecificationTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApplicationModels/ApiBehaviorSpecificationTest.cs @@ -1,5 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 #pragma warning disable CA1812 namespace Asp.Versioning.ApplicationModels; diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs index 1fdd81e7..08dd678e 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs @@ -1,5 +1,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 +#pragma warning disable CA1815 // Override equals and operator equals on value types + namespace Asp.Versioning.Http; using System.Collections; diff --git a/src/Common/src/Common.Backport/BitOperations.cs b/src/Common/src/Common.Backport/BitOperations.cs index bbbf1cdc..316bfb02 100644 --- a/src/Common/src/Common.Backport/BitOperations.cs +++ b/src/Common/src/Common.Backport/BitOperations.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 + // REF: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs #pragma warning disable CS3019 // CLS compliance checking will not be performed because it is not visible from outside this assembly diff --git a/src/Common/src/Common.Backport/HashCode.cs b/src/Common/src/Common.Backport/HashCode.cs index eebe73d9..bd4a7e7b 100644 --- a/src/Common/src/Common.Backport/HashCode.cs +++ b/src/Common/src/Common.Backport/HashCode.cs @@ -1,5 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 #pragma warning disable IDE0007 // Use implicit type #pragma warning disable IDE0079 // Remove unnecessary suppression #pragma warning disable SA1108 // Block statements should not contain embedded comments diff --git a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder{T}.cs b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder{T}.cs index f7994129..bc886b83 100644 --- a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder{T}.cs +++ b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder{T}.cs @@ -116,7 +116,11 @@ public virtual ActionApiVersionConventionBuilder AdvertisesDeprecatedApiVersi return this; } +#pragma warning disable IDE0079 +#pragma warning disable CA1033 // Interface methods should be callable by child types Type IActionConventionBuilder.ControllerType => typeof( T ); +#pragma warning restore CA1033 // Interface methods should be callable by child types +#pragma warning restore IDE0079 void IDeclareApiVersionConventionBuilder.IsApiVersionNeutral() => IsApiVersionNeutral(); diff --git a/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder{T}.cs b/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder{T}.cs index 765ca431..d7d4dc0d 100644 --- a/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder{T}.cs +++ b/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder{T}.cs @@ -136,7 +136,11 @@ protected override bool TryGetConvention( return false; } +#pragma warning disable IDE0079 +#pragma warning disable CA1033 // Interface methods should be callable by child types Type IControllerConventionBuilder.ControllerType => typeof( T ); +#pragma warning restore CA1033 // Interface methods should be callable by child types +#pragma warning restore IDE0079 void IDeclareApiVersionConventionBuilder.IsApiVersionNeutral() => IsApiVersionNeutral(); diff --git a/src/Common/src/Common.Mvc/ReportApiVersionsAttribute.cs b/src/Common/src/Common.Mvc/ReportApiVersionsAttribute.cs index 2093b4c5..142ad4e4 100644 --- a/src/Common/src/Common.Mvc/ReportApiVersionsAttribute.cs +++ b/src/Common/src/Common.Mvc/ReportApiVersionsAttribute.cs @@ -23,6 +23,11 @@ public sealed partial class ReportApiVersionsAttribute : ActionFilterAttribute /// public ReportApiVersionsAttribute() { } + // cannot use attribute syntax, but this allows the attribute to be instantiated just like + // any other class implementing IActionFilter. the parameterless constructor uses DI +#pragma warning disable IDE0079 +#pragma warning disable CA1019 // Define accessors for attribute arguments + /// /// Initializes a new instance of the class. /// diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs index d8ec26bd..c35d1db0 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs @@ -52,7 +52,11 @@ public virtual string Describe( AllowedQueryOptions queryOption, ODataQueryOptio string.Format( CurrentCulture, ODataExpSR.UnsupportedQueryOption, +#pragma warning disable IDE0079 +#pragma warning disable CA1308 // Normalize strings to uppercase (proper casing is lowercase) queryOption.ToString().ToLowerInvariant() ), +#pragma warning restore CA1308 // Normalize strings to uppercase +#pragma warning restore IDE0079 nameof( queryOption ) ), }; } @@ -284,11 +288,15 @@ private static void AppendAllowedOptions( StringBuilder description, ODataQueryO if ( context.AllowedFunctions != AllowedFunctions.None && context.AllowedFunctions != AllowedFunctions.AllFunctions ) { +#pragma warning disable IDE0079 +#pragma warning disable CA1308 // Normalize strings to uppercase (proper casing is lowercase) description.Append( Space ) .AppendFormat( CurrentCulture, ODataExpSR.AllowedFunctionsDesc, context.AllowedFunctions.ToString().ToLowerInvariant() ); +#pragma warning restore CA1308 // Normalize strings to uppercase +#pragma warning restore IDE0079 } } diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs index 52759fb6..686a1953 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs @@ -139,7 +139,11 @@ private static bool IsSupported( string? httpMethod ) private string GetName( AllowedQueryOptions option ) { +#pragma warning disable IDE0079 +#pragma warning disable CA1308 // Normalize strings to uppercase (proper casing is lowercase) var name = option.ToString().ToLowerInvariant(); +#pragma warning restore CA1308 // Normalize strings to uppercase +#pragma warning restore IDE0079 return Settings.NoDollarPrefix ? name : name.Insert( 0, "$" ); } diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/PropertyDependency.cs b/src/Common/src/Common.OData.ApiExplorer/OData/PropertyDependency.cs index 562229bd..bf84b902 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/PropertyDependency.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/PropertyDependency.cs @@ -4,7 +4,7 @@ namespace Asp.Versioning.OData; using System.Reflection.Emit; -internal class PropertyDependency +internal sealed class PropertyDependency { internal PropertyDependency( EdmTypeKey dependentOnTypeKey, diff --git a/src/Common/src/Common.ProblemDetails/ProblemDetailsDefaults.cs b/src/Common/src/Common.ProblemDetails/ProblemDetailsDefaults.cs index 7b0f8258..bc668a05 100644 --- a/src/Common/src/Common.ProblemDetails/ProblemDetailsDefaults.cs +++ b/src/Common/src/Common.ProblemDetails/ProblemDetailsDefaults.cs @@ -48,6 +48,9 @@ public static class ProblemDetailsDefaults "Ambiguous API version", "AmbiguousApiVersion" ); +#pragma warning disable IDE0079 +#pragma warning disable CA1034 // Nested types should not be visible + /// /// Represents the default problem details media type. /// diff --git a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs index 70a67ab8..d1d34217 100644 --- a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs +++ b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs @@ -113,7 +113,9 @@ public virtual MediaTypeApiVersionReaderBuilder Match( [StringSyntax( StringSynt #if !NETFRAMEWORK [CLSCompliant( false )] #endif +#pragma warning disable CA1716 // Identifiers should not match keywords public virtual MediaTypeApiVersionReaderBuilder Select( Func, IReadOnlyList> selector ) +#pragma warning restore CA1716 // Identifiers should not match keywords { select = selector; return this; diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs index eb4fc564..baef9fc0 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs @@ -30,6 +30,9 @@ public void action_should_call_action_on_controller_builder() controllerBuilder.Verify( cb => cb.Action( method ), Once() ); } +#pragma warning disable IDE0079 +#pragma warning disable CA1034 // Nested types should not be visible + #if !NETFRAMEWORK [ApiController] #endif diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs index 1ab4dca2..ed3fcbbf 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs @@ -31,6 +31,9 @@ public void action_should_call_action_on_controller_builder() controllerBuilder.Verify( cb => cb.Action( method ), Once() ); } +#pragma warning disable IDE0079 +#pragma warning disable CA1034 // Nested types should not be visible + #if !NETFRAMEWORK [ApiController] #endif diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ActionConventionBuilderExtensionsTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ActionConventionBuilderExtensionsTest.cs index 5d8f409d..00096fce 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ActionConventionBuilderExtensionsTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ActionConventionBuilderExtensionsTest.cs @@ -104,9 +104,10 @@ public void action_should_throw_exception_when_method_does_not_exist() actionConvention.Should().Throw().And.Message.Should().Be( message ); } -#pragma warning disable CA1822 #pragma warning disable IDE0060 #pragma warning disable IDE0079 +#pragma warning disable CA1034 // Nested types should not be visible +#pragma warning disable CA1822 #if !NETFRAMEWORK [ApiController] diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs index abe982a9..17bd1849 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs @@ -121,6 +121,9 @@ private sealed class TestApiVersionConventionBuilder : ApiVersionConventionBuild internal IDictionary ProtectedControllerConventionBuilders => ControllerConventionBuilders; } +#pragma warning disable IDE0079 +#pragma warning disable CA1812 + private sealed class StubController : ControllerBase { public IActionResult Get() => Ok(); diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs index bed8a174..237e600b 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs @@ -74,6 +74,7 @@ private sealed class TestControllerApiVersionConventionBuilder : ControllerApiVe internal ActionApiVersionConventionBuilderCollection ProtectedActionBuilders => ActionBuilders; } +#pragma warning disable CA1812 #if !NETFRAMEWORK [ApiController] diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs index 9b5bf47f..54f3cbdb 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs @@ -78,6 +78,8 @@ internal TestControllerApiVersionConventionBuilder( Type controllerType ) : base internal ActionApiVersionConventionBuilderCollection ProtectedActionBuilders => ActionBuilders; } +#pragma warning disable IDE0079 +#pragma warning disable CA1812 #if !NETFRAMEWORK [ApiController] diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerConventionBuilderExtensionsTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerConventionBuilderExtensionsTest.cs index 5463b05a..0d55d027 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerConventionBuilderExtensionsTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerConventionBuilderExtensionsTest.cs @@ -104,6 +104,7 @@ public void action_should_throw_exception_when_method_does_not_exist() #pragma warning disable IDE0060 #pragma warning disable IDE0079 #pragma warning disable CA1822 +#pragma warning disable CA1034 // Nested types should not be visible #if !NETFRAMEWORK [ApiController] diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderExtensionsTest.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderExtensionsTest.cs index f2161bbf..6080579f 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderExtensionsTest.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderExtensionsTest.cs @@ -230,6 +230,7 @@ public void action_should_throw_exception_when_method_does_not_exist() #pragma warning disable IDE0060 #pragma warning disable IDE0079 +#pragma warning disable CA1034 // Nested types should not be visible #pragma warning disable CA1822 public sealed class StubController : ControllerBase diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTTest.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTTest.cs index 03555e12..6cb0b5af 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTTest.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTTest.cs @@ -239,6 +239,8 @@ public void action_should_call_action_on_controller_builder() controllerBuilder.Verify( cb => cb.Action( method ), Once() ); } +#pragma warning disable CA1034 // Nested types should not be visible + public sealed class TestController : ControllerBase { public IActionResult Get() => Ok(); diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTest.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTest.cs index 8d27dd30..69938e1f 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTest.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTest.cs @@ -239,6 +239,8 @@ public void action_should_call_action_on_controller_builder() controllerBuilder.Verify( cb => cb.Action( method ), Once() ); } +#pragma warning disable CA1034 // Nested types should not be visible + public sealed class TestController : ControllerBase { public IActionResult Get() => Ok(); diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataQueryOptionsConventionBuilderTest.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataQueryOptionsConventionBuilderTest.cs index 8bdbeb07..6ca4f8a9 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataQueryOptionsConventionBuilderTest.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataQueryOptionsConventionBuilderTest.cs @@ -112,6 +112,8 @@ private sealed class TestODataQueryOptionsConventionBuilder : ODataQueryOptionsC internal new IList Conventions => base.Conventions; } +#pragma warning disable CA1034 // Nested types should not be visible + public sealed class StubController : ODataController { public IActionResult Get() => Ok(); diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Company.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Company.cs index e09502ba..e6daed7c 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Company.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Company.cs @@ -8,7 +8,11 @@ public class Company public Company ParentCompany { get; set; } +#pragma warning disable CA1002 // Do not expose generic lists +#pragma warning disable CA2227 // Collection properties should be read only public List Subsidiaries { get; set; } +#pragma warning restore CA2227 // Collection properties should be read only +#pragma warning restore CA1002 // Do not expose generic lists public string Name { get; set; } diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Contact.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Contact.cs index 8f2ea637..414f2094 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Contact.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Contact.cs @@ -14,5 +14,9 @@ public class Contact public string Phone { get; set; } +#pragma warning disable CA1002 // Do not expose generic lists +#pragma warning disable CA2227 // Collection properties should be read only public List
Addresses { get; set; } +#pragma warning restore CA2227 // Collection properties should be read only +#pragma warning restore CA1002 // Do not expose generic lists } \ No newline at end of file diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Employer.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Employer.cs index 496be21c..efa27e3b 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Employer.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Employer.cs @@ -6,7 +6,9 @@ public class Employer { public int EmployerId { get; set; } +#pragma warning disable CA2227 // Collection properties should be read only public ICollection Employees { get; set; } +#pragma warning restore CA2227 // Collection properties should be read only public DateTime Birthday { get; set; } From 518fec0e541de62e0c8d5b2ae87a3f9f9748c387 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 27 Dec 2022 11:35:59 -0800 Subject: [PATCH 024/131] Fix empty model check; misses only complex types --- .../src/Common.OData/OData/VersionedODataModelBuilder.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Common/src/Common.OData/OData/VersionedODataModelBuilder.cs b/src/Common/src/Common.OData/OData/VersionedODataModelBuilder.cs index c54ad30f..f1fa3c43 100644 --- a/src/Common/src/Common.OData/OData/VersionedODataModelBuilder.cs +++ b/src/Common/src/Common.OData/OData/VersionedODataModelBuilder.cs @@ -110,11 +110,10 @@ private void BuildModelPerApiVersion( configurations[j].Apply( builder, apiVersion, routePrefix ); } + const int EntityContainerOnly = 1; var model = builder.GetEdmModel(); var container = model.EntityContainer; - var empty = !container.EntitySets().Any() && - !container.Singletons().Any() && - !container.OperationImports().Any(); + var empty = model.SchemaElements.Count() == EntityContainerOnly; if ( empty ) { From e32fc81cc4e8bb5af0f4830d9ed3ce82d9b34dfa Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 27 Dec 2022 11:36:49 -0800 Subject: [PATCH 025/131] Switch ad hoc models to be complex types instead of entities; skips validation --- .../ImplicitModelBoundSettingsConvention.cs | 3 - .../ImplicitModelBoundSettingsConvention.cs | 57 ++++++++++++++++--- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs index 3800afd6..1e7fe4e0 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs @@ -2,10 +2,7 @@ namespace Asp.Versioning.Conventions; -using Asp.Versioning; -using Asp.Versioning.OData; using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.OData.ModelBuilder; /// /// Provides additional implementation specific to ASP.NET Core. diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs index d9c6822f..555fd9bc 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs @@ -8,6 +8,7 @@ namespace Asp.Versioning.Conventions; using Microsoft.AspNet.OData.Builder; #else using Microsoft.OData.ModelBuilder; +using System.Buffers; #endif /// @@ -56,18 +57,56 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string? rou private static HashSet? GetExistingTypes( ODataModelBuilder builder ) { - var types = default( HashSet ); + HashSet types; - foreach ( var entitySet in builder.EntitySets ) + if ( builder.StructuralTypes is ICollection collection ) { - types ??= new(); - types.Add( entitySet.ClrType ); + var count = collection.Count; + + if ( count == 0 ) + { + return default; + } + +#if NETFRAMEWORK + var array = new StructuralTypeConfiguration[count]; + types = new(); +#else + var pool = ArrayPool.Shared; + var array = pool.Rent( count ); + + types = new( capacity: count ); +#endif + + collection.CopyTo( array, 0 ); + + for ( var i = 0; i < count; i++ ) + { + types.Add( array[i].ClrType ); + } + +#if !NETFRAMEWORK + pool.Return( array, clearArray: true ); +#endif + + return types; + } + + using var structuralTypes = builder.StructuralTypes.GetEnumerator(); + + if ( !structuralTypes.MoveNext() ) + { + return default; } - foreach ( var singleton in builder.Singletons ) + types = new HashSet() + { + structuralTypes.Current.ClrType, + }; + + while ( structuralTypes.MoveNext() ) { - types ??= new(); - types.Add( singleton.ClrType ); + types.Add( structuralTypes.Current.ClrType ); } return types; @@ -75,9 +114,9 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string? rou private void OnModelCreating( ODataModelBuilder builder ) { - foreach ( var entityType in types.Select( builder.AddEntityType ) ) + foreach ( var type in types ) { - builder.AddEntitySet( entityType.Name, entityType ); + builder.AddComplexType( type ); } } } \ No newline at end of file From 334b2aa37dbc23f53ee49e8e99b5c91d34d60cf7 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 27 Dec 2022 18:05:31 -0800 Subject: [PATCH 026/131] Set patch version and release notes --- .../Asp.Versioning.WebApi.OData.ApiExplorer.csproj | 2 +- .../Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt | 2 +- .../Asp.Versioning.WebApi.OData.csproj | 2 +- .../OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt | 2 +- .../Asp.Versioning.OData.ApiExplorer.csproj | 2 +- .../OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt | 2 +- .../OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj | 2 +- src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj index 0cd36a0c..6f9dca81 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj @@ -1,7 +1,7 @@  - 7.0.0 + 7.0.1 7.0.0.0 net45;net472 Asp.Versioning diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt index 5f282702..579a8f2e 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +Use complex types instead of entities for ad hoc models \ No newline at end of file diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj index 87b75cd3..69c71dd7 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj @@ -1,7 +1,7 @@  - 7.0.0 + 7.0.1 7.0.0.0 net45;net472 Asp.Versioning diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt index 5f282702..a997dbbc 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +Fix empty EDM detection \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj index 36b8c467..b4f6b669 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj @@ -1,7 +1,7 @@  - 7.0.0 + 7.0.1 7.0.0.0 net7.0 Asp.Versioning diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt index 5f282702..579a8f2e 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +Use complex types instead of entities for ad hoc models \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj index 55e90b91..f0eabecd 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj @@ -1,7 +1,7 @@  - 7.0.0 + 7.0.1 7.0.0.0 net7.0 Asp.Versioning diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt b/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt index 5f282702..a997dbbc 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +Fix empty EDM detection \ No newline at end of file From 2292fbe6a1598d944cd5cbca918cb79da7339116 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 27 Dec 2022 19:09:05 -0800 Subject: [PATCH 027/131] Fix CodeQL violation --- .../Builder/EndpointBuilderFinalizer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs index 470bb7b8..5b8d029e 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs @@ -278,10 +278,10 @@ private record struct ApiVersionBuckets( IReadOnlyList Advertised, IReadOnlyList AdvertisedDeprecated ) { - internal bool AreEmpty = Mapped.Count == 0 - && Supported.Count == 0 - && Deprecated.Count == 0 - && Advertised.Count == 0 - && AdvertisedDeprecated.Count == 0; + internal readonly bool AreEmpty = Mapped.Count == 0 + && Supported.Count == 0 + && Deprecated.Count == 0 + && Advertised.Count == 0 + && AdvertisedDeprecated.Count == 0; } } \ No newline at end of file From 6ead32fd4cf5c13f2f4970ca178a34ede72286dd Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 13 Mar 2023 16:41:47 -0700 Subject: [PATCH 028/131] Backport nullable attributes --- .../src/Common.Backport/NullableAttributes.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/Common/src/Common.Backport/NullableAttributes.cs diff --git a/src/Common/src/Common.Backport/NullableAttributes.cs b/src/Common/src/Common.Backport/NullableAttributes.cs new file mode 100644 index 00000000..c5b2c2ae --- /dev/null +++ b/src/Common/src/Common.Backport/NullableAttributes.cs @@ -0,0 +1,59 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +#pragma warning disable SA1402, SA1649 + +// REF: https://github.com/dotnet/runtime/blob/1c8d37af80667daffb3cb80ce0fe915621e8f039/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +namespace System.Diagnostics.CodeAnalysis; + +[AttributeUsage( AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false )] +internal sealed class AllowNullAttribute : Attribute { } + +[AttributeUsage( AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false )] +internal sealed class DisallowNullAttribute : Attribute { } + +[AttributeUsage( AttributeTargets.Method, Inherited = false )] +internal sealed class DoesNotReturnAttribute : Attribute { } + +[AttributeUsage( AttributeTargets.Parameter )] +internal sealed class DoesNotReturnIfAttribute : Attribute +{ + public DoesNotReturnIfAttribute( bool parameterValue ) { } + + public bool ParameterValue => default; +} + +[AttributeUsage( AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Event | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Struct, Inherited = false, AllowMultiple = false )] +internal sealed class ExcludeFromCodeCoverageAttribute : Attribute { } + +[AttributeUsage( AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false )] +internal sealed class MaybeNullAttribute : Attribute { } + +[AttributeUsage( AttributeTargets.Parameter )] +internal sealed class MaybeNullWhenAttribute : Attribute +{ + public MaybeNullWhenAttribute( bool returnValue ) { } + + public bool ReturnValue => default; +} + +[AttributeUsage( AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false )] +internal sealed class NotNullAttribute : Attribute { } + +[AttributeUsage( AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false )] +internal sealed class NotNullIfNotNullAttribute : Attribute +{ + public NotNullIfNotNullAttribute( string parameterName ) { } + + public string ParameterName => default!; +} + +[AttributeUsage( AttributeTargets.Parameter )] +internal sealed class NotNullWhenAttribute : Attribute +{ + public NotNullWhenAttribute( bool returnValue ) { } + + public bool ReturnValue => default; +} \ No newline at end of file From c9745a646de7d95d86e98d825339d3b74e61ecbb Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 13 Mar 2023 16:42:09 -0700 Subject: [PATCH 029/131] Remove unnecessary compiler directives --- .../ApiVersionParser.cs | 2 -- .../Asp.Versioning.Abstractions.csproj | 4 ++++ .../ISunsetPolicyManager.cs | 8 +------ .../ISunsetPolicyManagerExtensions.cs | 15 +++---------- .../LinkHeaderValue.cs | 14 +++---------- .../netstandard2.0/IApiVersionParser.cs | 7 +------ .../IApiVersionParserExtensions.cs | 5 +---- ...Versioning.WebApi.OData.ApiExplorer.csproj | 1 + .../ApiExplorer/VersionedApiExplorer.cs | 21 ++++++------------- .../Asp.Versioning.WebApi.csproj | 1 + .../Controllers/ActionSelectorCacheItem.cs | 6 +++--- ...ntrollerApiVersionConventionBuilderBase.cs | 2 +- .../ApiVersionControllerSelector.cs | 2 +- .../Routing/ApiVersionRouteConstraint.cs | 2 +- .../HttpRequestMessageExtensions.cs | 4 ++-- .../HttpActionDescriptorExtensions.cs | 4 ++-- .../HttpControllerDescriptorExtensions.cs | 8 +++---- .../System.Web.Http/HttpRouteExtensions.cs | 2 +- ...ntrollerApiVersionConventionBuilderBase.cs | 5 +---- ...onApiVersionConventionBuilderCollection.cs | 7 +------ ...piVersionConventionBuilderCollection{T}.cs | 7 +------ .../ControllerApiVersionConventionBuilder.cs | 7 +------ ...ontrollerApiVersionConventionBuilder{T}.cs | 7 +------ ...QueryOptionsConventionBuilderCollection.cs | 7 +------ ...ryOptionsConventionBuilderCollection{T}.cs | 7 +------ .../OData/TypeExtensions.cs | 7 +------ src/Common/src/Common/CollectionExtensions.cs | 5 +---- src/Common/src/Common/PolicyKey.cs | 6 +----- src/Common/src/Common/SunsetPolicyManager.cs | 5 +---- 29 files changed, 47 insertions(+), 131 deletions(-) diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs index f35292f9..c52194d3 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs @@ -187,8 +187,6 @@ public virtual ApiVersion Parse( Text text ) /// #if NETSTANDARD1_0 public virtual bool TryParse( Text? text, out ApiVersion apiVersion ) -#elif NETSTANDARD2_0 - public virtual bool TryParse( Text text, out ApiVersion apiVersion ) #else public virtual bool TryParse( Text text, [MaybeNullWhen( false )] out ApiVersion apiVersion ) #endif diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj b/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj index 04d6b034..42901889 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj @@ -30,6 +30,10 @@ + + + + diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManager.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManager.cs index 77a175a7..de2dd77e 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManager.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManager.cs @@ -18,11 +18,5 @@ public interface ISunsetPolicyManager /// policy for the specified API version. If /// API version is null, it is assumed the caller intends to match /// any sunset policy for the specified . - bool TryGetPolicy( - string? name, - ApiVersion? apiVersion, -#if !NETSTANDARD - [MaybeNullWhen( false )] -#endif - out SunsetPolicy sunsetPolicy ); + bool TryGetPolicy( string? name, ApiVersion? apiVersion, [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy ); } \ No newline at end of file diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs index 11e21279..bd5766c5 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs @@ -17,10 +17,7 @@ public static class ISunsetPolicyManagerExtensions public static bool TryGetPolicy( this ISunsetPolicyManager policyManager, ApiVersion apiVersion, -#if !NETSTANDARD - [MaybeNullWhen( false )] -#endif - out SunsetPolicy sunsetPolicy ) + [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy ) { if ( policyManager == null ) { @@ -40,10 +37,7 @@ public static bool TryGetPolicy( public static bool TryGetPolicy( this ISunsetPolicyManager policyManager, string name, -#if !NETSTANDARD - [MaybeNullWhen( false )] -#endif - out SunsetPolicy sunsetPolicy ) + [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy ) { if ( policyManager == null ) { @@ -104,10 +98,7 @@ public static bool TryResolvePolicy( this ISunsetPolicyManager policyManager, string? name, ApiVersion? apiVersion, -#if !NETSTANDARD - [MaybeNullWhen( false )] -#endif - out SunsetPolicy sunsetPolicy ) + [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy ) { if ( policyManager == null ) { diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs index ab5507f5..1fe6dbfb 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs @@ -161,10 +161,7 @@ public StringSegment Language public static bool TryParse( StringSegment input, Func? resolveRelativeUrl, -#if !NETSTANDARD - [MaybeNullWhen( false )] -#endif - out LinkHeaderValue parsedValue ) + [MaybeNullWhen( false )] out LinkHeaderValue parsedValue ) { #if NETSTANDARD1_0 if ( string.IsNullOrEmpty( input ) ) @@ -302,9 +299,7 @@ public static bool TryParse( public static bool TryParseList( IList? input, Func? resolveRelativeUrl, -#if !NETSTANDARD [MaybeNullWhen( false )] -#endif out IList parsedValues ) { if ( input == null ) @@ -404,10 +399,7 @@ private static void AppendTargetAttribute( StringBuilder builder, ReadOnlySpan? resolveRelativeUrl, -#if !NETSTANDARD - [MaybeNullWhen( false )] -#endif - out Uri targetLink ) + [MaybeNullWhen( false )] out Uri targetLink ) { var start = segment.IndexOf( '<' ); @@ -495,7 +487,7 @@ public bool Remove( KeyValuePair item ) => public bool TryGetValue( StringSegment key, -#if !NETSTANDARD +#if !NETSTANDARD1_0 [MaybeNullWhen( false )] #endif out StringSegment value ) => items.TryGetValue( key, out value ); diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParser.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParser.cs index c806e8dd..cfbf616e 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParser.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParser.cs @@ -20,10 +20,5 @@ public interface IApiVersionParser /// The text to parse as an API version. /// The parsed API version or null. /// True if the parsing was successful; otherwise false. - bool TryParse( - ReadOnlySpan text, -#if !NETSTANDARD - [MaybeNullWhen( false )] -#endif - out ApiVersion apiVersion ); + bool TryParse( ReadOnlySpan text, [MaybeNullWhen( false )] out ApiVersion apiVersion ); } \ No newline at end of file diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParserExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParserExtensions.cs index 1eabc3b4..5935d5d7 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParserExtensions.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/IApiVersionParserExtensions.cs @@ -33,10 +33,7 @@ public static ApiVersion Parse( this IApiVersionParser parser, string? text ) public static bool TryParse( this IApiVersionParser parser, string? text, -#if !NETSTANDARD - [MaybeNullWhen( false )] -#endif - out ApiVersion apiVersion ) + [MaybeNullWhen( false )] out ApiVersion apiVersion ) { if ( parser == null ) { diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj index 6f9dca81..e4483a65 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj @@ -17,6 +17,7 @@ + diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs index 83d99d0e..36b1de41 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs @@ -713,7 +713,7 @@ protected virtual Collection ExploreRouteControllers( var apiDescriptions = new Collection(); var routeTemplate = route.RouteTemplate; - string controllerVariableValue; + string? controllerVariableValue; if ( controllerVariableRegex.IsMatch( routeTemplate ) ) { @@ -734,12 +734,12 @@ protected virtual Collection ExploreRouteControllers( } } else if ( route.Defaults.TryGetValue( RouteValueKeys.Controller, out controllerVariableValue ) && - controllerMappings.TryGetValue( controllerVariableValue, out var controllerDescriptor ) ) + controllerMappings.TryGetValue( controllerVariableValue!, out var controllerDescriptor ) ) { // bound controller variable {controller = "controllerName"} foreach ( var nestedControllerDescriptor in controllerDescriptor.AsEnumerable() ) { - if ( ShouldExploreController( controllerVariableValue, nestedControllerDescriptor, route, apiVersion ) ) + if ( ShouldExploreController( controllerVariableValue!, nestedControllerDescriptor, route, apiVersion ) ) { ExploreRouteActions( route, routeTemplate, nestedControllerDescriptor, apiDescriptions, apiVersion ); } @@ -782,7 +782,7 @@ private void ExploreRouteActions( return; } - string actionVariableValue; + string? actionVariableValue; if ( actionVariableRegex.IsMatch( localPath ) ) { @@ -935,17 +935,8 @@ private static bool ShouldEmitPrefixes( ICollection par parameter.CanConvertPropertiesFromString() ) > 1; } - private static Type GetCollectionElementType( Type collectionType ) - { - var elementType = collectionType.GetElementType(); - - if ( elementType == null ) - { - elementType = typeof( ICollection<> ).GetGenericBinderTypeArgs( collectionType ).First(); - } - - return elementType; - } + private static Type GetCollectionElementType( Type collectionType ) => + collectionType.GetElementType() ?? typeof( ICollection<> ).GetGenericBinderTypeArgs( collectionType ).First(); private static void AddPlaceholderForProperties( Dictionary parameterValuesForRoute, diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj index 5da0add5..0d99e781 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj @@ -17,6 +17,7 @@ + diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs index 0acd241f..bdcfe0de 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs @@ -304,7 +304,7 @@ private static List GetInitialCandidateWithParameterL continue; } - subRouteData.Values.TryGetValue( RouteValueKeys.Action, out string actionName ); + subRouteData.Values.TryGetValue( RouteValueKeys.Action, out string? actionName ); for ( var i = 0; i < candidates.Length; i++ ) { @@ -335,9 +335,9 @@ private CandidateAction[] GetInitialCandidateList( HttpControllerContext control var routeData = controllerContext.RouteData; CandidateAction[] candidates; - if ( routeData.Values.TryGetValue( RouteValueKeys.Action, out string actionName ) ) + if ( routeData.Values.TryGetValue( RouteValueKeys.Action, out string? actionName ) ) { - var actionsFoundByName = standardActions!.StandardActionNameMapping![actionName].ToArray(); + var actionsFoundByName = standardActions!.StandardActionNameMapping![actionName!].ToArray(); if ( actionsFoundByName.Length == 0 ) { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ControllerApiVersionConventionBuilderBase.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ControllerApiVersionConventionBuilderBase.cs index 0c194afe..a75dc789 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ControllerApiVersionConventionBuilderBase.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ControllerApiVersionConventionBuilderBase.cs @@ -54,7 +54,7 @@ public virtual void ApplyTo( HttpControllerDescriptor item ) /// The method representing the action to retrieve the convention for. /// The retrieved convention or null. /// True if the convention was successfully retrieved; otherwise, false. - protected abstract bool TryGetConvention( MethodInfo method, out IApiVersionConvention convention ); + protected abstract bool TryGetConvention( MethodInfo method, [MaybeNullWhen( false )] out IApiVersionConvention convention ); private void ApplyActionConventions( HttpControllerDescriptor controller ) { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs index a9473c8a..d42bedfa 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs @@ -123,7 +123,7 @@ public virtual IDictionary GetControllerMappin return null; } - if ( routeData.Values.TryGetValue( RouteDataTokenKeys.Controller, out string controller ) ) + if ( routeData.Values.TryGetValue( RouteDataTokenKeys.Controller, out string? controller ) ) { return controller; } diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs index 3af05991..65a77769 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs @@ -31,7 +31,7 @@ public bool Match( HttpRequestMessage request, IHttpRoute route, string paramete return false; } - if ( !values.TryGetValue( parameterName, out string value ) ) + if ( !values.TryGetValue( parameterName, out string? value ) ) { return false; } diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs index cd9ac2a7..8b2920fd 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs @@ -78,9 +78,9 @@ public static ApiVersionRequestProperties ApiVersionProperties( this HttpRequest throw new ArgumentNullException( nameof( request ) ); } - if ( request.Properties.TryGetValue( ApiVersionPropertiesKey, out ApiVersionRequestProperties properties ) ) + if ( request.Properties.TryGetValue( ApiVersionPropertiesKey, out ApiVersionRequestProperties? properties ) ) { - return properties; + return properties!; } var forceRouteConstraintEvaluation = !request.Properties.ContainsKey( RoutingContextKey ); diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpActionDescriptorExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpActionDescriptorExtensions.cs index 0821d758..1e523558 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpActionDescriptorExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpActionDescriptorExtensions.cs @@ -25,9 +25,9 @@ public static ApiVersionMetadata GetApiVersionMetadata( this HttpActionDescripto throw new ArgumentNullException( nameof( action ) ); } - if ( action.Properties.TryGetValue( typeof( ApiVersionMetadata ), out ApiVersionMetadata value ) ) + if ( action.Properties.TryGetValue( typeof( ApiVersionMetadata ), out ApiVersionMetadata? value ) ) { - return value; + return value!; } return ApiVersionMetadata.Empty; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpControllerDescriptorExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpControllerDescriptorExtensions.cs index 3e66c57d..66d1454f 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpControllerDescriptorExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpControllerDescriptorExtensions.cs @@ -39,9 +39,9 @@ public static ApiVersionModel GetApiVersionModel( this HttpControllerDescriptor throw new ArgumentNullException( nameof( controllerDescriptor ) ); } - if ( controllerDescriptor.Properties.TryGetValue( typeof( ApiVersionModel ), out ApiVersionModel value ) ) + if ( controllerDescriptor.Properties.TryGetValue( typeof( ApiVersionModel ), out ApiVersionModel? value ) ) { - return value; + return value!; } return ApiVersionModel.Empty; @@ -107,12 +107,12 @@ internal static IEnumerable AsEnumerable( this HttpCon yield return controllerDescriptor; } - if ( !includeCandidates || !controllerDescriptor.Properties.TryGetValue( PossibleControllerCandidatesKey, out IEnumerable candidates ) ) + if ( !includeCandidates || !controllerDescriptor.Properties.TryGetValue( PossibleControllerCandidatesKey, out IEnumerable? candidates ) ) { yield break; } - foreach ( var candidate in candidates ) + foreach ( var candidate in candidates! ) { if ( visited.Add( candidate ) ) { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteExtensions.cs index 5c69c15c..29dbeb03 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteExtensions.cs @@ -20,7 +20,7 @@ internal static class HttpRouteExtensions var directRouteActions = default( HttpActionDescriptor[] ); - if ( dataTokens.TryGetValue( RouteDataTokenKeys.Actions, out HttpActionDescriptor[] possibleDirectRouteActions ) && + if ( dataTokens.TryGetValue( RouteDataTokenKeys.Actions, out HttpActionDescriptor[]? possibleDirectRouteActions ) && possibleDirectRouteActions != null && possibleDirectRouteActions.Length > 0 ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ControllerApiVersionConventionBuilderBase.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ControllerApiVersionConventionBuilderBase.cs index 4ddc0718..6c7e548d 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ControllerApiVersionConventionBuilderBase.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ControllerApiVersionConventionBuilderBase.cs @@ -2,7 +2,6 @@ namespace Asp.Versioning.Conventions; -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; using System.Reflection; @@ -50,9 +49,7 @@ public virtual void ApplyTo( ControllerModel item ) /// The method representing the action to retrieve the convention for. /// The retrieved convention or null. /// True if the convention was successfully retrieved; otherwise, false. - protected abstract bool TryGetConvention( - MethodInfo method, - [MaybeNullWhen( false )] out IApiVersionConvention convention ); + protected abstract bool TryGetConvention( MethodInfo method, [MaybeNullWhen( false )] out IApiVersionConvention convention ); private void ApplyActionConventions( ControllerModel controller ) { diff --git a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection.cs b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection.cs index c0d226e7..7b3c91e4 100644 --- a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection.cs +++ b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection.cs @@ -64,12 +64,7 @@ protected internal virtual ActionApiVersionConventionBuilder GetOrAdd( MethodInf /// The controller action method to get the convention builder for. /// The controller action convention builder or null. /// True if the action builder is successfully retrieved; otherwise, false. - public virtual bool TryGetValue( - MethodInfo? actionMethod, -#if !NETFRAMEWORK - [MaybeNullWhen( false )] -#endif - out ActionApiVersionConventionBuilder actionBuilder ) + public virtual bool TryGetValue( MethodInfo? actionMethod, [MaybeNullWhen( false )] out ActionApiVersionConventionBuilder actionBuilder ) { if ( actionBuilderMappings == null || actionMethod == null ) { diff --git a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection{T}.cs b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection{T}.cs index 3547e92d..a94bc224 100644 --- a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection{T}.cs +++ b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection{T}.cs @@ -73,12 +73,7 @@ protected internal virtual ActionApiVersionConventionBuilder GetOrAdd( Method /// The controller action method to get the convention builder for. /// The controller action convention builder or null. /// True if the action builder is successfully retrieved; otherwise, false. - public virtual bool TryGetValue( - MethodInfo? actionMethod, -#if !NETFRAMEWORK - [MaybeNullWhen( false )] -#endif - out ActionApiVersionConventionBuilder actionBuilder ) + public virtual bool TryGetValue( MethodInfo? actionMethod, [MaybeNullWhen( false )] out ActionApiVersionConventionBuilder actionBuilder ) { if ( actionBuilderMappings == null || actionMethod == null ) { diff --git a/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder.cs b/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder.cs index e1f1c1ec..277f9cd4 100644 --- a/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder.cs +++ b/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder.cs @@ -127,12 +127,7 @@ public virtual ControllerApiVersionConventionBuilder AdvertisesDeprecatedApiVers /// The method representing the action to retrieve the convention for. /// The retrieved convention or null. /// True if the convention was successfully retrieved; otherwise, false. - protected override bool TryGetConvention( - MethodInfo method, -#if !NETFRAMEWORK - [MaybeNullWhen( false )] -#endif - out IApiVersionConvention convention ) + protected override bool TryGetConvention( MethodInfo method, [MaybeNullWhen( false )] out IApiVersionConvention convention ) { if ( actionBuilders is not null && actionBuilders.TryGetValue( method, out var actionBuilder ) ) diff --git a/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder{T}.cs b/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder{T}.cs index d7d4dc0d..6babc238 100644 --- a/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder{T}.cs +++ b/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder{T}.cs @@ -120,12 +120,7 @@ public virtual ControllerApiVersionConventionBuilder AdvertisesDeprecatedApiV /// The method representing the action to retrieve the convention for. /// The retrieved convention or null. /// True if the convention was successfully retrieved; otherwise, false. - protected override bool TryGetConvention( - MethodInfo method, -#if !NETFRAMEWORK - [MaybeNullWhen( false )] -#endif - out IApiVersionConvention convention ) + protected override bool TryGetConvention( MethodInfo method, [MaybeNullWhen( false )] out IApiVersionConvention convention ) { if ( actionBuilders is not null && actionBuilders.TryGetValue( method, out var builder ) ) { diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection.cs index 5f60f7c2..539051ea 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection.cs @@ -60,12 +60,7 @@ protected internal virtual ODataActionQueryOptionsConventionBuilder GetOrAdd( Me /// The controller action method to get the convention builder for. /// The controller action convention builder or null. /// True if the action builder is successfully retrieved; otherwise, false. - public virtual bool TryGetValue( - MethodInfo? actionMethod, -#if !NETFRAMEWORK - [NotNullWhen( true )] -#endif - out ODataActionQueryOptionsConventionBuilder? actionBuilder ) + public virtual bool TryGetValue( MethodInfo? actionMethod, [NotNullWhen( true )] out ODataActionQueryOptionsConventionBuilder? actionBuilder ) { if ( actionMethod == null || actionBuilderMappings == null || actionBuilderMappings.Count == 0 ) { diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection{T}.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection{T}.cs index ccce51af..718b854e 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection{T}.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection{T}.cs @@ -71,12 +71,7 @@ protected internal virtual ODataActionQueryOptionsConventionBuilder GetOrAdd( /// The controller action method to get the convention builder for. /// The controller action convention builder or null. /// True if the action builder is successfully retrieved; otherwise, false. - public virtual bool TryGetValue( - MethodInfo? actionMethod, -#if !NETFRAMEWORK - [NotNullWhen( true )] -#endif - out ODataActionQueryOptionsConventionBuilder? actionBuilder ) + public virtual bool TryGetValue( MethodInfo? actionMethod, [NotNullWhen( true )] out ODataActionQueryOptionsConventionBuilder? actionBuilder ) { if ( actionMethod == null || actionBuilderMappings == null || actionBuilderMappings.Count == 0 ) { diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs b/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs index bca4a961..a312e307 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs @@ -248,12 +248,7 @@ private static bool CanBeSubstituted( Type type ) => #endif !type.IsODataActionParameters(); - internal static bool IsEnumerable( - this Type type, -#if !NETFRAMEWORK - [NotNullWhen( true )] -#endif - out Type? itemType ) + internal static bool IsEnumerable( this Type type, [NotNullWhen( true )] out Type? itemType ) { var types = new Queue(); diff --git a/src/Common/src/Common/CollectionExtensions.cs b/src/Common/src/Common/CollectionExtensions.cs index 157ac438..4c6f1e4e 100644 --- a/src/Common/src/Common/CollectionExtensions.cs +++ b/src/Common/src/Common/CollectionExtensions.cs @@ -7,10 +7,7 @@ internal static partial class CollectionExtensions internal static bool TryGetValue( this IDictionary dictionary, TKey key, -#if !NETFRAMEWORK - [MaybeNullWhen( false )] -#endif - out TValue value ) + [MaybeNullWhen( false )] out TValue value ) where TKey : notnull { if ( dictionary.TryGetValue( key, out var val ) && val is TValue v ) diff --git a/src/Common/src/Common/PolicyKey.cs b/src/Common/src/Common/PolicyKey.cs index 618740b3..e51e2edf 100644 --- a/src/Common/src/Common/PolicyKey.cs +++ b/src/Common/src/Common/PolicyKey.cs @@ -19,11 +19,7 @@ public PolicyKey( string? name, ApiVersion? version ) public bool Equals( PolicyKey other ) => GetHashCode() == other.GetHashCode(); - public override bool Equals( -#if !NETFRAMEWORK - [NotNullWhen( true )] -#endif - object? obj ) => obj is PolicyKey other && Equals( other ); + public override bool Equals( [NotNullWhen( true )] object? obj ) => obj is PolicyKey other && Equals( other ); public override int GetHashCode() { diff --git a/src/Common/src/Common/SunsetPolicyManager.cs b/src/Common/src/Common/SunsetPolicyManager.cs index 2c19a773..50fdf115 100644 --- a/src/Common/src/Common/SunsetPolicyManager.cs +++ b/src/Common/src/Common/SunsetPolicyManager.cs @@ -13,10 +13,7 @@ public partial class SunsetPolicyManager : ISunsetPolicyManager public virtual bool TryGetPolicy( string? name, ApiVersion? apiVersion, -#if !NETFRAMEWORK - [MaybeNullWhen( false )] -#endif - out SunsetPolicy sunsetPolicy ) + [MaybeNullWhen( false )] out SunsetPolicy sunsetPolicy ) { if ( string.IsNullOrEmpty( name ) && apiVersion == null ) { From 05551130a02ff0525ec1849e747c1578614b8fca Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 10 Mar 2023 16:52:52 -0800 Subject: [PATCH 030/131] Reorder visitation such that EnableQueryAttribute can override Model Bound Settings. Related to #928 --- .../ODataValidationSettingsConventionTest.cs | 58 +++++++++++++++++++ .../Conventions/ODataAttributeVisitor.cs | 4 +- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs index e5cd95ce..1193606f 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs @@ -19,6 +19,7 @@ namespace Asp.Versioning.Conventions; using Microsoft.OData.ModelBuilder; using Microsoft.OData.ModelBuilder.Config; using System.Reflection; +using Xunit; using static Microsoft.AspNetCore.Http.StatusCodes; using static Microsoft.AspNetCore.Mvc.ModelBinding.BindingSource; using static Microsoft.AspNetCore.OData.Query.AllowedArithmeticOperators; @@ -469,6 +470,50 @@ public void apply_to_should_use_model_bound_query_attributes() options => options.ExcludingMissingMembers() ); } + [Fact] + public void apply_should_override_model_bound_settings_with_enable_query_attribute() + { + // arrange + var builder = new ODataConventionModelBuilder().EnableLowerCamelCase(); + + builder.EntitySet( "Customers" ); + + var validationSettings = new ODataValidationSettings() + { + AllowedQueryOptions = AllowedQueryOptions.None, + AllowedArithmeticOperators = AllowedArithmeticOperators.None, + AllowedLogicalOperators = AllowedLogicalOperators.None, + AllowedFunctions = AllowedFunctions.None, + }; + var settings = new TestODataQueryOptionSettings( typeof( Customer ) ); + var convention = new ODataValidationSettingsConvention( validationSettings, settings ); + var model = builder.GetEdmModel(); + var description = NewApiDescription( typeof( CustomersController ), typeof( IEnumerable ), model ); + + // act + convention.ApplyTo( description ); + + // assert + var parameter = description.ParameterDescriptions.Single(); + + parameter.Should().BeEquivalentTo( + new + { + Name = "$filter", + Source = Query, + Type = typeof( string ), + DefaultValue = default( object ), + IsRequired = false, + ModelMetadata = new { Description = "Test" }, + ParameterDescriptor = new + { + Name = "$filter", + ParameterType = typeof( string ), + }, + }, + options => options.ExcludingMissingMembers() ); + } + [Fact] public void apply_to_should_process_odataX2Dlike_api_description() { @@ -678,6 +723,13 @@ public class OrdersController : ODataController public IActionResult Get( ODataQueryOptions options ) => Ok(); } + public class CustomersController : ODataController + { + [EnableQuery( AllowedQueryOptions = Filter )] + [ProducesResponseType( typeof( IEnumerable ), Status200OK )] + public IActionResult Get( ODataQueryOptions options ) => Ok(); + } + [Select] [Filter] [Count] @@ -693,6 +745,12 @@ public class Order public int Quantity { get; set; } } + [Page( MaxTop = 25, PageSize = 25 )] + public class Customer + { + public int CustomerId { get; set; } + } + private sealed class TestODataQueryOptionSettings : ODataQueryOptionSettings { internal TestODataQueryOptionSettings( Type type, bool dollarPrefix = true ) : diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs index 1ee25007..4afba308 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs @@ -38,14 +38,14 @@ internal ODataAttributeVisitor( internal void Visit( ApiDescription apiDescription ) { - VisitAction( apiDescription.ActionDescriptor ); - var modelType = context.ReturnType; if ( modelType != null ) { VisitModel( modelType ); } + + VisitAction( apiDescription.ActionDescriptor ); } private void VisitModel( IEdmStructuredType modelType ) From 2487953bce4368f02e3ae69e29e86c33c037e9b2 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 10 Mar 2023 16:53:20 -0800 Subject: [PATCH 031/131] Bump version and update release notes --- .../Asp.Versioning.WebApi.OData.ApiExplorer.csproj | 2 +- .../Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt | 2 +- .../Asp.Versioning.OData.ApiExplorer.csproj | 2 +- .../OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj index e4483a65..4fbf47cd 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj @@ -1,7 +1,7 @@  - 7.0.1 + 7.0.2 7.0.0.0 net45;net472 Asp.Versioning diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt index 579a8f2e..977a8d55 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ -Use complex types instead of entities for ad hoc models \ No newline at end of file +Fix: `EnableQueryAttribute` should override _Model Bound_ settings (Related to [#928](https://github.com/dotnet/aspnet-api-versioning/issues/928)) \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj index b4f6b669..dfe4c9b7 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj @@ -1,7 +1,7 @@  - 7.0.1 + 7.0.2 7.0.0.0 net7.0 Asp.Versioning diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt index 579a8f2e..977a8d55 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ -Use complex types instead of entities for ad hoc models \ No newline at end of file +Fix: `EnableQueryAttribute` should override _Model Bound_ settings (Related to [#928](https://github.com/dotnet/aspnet-api-versioning/issues/928)) \ No newline at end of file From 1970f0f28b579fd016f59ecbffc7d97669811de4 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 16 Jun 2023 15:26:49 -0700 Subject: [PATCH 032/131] Fix models in multiple EDMs with same API version; Fixes #996 --- .../OData/ClassSignature.cs | 4 +-- .../OData/DefaultModelTypeBuilder.cs | 20 +++++++------- .../OData/EdmModelKey.cs | 26 +++++++++++++++++++ .../OData/EdmTypeKey.cs | 9 ++++--- 4 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 src/Common/src/Common.OData.ApiExplorer/OData/EdmModelKey.cs diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/ClassSignature.cs b/src/Common/src/Common.OData.ApiExplorer/OData/ClassSignature.cs index 749a5cfc..5dc090ea 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/ClassSignature.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/ClassSignature.cs @@ -12,7 +12,7 @@ internal sealed class ClassSignature : IEquatable private static readonly ConstructorInfo newOriginalType = typeof( OriginalTypeAttribute ).GetConstructors()[0]; private int? hashCode; - internal ClassSignature( Type originalType, IEnumerable properties, ApiVersion apiVersion ) + internal ClassSignature( string name, Type originalType, IEnumerable properties, ApiVersion apiVersion ) { var attributeBuilders = new List() { @@ -21,7 +21,7 @@ internal ClassSignature( Type originalType, IEnumerable propertie attributeBuilders.AddRange( originalType.DeclaredAttributes() ); - Name = originalType.FullName!; + Name = name; Attributes = attributeBuilders.ToArray(); Properties = properties.ToArray(); ApiVersion = apiVersion; diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs index 4b969183..04dab8ec 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs @@ -41,9 +41,9 @@ public sealed class DefaultModelTypeBuilder : IModelTypeBuilder private readonly bool adHoc; private readonly bool excludeAdHocModels; private DefaultModelTypeBuilder? adHocBuilder; - private ConcurrentDictionary? modules; - private ConcurrentDictionary>? generatedEdmTypesPerVersion; - private ConcurrentDictionary>? generatedActionParamsPerVersion; + private ConcurrentDictionary? modules; + private ConcurrentDictionary>? generatedEdmTypesPerVersion; + private ConcurrentDictionary>? generatedActionParamsPerVersion; private DefaultModelTypeBuilder( bool excludeAdHocModels, bool adHoc ) { @@ -96,7 +96,7 @@ public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredTyp generatedEdmTypesPerVersion ??= new(); - var edmTypes = generatedEdmTypesPerVersion.GetOrAdd( apiVersion, key => GenerateTypesForEdmModel( model, key ) ); + var edmTypes = generatedEdmTypesPerVersion.GetOrAdd( new( model, apiVersion ), key => GenerateTypesForEdmModel( model, key.ApiVersion ) ); return edmTypes[new( structuredType, apiVersion )]; } @@ -132,7 +132,7 @@ public Type NewActionParameters( IEdmModel model, IEdmAction action, string cont generatedActionParamsPerVersion ??= new(); - var paramTypes = generatedActionParamsPerVersion.GetOrAdd( apiVersion, _ => new() ); + var paramTypes = generatedActionParamsPerVersion.GetOrAdd( new( model, apiVersion ), _ => new() ); var fullTypeName = $"{controllerName}.{action.Namespace}.{controllerName}{action.Name}Parameters"; var key = new EdmTypeKey( fullTypeName, apiVersion ); var type = paramTypes.GetOrAdd( key, _ => @@ -140,7 +140,7 @@ public Type NewActionParameters( IEdmModel model, IEdmAction action, string cont var context = new TypeSubstitutionContext( model, this, apiVersion ); var properties = action.Parameters.Where( p => p.Name != "bindingParameter" ).Select( p => new ClassProperty( p, context ) ); var signature = new ClassSignature( fullTypeName, properties, apiVersion ); - var moduleBuilder = ( modules ??= new() ).GetOrAdd( apiVersion, CreateModuleForApiVersion ); + var moduleBuilder = ( modules ??= new() ).GetOrAdd( new( model, apiVersion ), CreateModuleForApiVersion ); return CreateTypeFromSignature( moduleBuilder, signature ); } ); @@ -150,7 +150,7 @@ public Type NewActionParameters( IEdmModel model, IEdmAction action, string cont private IDictionary GenerateTypesForEdmModel( IEdmModel model, ApiVersion apiVersion ) { - ModuleBuilder NewModuleBuilder() => ( modules ??= new() ).GetOrAdd( apiVersion, CreateModuleForApiVersion ); + ModuleBuilder NewModuleBuilder() => ( modules ??= new() ).GetOrAdd( new( model, apiVersion ), CreateModuleForApiVersion ); var context = new BuilderContext( model, apiVersion, NewModuleBuilder ); @@ -336,7 +336,7 @@ private static Type ResolveType( return type; } - var signature = new ClassSignature( clrType, properties, apiVersion ); + var signature = new ClassSignature( typeKey.FullName, clrType, properties, apiVersion ); if ( hasUnfinishedTypes ) { @@ -518,9 +518,9 @@ private static AssemblyName NewAssemblyName( ApiVersion apiVersion, bool adHoc ) } [MethodImpl( MethodImplOptions.AggressiveInlining )] - private ModuleBuilder CreateModuleForApiVersion( ApiVersion apiVersion ) + private ModuleBuilder CreateModuleForApiVersion( EdmModelKey key ) { - var assemblyName = NewAssemblyName( apiVersion, adHoc ); + var assemblyName = NewAssemblyName( key.ApiVersion, adHoc ); #if NETFRAMEWORK var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( assemblyName, Run ); #else diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/EdmModelKey.cs b/src/Common/src/Common.OData.ApiExplorer/OData/EdmModelKey.cs new file mode 100644 index 00000000..3fd0380a --- /dev/null +++ b/src/Common/src/Common.OData.ApiExplorer/OData/EdmModelKey.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.OData; + +using Microsoft.OData.Edm; + +internal readonly struct EdmModelKey : IEquatable +{ + private readonly int hashCode; + + public readonly IEdmModel EdmModel; + public readonly ApiVersion ApiVersion; + + internal EdmModelKey( IEdmModel model, ApiVersion apiVersion ) => + hashCode = HashCode.Combine( ( EdmModel = model ).GetHashCode(), ApiVersion = apiVersion ); + + public static bool operator ==( EdmModelKey obj, EdmModelKey other ) => obj.Equals( other ); + + public static bool operator !=( EdmModelKey obj, EdmModelKey other ) => !obj.Equals( other ); + + public override int GetHashCode() => hashCode; + + public override bool Equals( object? obj ) => obj is EdmModelKey other && Equals( other ); + + public bool Equals( EdmModelKey other ) => hashCode == other.hashCode; +} \ No newline at end of file diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/EdmTypeKey.cs b/src/Common/src/Common.OData.ApiExplorer/OData/EdmTypeKey.cs index 7c0301a8..4fd693d1 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/EdmTypeKey.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/EdmTypeKey.cs @@ -8,14 +8,17 @@ namespace Asp.Versioning.OData; { private readonly int hashCode; + public readonly string FullName; + public readonly ApiVersion ApiVersion; + internal EdmTypeKey( IEdmStructuredType type, ApiVersion apiVersion ) => - hashCode = HashCode.Combine( type.FullTypeName(), apiVersion ); + hashCode = HashCode.Combine( FullName = type.FullTypeName(), ApiVersion = apiVersion ); internal EdmTypeKey( IEdmTypeReference type, ApiVersion apiVersion ) => - hashCode = HashCode.Combine( type.FullName(), apiVersion ); + hashCode = HashCode.Combine( FullName = type.FullName(), ApiVersion = apiVersion ); internal EdmTypeKey( string fullTypeName, ApiVersion apiVersion ) => - hashCode = HashCode.Combine( fullTypeName, apiVersion ); + hashCode = HashCode.Combine( FullName = fullTypeName, ApiVersion = apiVersion ); public static bool operator ==( EdmTypeKey obj, EdmTypeKey other ) => obj.Equals( other ); From f29831ea21d69d44dba1e1b2916da6a3d93514c6 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 16 Jun 2023 15:27:09 -0700 Subject: [PATCH 033/131] Add release notes and bump versions --- .../Asp.Versioning.WebApi.OData.ApiExplorer.csproj | 2 +- .../Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt | 2 +- .../Asp.Versioning.OData.ApiExplorer.csproj | 2 +- .../OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj index 4fbf47cd..5e8a0bc2 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj @@ -1,7 +1,7 @@  - 7.0.2 + 7.0.3 7.0.0.0 net45;net472 Asp.Versioning diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt index 977a8d55..a6d1afd0 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ -Fix: `EnableQueryAttribute` should override _Model Bound_ settings (Related to [#928](https://github.com/dotnet/aspnet-api-versioning/issues/928)) \ No newline at end of file +Fix: Explore model per EDM + API version ([#996](https://github.com/dotnet/aspnet-api-versioning/issues/996)) \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj index dfe4c9b7..de83304c 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj @@ -1,7 +1,7 @@  - 7.0.2 + 7.0.3 7.0.0.0 net7.0 Asp.Versioning diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt index 977a8d55..a6d1afd0 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ -Fix: `EnableQueryAttribute` should override _Model Bound_ settings (Related to [#928](https://github.com/dotnet/aspnet-api-versioning/issues/928)) \ No newline at end of file +Fix: Explore model per EDM + API version ([#996](https://github.com/dotnet/aspnet-api-versioning/issues/996)) \ No newline at end of file From 592559cfb02c14949534b55196b35e9bd9277043 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 11 Aug 2023 11:09:11 -0700 Subject: [PATCH 034/131] Prevent infinite recursion. Fixes #1017 --- .../src/Asp.Versioning.Http/ApiVersioningFeature.cs | 6 +----- src/Common/src/Common/ApiVersionReader.cs | 8 ++++++++ src/Common/src/Common/ApiVersioningOptions.cs | 3 +-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs index d17375ac..6b863530 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs @@ -34,11 +34,7 @@ public IReadOnlyList RawRequestedApiVersions { if ( rawApiVersions is null ) { - var reader = context.RequestServices.GetService() - ?? ApiVersionReader.Combine( - new QueryStringApiVersionReader(), - new UrlSegmentApiVersionReader() ); - + var reader = context.RequestServices.GetService() ?? ApiVersionReader.Default; rawApiVersions = reader.Read( context.Request ); } diff --git a/src/Common/src/Common/ApiVersionReader.cs b/src/Common/src/Common/ApiVersionReader.cs index 4bc670c1..276aea06 100644 --- a/src/Common/src/Common/ApiVersionReader.cs +++ b/src/Common/src/Common/ApiVersionReader.cs @@ -16,6 +16,14 @@ namespace Asp.Versioning; #endif public static class ApiVersionReader { + private static IApiVersionReader? @default; + + /// + /// Gets the default API version reader. + /// + /// The default . + public static IApiVersionReader Default => @default ??= Combine( new QueryStringApiVersionReader(), new UrlSegmentApiVersionReader() ); + /// /// Returns a new API version reader that is a combination of the specified set. /// diff --git a/src/Common/src/Common/ApiVersioningOptions.cs b/src/Common/src/Common/ApiVersioningOptions.cs index 6fb980dd..b5297ba9 100644 --- a/src/Common/src/Common/ApiVersioningOptions.cs +++ b/src/Common/src/Common/ApiVersioningOptions.cs @@ -6,7 +6,6 @@ namespace Asp.Versioning; #if NETFRAMEWORK using System.Net; #endif -using static Asp.Versioning.ApiVersionReader; /// /// Represents the possible options for API versioning. @@ -73,7 +72,7 @@ public partial class ApiVersioningOptions #endif public IApiVersionReader ApiVersionReader { - get => apiVersionReader ??= Combine( new QueryStringApiVersionReader(), new UrlSegmentApiVersionReader() ); + get => apiVersionReader ??= Versioning.ApiVersionReader.Default; set => apiVersionReader = value; } From f45d9fb5ef5d61c8a18823bb497ac40dd26e4a9e Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 11 Aug 2023 11:26:51 -0700 Subject: [PATCH 035/131] Fix new code analysis violations --- .../ApiExplorer/ODataApiExplorerOptionsFactory.cs | 2 +- .../src/Asp.Versioning.Http.Client/ApiVersionHandler.cs | 4 ++-- .../src/Common.OData.ApiExplorer/OData/ODataValue{T}.cs | 2 +- src/Common/src/Common.OData/OData/ODataId.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs index ae057645..261b6c54 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs @@ -107,6 +107,6 @@ private static ODataApiVersionCollectionProvider CollateApiVersions( private sealed class ODataApiVersionCollectionProvider : IODataApiVersionCollectionProvider { - required public IReadOnlyList ApiVersions { get; set; } + public required IReadOnlyList ApiVersions { get; set; } } } \ No newline at end of file diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHandler.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHandler.cs index 88f09625..38563a84 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHandler.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHandler.cs @@ -30,7 +30,7 @@ public ApiVersionHandler( ApiVersion apiVersion, IApiNotification? notification = default, IApiVersionParser? parser = default, - ApiVersionHeaderEnumerable? enumerable = default) + ApiVersionHeaderEnumerable? enumerable = default ) { this.apiVersionWriter = apiVersionWriter ?? throw new ArgumentNullException( nameof( apiVersionWriter ) ); this.apiVersion = apiVersion ?? throw new ArgumentNullException( nameof( apiVersion ) ); @@ -75,7 +75,7 @@ protected virtual bool IsDeprecatedApi( HttpResponseMessage response ) foreach ( var reportedApiVersion in enumerable.Deprecated( response, parser ) ) { // don't use '==' operator because a derived type may not overload it - if ( apiVersion.CompareTo( reportedApiVersion ) == 0 ) + if ( apiVersion.Equals( reportedApiVersion ) ) { return true; } diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/ODataValue{T}.cs b/src/Common/src/Common.OData.ApiExplorer/OData/ODataValue{T}.cs index 20a2e31a..284fb87b 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/ODataValue{T}.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/ODataValue{T}.cs @@ -23,6 +23,6 @@ public class ODataValue #if NETFRAMEWORK public T Value { get; set; } = default!; #else - required public T Value { get; set; } + public required T Value { get; set; } #endif } \ No newline at end of file diff --git a/src/Common/src/Common.OData/OData/ODataId.cs b/src/Common/src/Common.OData/OData/ODataId.cs index 4206e994..e4aa3fd2 100644 --- a/src/Common/src/Common.OData/OData/ODataId.cs +++ b/src/Common/src/Common.OData/OData/ODataId.cs @@ -21,6 +21,6 @@ public class ODataId #if NETFRAMEWORK public Uri Value { get; set; } = default!; #else - required public Uri Value { get; set; } + public required Uri Value { get; set; } #endif } \ No newline at end of file From f25b079689e5c466aa511ca55df5e30e73b90041 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 11 Aug 2023 12:00:03 -0700 Subject: [PATCH 036/131] Fix media type subset matching. Fixes #1015 --- .../Http/MediaTypeFixture.cs | 9 +++++ .../Http/MinimalApiFixture.cs | 8 +++++ .../when using a media type.cs | 35 +++++++++++++++++++ .../Rfc7231ProblemDetailsWriter.cs | 6 ++-- 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MediaTypeFixture.cs create mode 100644 src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/given a versioned minimal API/when using a media type.cs diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MediaTypeFixture.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MediaTypeFixture.cs new file mode 100644 index 00000000..5736c1bf --- /dev/null +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MediaTypeFixture.cs @@ -0,0 +1,9 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.Http; + +public class MediaTypeFixture : MinimalApiFixture +{ + protected override void OnAddApiVersioning( ApiVersioningOptions options ) => + options.ApiVersionReader = new MediaTypeApiVersionReader(); +} \ No newline at end of file diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs index 208c5130..f0b841c9 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs @@ -76,4 +76,12 @@ protected override void OnConfigureEndpoints( IEndpointRouteBuilder endpoints ) .WithApiVersionSet( orders ) .IsApiVersionNeutral(); } + + protected override void OnAddApiVersioning( ApiVersioningOptions options ) + { + options.ApiVersionReader = ApiVersionReader.Combine( + new QueryStringApiVersionReader(), + new UrlSegmentApiVersionReader(), + new MediaTypeApiVersionReader() ); + } } \ No newline at end of file diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/given a versioned minimal API/when using a media type.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/given a versioned minimal API/when using a media type.cs new file mode 100644 index 00000000..ee292d3d --- /dev/null +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/given a versioned minimal API/when using a media type.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.Http; + +using Asp.Versioning; +using System.Net.Http; +using System.Net.Http.Json; +using static System.Net.Http.Headers.MediaTypeWithQualityHeaderValue; +using static System.Net.Http.HttpMethod; +using static System.Net.HttpStatusCode; + +public class when_using_a_media_type : AcceptanceTest, IClassFixture +{ + [Fact] + public async Task problem_details_should_be_returned_for_accept_header_with_unsupported_api_version() + { + // arrange + using var request = new HttpRequestMessage( Post, "api/values" ) + { + Headers = { Accept = { Parse( "application/json;v=3.0" ) } }, + Content = JsonContent.Create( new { test = true }, Parse( "application/json;v=3.0" ) ), + }; + + // act + var response = await Client.SendAsync( request ); + var problem = await response.Content.ReadAsProblemDetailsAsync(); + + // assert + response.StatusCode.Should().Be( UnsupportedMediaType ); + problem.Type.Should().Be( ProblemDetailsDefaults.Unsupported.Type ); + } + + public when_using_a_media_type( MediaTypeFixture fixture, ITestOutputHelper console ) + : base( fixture ) => console.WriteLine( fixture.DirectedGraphVisualizationUrl ); +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Rfc7231ProblemDetailsWriter.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Rfc7231ProblemDetailsWriter.cs index 261060d6..8c7382be 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Rfc7231ProblemDetailsWriter.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Rfc7231ProblemDetailsWriter.cs @@ -9,7 +9,7 @@ namespace Asp.Versioning; internal sealed class Rfc7231ProblemDetailsWriter : IProblemDetailsWriter { private static readonly MediaTypeHeaderValue jsonMediaType = new( "application/json" ); - private static readonly MediaTypeHeaderValue problemDetailsJsonMediaType = new( "application/problem+json" ); + private static readonly MediaTypeHeaderValue problemDetailsJsonMediaType = new( ProblemDetailsDefaults.MediaType.Json ); private readonly IProblemDetailsWriter decorated; public Rfc7231ProblemDetailsWriter( IProblemDetailsWriter decorated ) => this.decorated = decorated; @@ -36,8 +36,8 @@ public bool CanWrite( ProblemDetailsContext context ) { var acceptHeaderValue = acceptHeader[i]; - if ( jsonMediaType.IsSubsetOf( acceptHeaderValue ) || - problemDetailsJsonMediaType.IsSubsetOf( acceptHeaderValue ) ) + if ( acceptHeaderValue.IsSubsetOf( jsonMediaType ) || + acceptHeaderValue.IsSubsetOf( problemDetailsJsonMediaType ) ) { return true; } From ed37dd4a9b3a1ce16320078ceae09ab381dc0a20 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 11 Aug 2023 13:36:25 -0700 Subject: [PATCH 037/131] Validate ApiVersioningOptions.DefaultApiVersion != ApiVersion.Neutral. Resolves #1011 --- .../src/Asp.Versioning.WebApi/SR.Designer.cs | 9 ++++ .../WebApi/src/Asp.Versioning.WebApi/SR.resx | 8 ++++ .../HttpConfigurationExtensions.cs | 26 ++++++++++++ .../HttpConfigurationExtensionsTest.cs | 13 ++++++ .../IServiceCollectionExtensions.cs | 7 ++-- .../src/Asp.Versioning.Http/SR.Designer.cs | 9 ++++ .../WebApi/src/Asp.Versioning.Http/SR.resx | 8 ++++ .../ValidateApiVersioningOptions.cs | 41 +++++++++++++++++++ .../IServiceCollectionExtensionsTest.cs | 26 ++++++++++++ 9 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Http/ValidateApiVersioningOptions.cs create mode 100644 src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/DependencyInjection/IServiceCollectionExtensionsTest.cs diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs index 56042d8b..6bf7e427 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs @@ -159,6 +159,15 @@ internal static string DirectRoute_AmbiguousController { } } + /// + /// Looks up a localized string similar to {0}.{1} is an invalid value for {2}.{3}. Did you mean to apply {4} via attribute or convention instead?. + /// + internal static string InvalidDefaultApiVersion { + get { + return ResourceManager.GetString("InvalidDefaultApiVersion", resourceCulture); + } + } + /// /// Looks up a localized string similar to A controller was not selected for request URI '{0}' and API version '{1}'.. /// diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx index ed143dc1..a5b89e38 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx @@ -150,6 +150,14 @@ Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.{1}{1}The request has found the following matching controller types: {0} + + {0}.{1} is an invalid value for {2}.{3}. Did you mean to apply {4} via attribute or convention instead? + 0 = ApiVersion +1 = Neutral +2 = ApiVersioningOptions +3 = DefaultApiVersion +4 = IApiVersionNeutral + A controller was not selected for request URI '{0}' and API version '{1}'. diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs index 932639c4..ebd1a991 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs @@ -5,6 +5,7 @@ namespace System.Web.Http; using Asp.Versioning; using Asp.Versioning.Controllers; using Asp.Versioning.Dispatcher; +using System.Globalization; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using static Asp.Versioning.ApiVersionParameterLocation; @@ -65,6 +66,7 @@ public static void AddApiVersioning( this HttpConfiguration configuration, Actio var options = new ApiVersioningOptions(); setupAction( options ); + ValidateApiVersioningOptions( options ); configuration.AddApiVersioning( options ); } @@ -97,6 +99,30 @@ private static void AddApiVersioning( this HttpConfiguration configuration, ApiV SunsetPolicyManager.Default = new SunsetPolicyManager( options ); } + // ApiVersion.Neutral does not have the same meaning as IApiVersionNeutral. setting + // ApiVersioningOptions.DefaultApiVersion this value will not make all APIs version-neutral + // and will likely lead to many unexpected side effects. this is a best-effort, one-time + // validation check to help prevent people from going off the rails. if someone bypasses + // this validation by removing the check or updating the value later, then caveat emptor. + // + // REF: https://github.com/dotnet/aspnet-api-versioning/issues/1011 + private static void ValidateApiVersioningOptions( ApiVersioningOptions options ) + { + if ( options.DefaultApiVersion == ApiVersion.Neutral ) + { + var message = string.Format( + CultureInfo.CurrentCulture, + SR.InvalidDefaultApiVersion, + nameof( ApiVersion ), + nameof( ApiVersion.Neutral ), + nameof( ApiVersioningOptions ), + nameof( ApiVersioningOptions.DefaultApiVersion ), + nameof( IApiVersionNeutral ) ); + + throw new InvalidOperationException( message ); + } + } + internal static IReportApiVersions GetApiVersionReporter( this HttpConfiguration configuration ) { var options = configuration.GetApiVersioningOptions(); diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs index a77f8010..22cec40e 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs @@ -37,4 +37,17 @@ public void add_api_versioning_should_report_api_versions_when_option_is_enabled configuration.Services.GetActionSelector().Should().BeOfType(); configuration.Filters.Single().Instance.Should().BeOfType(); } + + [Fact] + public void add_api_versioning_should_not_allow_default_neutral_api_version() + { + // arrange + var configuration = new HttpConfiguration(); + + // act + Action options = () => configuration.AddApiVersioning( options => options.DefaultApiVersion = ApiVersion.Neutral ); + + // assert + options.Should().Throw(); + } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs index 3419a436..66c1152a 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs @@ -85,11 +85,12 @@ private static void AddApiVersioningServices( IServiceCollection services ) } services.TryAddSingleton(); - services.Add( Singleton( sp => sp.GetRequiredService>().Value.ApiVersionReader ) ); - services.Add( Singleton( sp => (IApiVersionParameterSource) sp.GetRequiredService>().Value.ApiVersionReader ) ); - services.Add( Singleton( sp => sp.GetRequiredService>().Value.ApiVersionSelector ) ); + services.AddSingleton( sp => sp.GetRequiredService>().Value.ApiVersionReader ); + services.AddSingleton( sp => (IApiVersionParameterSource) sp.GetRequiredService>().Value.ApiVersionReader ); + services.AddSingleton( sp => sp.GetRequiredService>().Value.ApiVersionSelector ); services.TryAddSingleton(); services.TryAddSingleton(); + services.TryAddEnumerable( Transient, ValidateApiVersioningOptions>() ); services.TryAddEnumerable( Transient, ApiVersioningRouteOptionsSetup>() ); services.TryAddEnumerable( Singleton() ); services.TryAddEnumerable( Singleton() ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.Designer.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.Designer.cs index afa465b1..4a56d6b9 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.Designer.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.Designer.cs @@ -96,6 +96,15 @@ internal static string ConventionAddedAfterEndpointBuilt { } } + /// + /// Looks up a localized string similar to {0}.{1} is an invalid value for {2}.{3}. Did you mean to apply {4} via attribute or convention instead?. + /// + internal static string InvalidDefaultApiVersion { + get { + return ResourceManager.GetString("InvalidDefaultApiVersion", resourceCulture); + } + } + /// /// Looks up a localized string similar to An endpoint cannot apply multiple API version sets.. /// diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.resx b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.resx index b3d9185d..dde23ba1 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.resx +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.resx @@ -129,6 +129,14 @@ Conventions cannot be added after building the endpoint. + + {0}.{1} is an invalid value for {2}.{3}. Did you mean to apply {4} via attribute or convention instead? + 0 = ApiVersion +1 = Neutral +2 = ApiVersioningOptions +3 = DefaultApiVersion +4 = IApiVersionNeutral + An endpoint cannot apply multiple API version sets. diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ValidateApiVersioningOptions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ValidateApiVersioningOptions.cs new file mode 100644 index 00000000..c07373fa --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ValidateApiVersioningOptions.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning; + +using Microsoft.Extensions.Options; +using System.Globalization; + +// ApiVersion.Neutral does not have the same meaning as IApiVersionNeutral. setting +// ApiVersioningOptions.DefaultApiVersion this value will not make all APIs version-neutral +// and will likely lead to many unexpected side effects. this is a best-effort, one-time +// validation check to help prevent people from going off the rails. if someone bypasses +// this validation by removing the check or updating the value later, then caveat emptor. +// +// REF: https://github.com/dotnet/aspnet-api-versioning/issues/1011 +#pragma warning disable CA1812 // Avoid uninstantiated internal classes +internal sealed class ValidateApiVersioningOptions : IValidateOptions +#pragma warning restore CA1812 // Avoid uninstantiated internal classes +{ + public ValidateOptionsResult Validate( string? name, ApiVersioningOptions options ) + { + if ( name is not null && name != Options.DefaultName ) + { + return ValidateOptionsResult.Skip; + } + + if ( options.DefaultApiVersion == ApiVersion.Neutral ) + { + var message = string.Format( + CultureInfo.CurrentCulture, + SR.InvalidDefaultApiVersion, + nameof( ApiVersion ), + nameof( ApiVersion.Neutral ), + nameof( ApiVersioningOptions ), + nameof( ApiVersioningOptions.DefaultApiVersion ), + nameof( IApiVersionNeutral ) ); + return ValidateOptionsResult.Fail( message ); + } + + return ValidateOptionsResult.Success; + } +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/DependencyInjection/IServiceCollectionExtensionsTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/DependencyInjection/IServiceCollectionExtensionsTest.cs new file mode 100644 index 00000000..8ed7463e --- /dev/null +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/DependencyInjection/IServiceCollectionExtensionsTest.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Microsoft.Extensions.DependencyInjection; + +using Asp.Versioning; +using Microsoft.Extensions.Options; + +public class IServiceCollectionExtensionsTest +{ + [Fact] + public void add_api_versioning_should_not_allow_default_neutral_api_version() + { + // arrange + var services = new ServiceCollection(); + + services.AddApiVersioning( options => options.DefaultApiVersion = ApiVersion.Neutral ); + + var provider = services.BuildServiceProvider(); + + // act + Func options = () => provider.GetRequiredService>().Value; + + // assert + options.Should().Throw(); + } +} \ No newline at end of file From d09d7ee8f8b5300cd236ef63fc2e9bb61e5d3285 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 11 Aug 2023 13:36:45 -0700 Subject: [PATCH 038/131] Update test package versions --- build/test.targets | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/build/test.targets b/build/test.targets index 8616ce63..b5c39483 100644 --- a/build/test.targets +++ b/build/test.targets @@ -3,8 +3,13 @@ 6.8.0 - 4.18.3 - 2.4.5 + + + 4.20.69 + 2.5.0 @@ -15,14 +20,14 @@ - + - + - + From f8c6fe7448d8182ce511e18acfa812d165a9ffc8 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 11 Aug 2023 13:59:09 -0700 Subject: [PATCH 039/131] Enable NuGet symbol packages --- build/nuget.props | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/nuget.props b/build/nuget.props index c913c57b..c783c23a 100644 --- a/build/nuget.props +++ b/build/nuget.props @@ -26,9 +26,10 @@ + true + snupkg true true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb true From 0aeb3dd2e8c0a0492e52af2e76525beddd95fa45 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 11 Aug 2023 14:46:23 -0700 Subject: [PATCH 040/131] Fix $top in OData examples. Resolves #944 --- .../Configuration/PersonModelConfiguration.cs | 1 + .../Configuration/ProductConfiguration.cs | 4 +++- .../Configuration/SupplierConfiguration.cs | 6 +++++- .../OpenApiODataWebApiExample/Models/Order.cs | 1 + .../V3/SuppliersController.cs | 2 +- .../Configuration/PersonModelConfiguration.cs | 1 + .../Configuration/ProductConfiguration.cs | 5 ++++- .../Configuration/SupplierConfiguration.cs | 7 ++++++- .../OData/ODataOpenApiExample/Models/Order.cs | 1 + .../ODataOpenApiExample/V3/SuppliersController.cs | 14 +++++++------- 10 files changed, 30 insertions(+), 12 deletions(-) diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/PersonModelConfiguration.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/PersonModelConfiguration.cs index 86759c34..aa3c7000 100644 --- a/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/PersonModelConfiguration.cs +++ b/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/PersonModelConfiguration.cs @@ -18,6 +18,7 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string rout person.HasKey( p => p.Id ); person.Select().OrderBy( "firstName", "lastName" ); + person.Page( maxTopValue: 100, pageSizeValue: default ); if ( apiVersion < ApiVersions.V3 ) { diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/ProductConfiguration.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/ProductConfiguration.cs index b6c151f0..edc33de9 100644 --- a/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/ProductConfiguration.cs +++ b/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/ProductConfiguration.cs @@ -18,8 +18,10 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string rout return; } - var product = builder.EntitySet( "Products" ).EntityType.HasKey( p => p.Id ); + var product = builder.EntitySet( "Products" ).EntityType; + product.HasKey( p => p.Id ); + product.Page( maxTopValue: 100, pageSizeValue: default ); product.Action( "Rate" ).Parameter( "stars" ); product.Collection.Action( "Rate" ).Parameter( "stars" ); } diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/SupplierConfiguration.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/SupplierConfiguration.cs index f6e2f817..b1f91df2 100644 --- a/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/SupplierConfiguration.cs +++ b/examples/AspNet/OData/OpenApiODataWebApiExample/Configuration/SupplierConfiguration.cs @@ -18,7 +18,11 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string rout return; } - builder.EntitySet( "Suppliers" ).EntityType.HasKey( p => p.Id ); + var supplier = builder.EntitySet( "Suppliers" ).EntityType; + + supplier.HasKey( p => p.Id ); + supplier.Page( maxTopValue: 100, pageSizeValue: default ); + builder.Singleton( "Acme" ); } } \ No newline at end of file diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/Models/Order.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/Models/Order.cs index 6ed326dc..e3f15cc4 100644 --- a/examples/AspNet/OData/OpenApiODataWebApiExample/Models/Order.cs +++ b/examples/AspNet/OData/OpenApiODataWebApiExample/Models/Order.cs @@ -7,6 +7,7 @@ /// /// Represents an order. /// +[Page( MaxTop = 100 )] [Select] [Select( "effectiveDate", SelectType = SelectExpandType.Disabled )] public class Order diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/V3/SuppliersController.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/V3/SuppliersController.cs index 6e1412da..af837960 100644 --- a/examples/AspNet/OData/OpenApiODataWebApiExample/V3/SuppliersController.cs +++ b/examples/AspNet/OData/OpenApiODataWebApiExample/V3/SuppliersController.cs @@ -118,7 +118,7 @@ public IHttpActionResult Put( [FromODataUri] int key, [FromBody] Supplier update /// /// The supplier identifier. /// The associated supplier products. - [EnableQuery] + [EnableQuery( MaxTop = 100 )] public IQueryable GetProducts( [FromODataUri] int key ) => suppliers.Where( s => s.Id == key ).SelectMany( s => s.Products ); diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/PersonModelConfiguration.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/PersonModelConfiguration.cs index 5bae1fe3..98d8b060 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/PersonModelConfiguration.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/PersonModelConfiguration.cs @@ -18,6 +18,7 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string rout person.HasKey( p => p.Id ); person.Select().OrderBy( "firstName", "lastName" ); + person.Page( maxTopValue: 100, pageSizeValue: default ); if ( apiVersion < ApiVersions.V3 ) { diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/ProductConfiguration.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/ProductConfiguration.cs index e8936359..88f7d83a 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/ProductConfiguration.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/ProductConfiguration.cs @@ -18,6 +18,9 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string rout return; } - var product = builder.EntitySet( "Products" ).EntityType.HasKey( p => p.Id ); + var product = builder.EntitySet( "Products" ).EntityType; + + product.HasKey( p => p.Id ); + product.Page( maxTopValue: 100, pageSizeValue: default ); } } \ No newline at end of file diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/SupplierConfiguration.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/SupplierConfiguration.cs index 7a803487..e8f1f9e0 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/SupplierConfiguration.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/Configuration/SupplierConfiguration.cs @@ -18,7 +18,12 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string rout return; } - builder.EntitySet( "Suppliers" ).EntityType.HasKey( p => p.Id ); + var supplier = builder.EntitySet( "Suppliers" ).EntityType; + + + supplier.HasKey( p => p.Id ); + supplier.Page( maxTopValue: 100, pageSizeValue: default ); + builder.Singleton( "Acme" ); } } \ No newline at end of file diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Models/Order.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Models/Order.cs index 009e9862..1df9452e 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/Models/Order.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/Models/Order.cs @@ -6,6 +6,7 @@ /// /// Represents an order. /// +[Page( MaxTop = 100 )] [Select] [Select( "effectiveDate", SelectType = SelectExpandType.Disabled )] public class Order diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs b/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs index 401e1a59..652b4d4e 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/V3/SuppliersController.cs @@ -17,9 +17,9 @@ public class SuppliersController : ODataController { private readonly IQueryable suppliers = new[] - { - NewSupplier( 1 ), - NewSupplier( 2 ), + { + NewSupplier( 1 ), + NewSupplier( 2 ), NewSupplier( 3 ), }.AsQueryable(); @@ -46,7 +46,7 @@ public class SuppliersController : ODataController [Produces( "application/json" )] [ProducesResponseType( typeof( Supplier ), Status200OK )] [ProducesResponseType( Status404NotFound )] - public SingleResult Get( int key ) => + public SingleResult Get( int key ) => SingleResult.Create( suppliers.Where( p => p.Id == key ) ); /// @@ -147,7 +147,7 @@ public IActionResult Put( int key, [FromBody] Supplier update ) /// The supplier identifier. /// The associated supplier products. [HttpGet] - [EnableQuery] + [EnableQuery( MaxTop = 100 )] public IQueryable GetProducts( int key ) => suppliers.Where( s => s.Id == key ).SelectMany( s => s.Products ); @@ -181,8 +181,8 @@ public IActionResult CreateRef( [ProducesResponseType( Status204NoContent )] [ProducesResponseType( Status404NotFound )] public IActionResult DeleteRef( - int key, - int relatedKey, + int key, + int relatedKey, string navigationProperty ) => NoContent(); private static Supplier NewSupplier( int id ) => From 338d1da47e3cbb70088c19fc473eafa2da3aab2f Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 11 Aug 2023 18:06:58 -0700 Subject: [PATCH 041/131] Update to new code signing service --- .config/dotnet-tools.json | 6 +++--- build/signing.json | 13 ------------- build/steps-release.yml | 35 +++++++++++++---------------------- 3 files changed, 16 insertions(+), 38 deletions(-) delete mode 100644 build/signing.json diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index b0c48605..a05db39d 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -2,10 +2,10 @@ "version": 1, "isRoot": true, "tools": { - "signclient": { - "version": "1.3.155", + "sign": { + "version": "0.9.1-beta.23356.1", "commands": [ - "SignClient" + "sign" ] } } diff --git a/build/signing.json b/build/signing.json deleted file mode 100644 index 3276a45d..00000000 --- a/build/signing.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "SignClient": { - "AzureAd": { - "AADInstance": "https://login.microsoftonline.com/", - "ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8", - "TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e" - }, - "Service": { - "Url": "https://codesign.dotnetfoundation.org/", - "ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001" - } - } -} \ No newline at end of file diff --git a/build/steps-release.yml b/build/steps-release.yml index 60fd3c3a..d2a884a5 100644 --- a/build/steps-release.yml +++ b/build/steps-release.yml @@ -25,32 +25,23 @@ steps: - script: dotnet tool restore displayName: Restore Tools -- pwsh: > - Compress-Archive - -Path $(Build.ArtifactStagingDirectory)/packages/* - -DestinationPath $(Build.ArtifactStagingDirectory)/packages.zip - displayName: Package Artifacts for Signing - - script: > - dotnet signclient sign - --config build/signing.json - --input $(Build.ArtifactStagingDirectory)/packages.zip - --user "$(codesign_user)" - --secret "$(codesign_secret)" - --name "ASP.NET API Versioning" - --description "Adds versioning semantics to APIs built with ASP.NET" - --descriptionUrl "https://github.com/dotnet/aspnet-api-versioning" + dotnet sign code azure-key-vault "*.nupkg" ` + --base-directory "$(Build.ArtifactStagingDirectory)/packages" ` + --publisher-name "ASP.NET API Versioning" ` + --description "Adds versioning semantics to APIs built with ASP.NET" ` + --description-url "https://github.com/dotnet/aspnet-api-versioning" ` + --azure-key-vault-tenant-id "$(SignTenantId)" ` + --azure-key-vault-client-id "$(SignClientId)" ` + --azure-key-vault-client-secret '$(SignClientSecret)' ` + --azure-key-vault-certificate "$(SignKeyVaultCertificate)" ` + --azure-key-vault-url "$(SignKeyVaultUrl)" + --timestamp-url http://timestamp.digicert.com displayName: Sign Artifacts -- pwsh: > - Expand-Archive - -Path $(Build.ArtifactStagingDirectory)/packages.zip - -DestinationPath $(Build.ArtifactStagingDirectory)/signed-packages - displayName: Extract Signed Artifacts - - task: PublishBuildArtifacts@1 - displayName: Publish package artifacts + displayName: Publish Artifacts inputs: - pathToPublish: $(Build.ArtifactStagingDirectory)/signed-packages + pathToPublish: $(Build.ArtifactStagingDirectory)/packages publishLocation: Container artifactName: NuGet Packages \ No newline at end of file From 558bc1f24fa0d07c79d066774d1cc1b62a20f1b9 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 11 Aug 2023 18:24:11 -0700 Subject: [PATCH 042/131] Bump patch version and add release notes --- .../Asp.Versioning.WebApi.OData.ApiExplorer.csproj | 2 +- .../Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt | 2 +- .../Asp.Versioning.WebApi.OData.csproj | 2 +- .../OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt | 2 +- .../Asp.Versioning.WebApi.ApiExplorer.csproj | 2 +- .../src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt | 2 +- .../src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj | 2 +- src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt | 4 +++- .../Asp.Versioning.OData.ApiExplorer.csproj | 2 +- .../src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt | 2 +- .../src/Asp.Versioning.OData/Asp.Versioning.OData.csproj | 2 +- .../OData/src/Asp.Versioning.OData/ReleaseNotes.txt | 2 +- .../src/Asp.Versioning.Http/Asp.Versioning.Http.csproj | 2 +- .../WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt | 5 ++++- .../Asp.Versioning.Mvc.ApiExplorer.csproj | 2 +- .../src/Asp.Versioning.Mvc.ApiExplorer/ReleaseNotes.txt | 2 +- .../WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj | 2 +- .../WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt | 2 +- .../Asp.Versioning.Http.Client.csproj | 2 +- src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt | 2 +- 20 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj index 5e8a0bc2..f5978313 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj @@ -1,7 +1,7 @@  - 7.0.3 + 7.0.4 7.0.0.0 net45;net472 Asp.Versioning diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt index a6d1afd0..1672692d 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ -Fix: Explore model per EDM + API version ([#996](https://github.com/dotnet/aspnet-api-versioning/issues/996)) \ No newline at end of file +Publish NuGet symbol package \ No newline at end of file diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj index 69c71dd7..370667d7 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj @@ -1,7 +1,7 @@  - 7.0.1 + 7.0.2 7.0.0.0 net45;net472 Asp.Versioning diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt index a997dbbc..1672692d 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt @@ -1 +1 @@ -Fix empty EDM detection \ No newline at end of file +Publish NuGet symbol package \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj index 5ded1914..df2190eb 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj @@ -1,7 +1,7 @@  - 7.0.0 + 7.0.1 7.0.0.0 net45;net472 ASP.NET Web API Versioning API Explorer diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt index 5f282702..1672692d 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +Publish NuGet symbol package \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj index 0d99e781..80798dae 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj @@ -1,7 +1,7 @@  - 7.0.0 + 7.0.1 7.0.0.0 net45;net472 ASP.NET Web API Versioning diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt index 5f282702..17684cfc 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt @@ -1 +1,3 @@ - \ No newline at end of file +Publish NuGet symbol package +Resolved [#1015](https://github.com/dotnet/aspnet-api-versioning/issues/1011) +Fixed [#1017](https://github.com/dotnet/aspnet-api-versioning/issues/1017) \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj index de83304c..62d2a48e 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj @@ -1,7 +1,7 @@  - 7.0.3 + 7.0.4 7.0.0.0 net7.0 Asp.Versioning diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt index a6d1afd0..1672692d 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ -Fix: Explore model per EDM + API version ([#996](https://github.com/dotnet/aspnet-api-versioning/issues/996)) \ No newline at end of file +Publish NuGet symbol package \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj index f0eabecd..7e1ac67c 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj @@ -1,7 +1,7 @@  - 7.0.1 + 7.0.2 7.0.0.0 net7.0 Asp.Versioning diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt b/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt index a997dbbc..1672692d 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt @@ -1 +1 @@ -Fix empty EDM detection \ No newline at end of file +Publish NuGet symbol package \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj index 4dc9d40d..9a6b5ea8 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj @@ -1,7 +1,7 @@  - 7.0.0 + 7.0.1 7.0.0.0 net7.0 Asp.Versioning diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt index 5f282702..54defc74 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt @@ -1 +1,4 @@ - \ No newline at end of file +Publish NuGet symbol package +Resolved [#1011](https://github.com/dotnet/aspnet-api-versioning/issues/1011) +Fixed [#1015](https://github.com/dotnet/aspnet-api-versioning/issues/1015) +Fixed [#1017](https://github.com/dotnet/aspnet-api-versioning/issues/1017) \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj index ca73aefc..e696251b 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj @@ -1,7 +1,7 @@  - 7.0.0 + 7.0.1 7.0.0.0 net7.0 Asp.Versioning.ApiExplorer diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ReleaseNotes.txt b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ReleaseNotes.txt index 5f282702..1672692d 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +Publish NuGet symbol package \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj index d3f07dc9..44a4dde0 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj @@ -1,7 +1,7 @@  - 7.0.0 + 7.0.1 7.0.0.0 net7.0 Asp.Versioning diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt index 5f282702..1672692d 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +Publish NuGet symbol package \ No newline at end of file diff --git a/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj b/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj index 42d38f19..836c26f6 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj +++ b/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj @@ -1,7 +1,7 @@  - 7.0.0 + 7.0.1 7.0.0.0 net7.0;netstandard1.1;netstandard2.0 Asp.Versioning.Http diff --git a/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt b/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt index 5f282702..1672692d 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt +++ b/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +Publish NuGet symbol package \ No newline at end of file From 93edb8a0740b8fd8f92a5e2c26e8261b44496fbc Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 14 Aug 2023 10:24:52 -0700 Subject: [PATCH 043/131] Remove back tick; it's shell not PowerShell --- build/steps-release.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build/steps-release.yml b/build/steps-release.yml index d2a884a5..50a7e9bf 100644 --- a/build/steps-release.yml +++ b/build/steps-release.yml @@ -26,15 +26,15 @@ steps: displayName: Restore Tools - script: > - dotnet sign code azure-key-vault "*.nupkg" ` - --base-directory "$(Build.ArtifactStagingDirectory)/packages" ` - --publisher-name "ASP.NET API Versioning" ` - --description "Adds versioning semantics to APIs built with ASP.NET" ` - --description-url "https://github.com/dotnet/aspnet-api-versioning" ` - --azure-key-vault-tenant-id "$(SignTenantId)" ` - --azure-key-vault-client-id "$(SignClientId)" ` - --azure-key-vault-client-secret '$(SignClientSecret)' ` - --azure-key-vault-certificate "$(SignKeyVaultCertificate)" ` + dotnet sign code azure-key-vault "*.nupkg" + --base-directory "$(Build.ArtifactStagingDirectory)/packages" + --publisher-name "ASP.NET API Versioning" + --description "Adds versioning semantics to APIs built with ASP.NET" + --description-url "https://github.com/dotnet/aspnet-api-versioning" + --azure-key-vault-tenant-id "$(SignTenantId)" + --azure-key-vault-client-id "$(SignClientId)" + --azure-key-vault-client-secret "$(SignClientSecret)" + --azure-key-vault-certificate "$(SignKeyVaultCertificate)" --azure-key-vault-url "$(SignKeyVaultUrl)" --timestamp-url http://timestamp.digicert.com displayName: Sign Artifacts From b294c94237f6679e8b00bb81de6d30b47811d679 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 28 Aug 2023 12:41:46 -0700 Subject: [PATCH 044/131] Fix code hygenie --- .../HttpServerFixture.cs | 3 ++ .../TestApplicationPart.cs | 33 +++++++++---------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/HttpServerFixture.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/HttpServerFixture.cs index 22b3f0ad..57c872e1 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/HttpServerFixture.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/HttpServerFixture.cs @@ -1,5 +1,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. + +// Ignore Spelling: app +// Ignore Spelling: Mvc namespace Asp.Versioning; using Microsoft.AspNetCore.Builder; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/TestApplicationPart.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/TestApplicationPart.cs index e107ef34..03c8f591 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/TestApplicationPart.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/TestApplicationPart.cs @@ -1,27 +1,26 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -namespace Asp.Versioning -{ - using Microsoft.AspNetCore.Mvc.ApplicationParts; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; +namespace Asp.Versioning; + +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; - internal sealed class TestApplicationPart : ApplicationPart, IApplicationPartTypeProvider - { - public TestApplicationPart() => Types = Enumerable.Empty(); +internal sealed class TestApplicationPart : ApplicationPart, IApplicationPartTypeProvider +{ + public TestApplicationPart() => Types = Enumerable.Empty(); - public TestApplicationPart( params TypeInfo[] types ) => Types = types; + public TestApplicationPart( params TypeInfo[] types ) => Types = types; - public TestApplicationPart( IEnumerable types ) => Types = types; + public TestApplicationPart( IEnumerable types ) => Types = types; - public TestApplicationPart( IEnumerable types ) : this( types.Select( t => t.GetTypeInfo() ) ) { } + public TestApplicationPart( IEnumerable types ) : this( types.Select( t => t.GetTypeInfo() ) ) { } - public TestApplicationPart( params Type[] types ) : this( types.Select( t => t.GetTypeInfo() ) ) { } + public TestApplicationPart( params Type[] types ) : this( types.Select( t => t.GetTypeInfo() ) ) { } - public override string Name => "Test Part"; + public override string Name => "Test Part"; - public IEnumerable Types { get; } - } + public IEnumerable Types { get; } } \ No newline at end of file From 7d3e38eb908c067c3f1c071bd9d99fea2dfb60f9 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 28 Aug 2023 12:42:29 -0700 Subject: [PATCH 045/131] Fix JSON serialization --- src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetails.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetails.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetails.cs index b6a27396..e3640c0e 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetails.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetails.cs @@ -32,7 +32,7 @@ public class ProblemDetails /// (e.g., using HTML [W3C.REC-html5-20141028]). When this member is not present, its value is assumed to be /// "about:blank". /// - [JsonProperty( "type" )] + [JsonProperty( "type", NullValueHandling = Ignore )] public string? Type { get; set; } /// @@ -72,5 +72,6 @@ public class ProblemDetails /// The round-tripping behavior for is determined by the implementation of the Input \ Output formatters. /// In particular, complex types or collection types may not round-trip to the original type when using the built-in JSON or XML formatters. /// + [JsonExtensionData] public IDictionary Extensions => extensions; } \ No newline at end of file From 8a85e5e09e055b2c571242fbf1bae6b4adfe0eb6 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 29 Aug 2023 13:12:56 -0700 Subject: [PATCH 046/131] Support error object backward compatibility. Related to #1019 --- .../Http/Basic/InteropFixture.cs | 15 ++ .../when error objects are enabled.cs | 51 +++++ .../Asp.Versioning.WebApi.csproj | 4 +- .../ErrorObjectFactory.cs | 94 +++++++++ .../ProblemDetailsMediaTypeFormatter.cs | 69 +++++++ .../ProblemDetailsFactory.cs | 37 ++-- .../Asp.Versioning.WebApi/ReleaseNotes.txt | 4 +- .../HttpRequestMessageExtensions.cs | 15 +- .../HttpConfigurationExtensions.cs | 43 ++++ .../Http/MinimalApiFixture.cs | 2 +- .../Asp.Versioning.Http.csproj | 4 +- .../Asp.Versioning.Http/ErrorObjectWriter.cs | 184 ++++++++++++++++++ .../src/Asp.Versioning.Http/ReleaseNotes.txt | 5 +- .../ErrorObjectWriterTest.cs | 129 ++++++++++++ 14 files changed, 628 insertions(+), 28 deletions(-) create mode 100644 src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/InteropFixture.cs create mode 100644 src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/given a versioned ApiController/when error objects are enabled.cs create mode 100644 src/AspNet/WebApi/src/Asp.Versioning.WebApi/ErrorObjectFactory.cs create mode 100644 src/AspNet/WebApi/src/Asp.Versioning.WebApi/Formatting/ProblemDetailsMediaTypeFormatter.cs create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs create mode 100644 src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/InteropFixture.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/InteropFixture.cs new file mode 100644 index 00000000..b61d1d68 --- /dev/null +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/InteropFixture.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +// Ignore Spelling: Interop +namespace Asp.Versioning.Http.Basic; + +using System.Web.Http; + +public class InteropFixture : BasicFixture +{ + protected override void OnConfigure( HttpConfiguration configuration ) + { + configuration.ConvertProblemDetailsToErrorObject(); + base.OnConfigure( configuration ); + } +} \ No newline at end of file diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/given a versioned ApiController/when error objects are enabled.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/given a versioned ApiController/when error objects are enabled.cs new file mode 100644 index 00000000..b40bfa14 --- /dev/null +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/Basic/given a versioned ApiController/when error objects are enabled.cs @@ -0,0 +1,51 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace given_a_versioned_ApiController; + +using Asp.Versioning; +using Asp.Versioning.Http.Basic; + +public class when_error_objects_are_enabled : AcceptanceTest, IClassFixture +{ + [Fact] + public async Task then_the_response_should_not_be_problem_details() + { + // arrange + var example = new + { + error = new + { + code = default( string ), + message = default( string ), + target = default( string ), + innerError = new + { + message = default( string ), + }, + }, + }; + + // act + var response = await GetAsync( "api/values?api-version=3.0" ); + var error = await response.Content.ReadAsExampleAsync( example ); + + // assert + response.Content.Headers.ContentType.MediaType.Should().Be( "application/json" ); + error.Should().BeEquivalentTo( + new + { + error = new + { + code = "UnsupportedApiVersion", + message = "Unsupported API version", + innerError = new + { + message = "No route providing a controller name with API version '3.0' " + + "was found to match request URI 'http://localhost/api/values'.", + }, + }, + } ); + } + + public when_error_objects_are_enabled( InteropFixture fixture ) : base( fixture ) { } +} \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj index 80798dae..03f348dd 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Asp.Versioning.WebApi.csproj @@ -1,8 +1,8 @@  - 7.0.1 - 7.0.0.0 + 7.1.0 + 7.1.0.0 net45;net472 ASP.NET Web API Versioning A service API versioning library for Microsoft ASP.NET Web API. diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ErrorObjectFactory.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ErrorObjectFactory.cs new file mode 100644 index 00000000..dd8b3689 --- /dev/null +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ErrorObjectFactory.cs @@ -0,0 +1,94 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +// REF: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/Infrastructure/DefaultProblemDetailsFactory.cs +namespace Asp.Versioning; + +using Newtonsoft.Json; +using static Asp.Versioning.ProblemDetailsDefaults; +using static Newtonsoft.Json.NullValueHandling; + +internal sealed class ErrorObjectFactory : IProblemDetailsFactory +{ + public ProblemDetails CreateProblemDetails( + HttpRequestMessage request, + int? statusCode = null, + string? title = null, + string? type = null, + string? detail = null, + string? instance = null ) + { + var status = statusCode ?? 500; + ErrorObject? problem; + + if ( type == Ambiguous.Type ) + { + problem = NewError( title, instance ); + problem.Error.Code = Ambiguous.Code; + } + else if ( type == Invalid.Type ) + { + problem = NewError( title, instance ); + problem.Error.Code = Invalid.Code; + return ProblemDetailsFactory.AddInvalidExtensions( request, status, problem, ApplyMessage ); + } + else if ( type == Unspecified.Type ) + { + problem = NewError( title, instance ); + problem.Error.Code = Unspecified.Code; + } + else if ( type == Unsupported.Type ) + { + problem = NewError( title, instance ); + problem.Error.Code = Unsupported.Code; + return ProblemDetailsFactory.AddUnsupportedExtensions( request, status, problem, ApplyMessage ); + } + + return ProblemDetailsFactory.Default.CreateProblemDetails( + request, + statusCode, + title, + type, + detail, + instance ); + } + + private static ErrorObject NewError( string? message, string? target ) => + new() + { + Error = + { + Message = message, + Target = target, + }, + }; + + private static void ApplyMessage( ErrorObject obj, string message ) => + obj.Error.InnerError = new() { Message = message }; + + private sealed class ErrorObject : ProblemDetails + { + [JsonProperty( "error" )] + public ErrorDetail Error { get; } = new(); + } + + private sealed class ErrorDetail + { + [JsonProperty( "code", NullValueHandling = Ignore )] + public string? Code { get; set; } + + [JsonProperty( "message", NullValueHandling = Ignore )] + public string? Message { get; set; } + + [JsonProperty( "target", NullValueHandling = Ignore )] + public string? Target { get; set; } + + [JsonProperty( "innerError", NullValueHandling = Ignore )] + public InnerError? InnerError { get; set; } + } + + private sealed class InnerError + { + [JsonProperty( "message", NullValueHandling = Ignore )] + public string? Message { get; set; } + } +} \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Formatting/ProblemDetailsMediaTypeFormatter.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Formatting/ProblemDetailsMediaTypeFormatter.cs new file mode 100644 index 00000000..cf44d707 --- /dev/null +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Formatting/ProblemDetailsMediaTypeFormatter.cs @@ -0,0 +1,69 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +// REF: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/Infrastructure/DefaultProblemDetailsFactory.cs +namespace Asp.Versioning.Formatting; + +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using static Asp.Versioning.ProblemDetailsDefaults; + +/// +/// Represents a media type formatter for problem details based on https://tools.ietf.org/html/rfc7807. +/// +public class ProblemDetailsMediaTypeFormatter : MediaTypeFormatter +{ + private readonly JsonMediaTypeFormatter json; + + /// + /// Initializes a new instance of the class. + /// + public ProblemDetailsMediaTypeFormatter() : this( new() ) { } + + /// + /// Initializes a new instance of the class. + /// + /// The existing instance to derive from. + public ProblemDetailsMediaTypeFormatter( JsonMediaTypeFormatter formatter ) + : base( formatter ) + { + json = formatter; + SupportedEncodings.Add( new UTF8Encoding( encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true ) ); + SupportedEncodings.Add( new UnicodeEncoding( bigEndian: false, byteOrderMark: true, throwOnInvalidBytes: true ) ); + SupportedMediaTypes.Add( DefaultMediaType ); + } + + /// + /// Gets the default media type. + /// + /// Returns the media type for application/problem+json. + public static MediaTypeHeaderValue DefaultMediaType { get; } = MediaTypeHeaderValue.Parse( MediaType.Json ); + + /// + public override bool CanReadType( Type type ) => false; + + /// + public override bool CanWriteType( Type type ) => typeof( ProblemDetails ).IsAssignableFrom( type ); + + /// + public override Task WriteToStreamAsync( + Type type, + object value, + Stream writeStream, + HttpContent content, + TransportContext transportContext, + CancellationToken cancellationToken ) => + json.WriteToStreamAsync( type, value, writeStream, content, transportContext, cancellationToken ); + + /// + public override void SetDefaultContentHeaders( Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType ) + { + mediaType.MediaType = DefaultMediaType.MediaType; + base.SetDefaultContentHeaders( type, headers, mediaType ); + } +} \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetailsFactory.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetailsFactory.cs index 3bb429dd..30d28c9c 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetailsFactory.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetailsFactory.cs @@ -10,9 +10,13 @@ namespace Asp.Versioning; internal sealed class ProblemDetailsFactory : IProblemDetailsFactory { - private static ProblemDetailsFactory? @default; + private static IProblemDetailsFactory? @default; - public static IProblemDetailsFactory Default => @default ??= new(); + public static IProblemDetailsFactory Default + { + get => @default ??= new ProblemDetailsFactory(); + set => @default = value; + } public ProblemDetails CreateProblemDetails( HttpRequestMessage request, @@ -40,7 +44,7 @@ public ProblemDetails CreateProblemDetails( else if ( type == Invalid.Type ) { problemDetails.Code = Invalid.Code; - return AddInvalidExtensions( request, status, problemDetails ); + return AddInvalidExtensions( request, status, problemDetails, ApplyMessage ); } else if ( type == Unspecified.Type ) { @@ -49,13 +53,17 @@ public ProblemDetails CreateProblemDetails( else if ( type == Unsupported.Type ) { problemDetails.Code = Unsupported.Code; - return AddUnsupportedExtensions( request, status, problemDetails ); + return AddUnsupportedExtensions( request, status, problemDetails, ApplyMessage ); } return problemDetails; } - private static ProblemDetailsEx AddInvalidExtensions( HttpRequestMessage request, int status, ProblemDetailsEx problemDetails ) + internal static T AddInvalidExtensions( + HttpRequestMessage request, + int status, + T problemDetails, + Action applyMessage ) where T : ProblemDetails { if ( status != 400 || !request.ShouldIncludeErrorDetail() ) { @@ -64,14 +72,18 @@ private static ProblemDetailsEx AddInvalidExtensions( HttpRequestMessage request var safeUrl = request.RequestUri.SafeFullPath(); var requestedVersion = request.ApiVersionProperties().RawRequestedApiVersion; - var error = string.Format( CurrentCulture, SR.VersionedControllerNameNotFound, safeUrl, requestedVersion ); + var message = string.Format( CurrentCulture, SR.VersionedControllerNameNotFound, safeUrl, requestedVersion ); - problemDetails.Error = error; + applyMessage( problemDetails, message ); return problemDetails; } - private static ProblemDetailsEx AddUnsupportedExtensions( HttpRequestMessage request, int status, ProblemDetailsEx problemDetails ) + internal static T AddUnsupportedExtensions( + HttpRequestMessage request, + int status, + T problemDetails, + Action applyMessage ) where T : ProblemDetails { if ( !request.ShouldIncludeErrorDetail() ) { @@ -95,14 +107,17 @@ private static ProblemDetailsEx AddUnsupportedExtensions( HttpRequestMessage req var safeUrl = request.RequestUri.SafeFullPath(); var requestedMethod = request.Method; - var version = request.GetRequestedApiVersion()?.ToString() ?? "(null)"; - var error = string.Format( CurrentCulture, messageFormat, safeUrl, version, requestedMethod ); + var version = request.ApiVersionProperties().RawRequestedApiVersion ?? "(null)"; + var message = string.Format( CurrentCulture, messageFormat, safeUrl, version, requestedMethod ); - problemDetails.Error = error; + applyMessage( problemDetails, message ); return problemDetails; } + private static void ApplyMessage( ProblemDetailsEx problemDetails, string message ) => + problemDetails.Error = message; + private sealed class ProblemDetailsEx : ProblemDetails { [JsonProperty( "code", NullValueHandling = Ignore )] diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt index 17684cfc..5cdc847a 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt @@ -1,3 +1 @@ -Publish NuGet symbol package -Resolved [#1015](https://github.com/dotnet/aspnet-api-versioning/issues/1011) -Fixed [#1017](https://github.com/dotnet/aspnet-api-versioning/issues/1017) \ No newline at end of file +Add backward compatibility for error objects \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs index 8b2920fd..b1f43605 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs @@ -122,14 +122,19 @@ internal static Tuple GetProblemDetail { var configuration = request.GetConfiguration(); var negotiator = configuration.Services.GetContentNegotiator(); - var result = negotiator.Negotiate( typeof( ProblemDetails ), request, configuration.Formatters ) ?? - new( configuration.Formatters.JsonFormatter ?? new(), MediaTypeHeaderValue.Parse( "application/problem+json" ) ); + var result = negotiator.Negotiate( typeof( ProblemDetails ), request, configuration.Formatters ); return result.MediaType.MediaType switch { - "application/json" => Tuple.Create( MediaTypeHeaderValue.Parse( "application/problem+json" ), result.Formatter ), - "application/xml" => Tuple.Create( MediaTypeHeaderValue.Parse( "application/problem+xml" ), result.Formatter ), - _ => Tuple.Create( result.MediaType, result.Formatter ), + null => Tuple.Create( + MediaTypeHeaderValue.Parse( ProblemDetailsDefaults.MediaType.Json ), + (MediaTypeFormatter) ( configuration.Formatters.JsonFormatter ?? new() ) ), + "application/xml" => Tuple.Create( + MediaTypeHeaderValue.Parse( ProblemDetailsDefaults.MediaType.Xml ), + result.Formatter ), + _ => Tuple.Create( + result.MediaType, + result.Formatter ), }; } } \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs index ebd1a991..8a225a96 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs @@ -5,6 +5,7 @@ namespace System.Web.Http; using Asp.Versioning; using Asp.Versioning.Controllers; using Asp.Versioning.Dispatcher; +using Asp.Versioning.Formatting; using System.Globalization; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; @@ -32,6 +33,24 @@ public static ApiVersioningOptions GetApiVersioningOptions( this HttpConfigurati return (ApiVersioningOptions) configuration.Properties.GetOrAdd( ApiVersioningOptionsKey, key => new ApiVersioningOptions() ); } + /// + /// Converts problem details into error objects. + /// + /// The current configuration. + /// This enables backward compatibility by converting into Error Objects that + /// conform to the Error Responses + /// in the Microsoft REST API Guidelines and + /// OData Error Responses. + public static void ConvertProblemDetailsToErrorObject( this HttpConfiguration configuration ) + { + if ( configuration == null ) + { + throw new ArgumentNullException( nameof( configuration ) ); + } + + configuration.Initializer += EnableErrorObjectResponses; + } + /// /// Adds service API versioning to the specified services collection. /// @@ -96,6 +115,8 @@ private static void AddApiVersioning( this HttpConfiguration configuration, ApiV configuration.Properties.AddOrUpdate( ApiVersioningOptionsKey, options, ( key, oldValue ) => options ); configuration.ParameterBindingRules.Add( typeof( ApiVersion ), ApiVersionParameterBinding.Create ); + configuration.Formatters.Insert( 0, new ProblemDetailsMediaTypeFormatter( configuration.Formatters.JsonFormatter ?? new() ) ); + SunsetPolicyManager.Default = new SunsetPolicyManager( options ); } @@ -123,6 +144,28 @@ private static void ValidateApiVersioningOptions( ApiVersioningOptions options ) } } + private static void EnableErrorObjectResponses( HttpConfiguration configuration ) + { + ProblemDetailsFactory.Default = new ErrorObjectFactory(); + + var formatters = configuration.Formatters; + var problemDetails = ProblemDetailsMediaTypeFormatter.DefaultMediaType; + + for ( var i = 0; i < formatters.Count; i++ ) + { + var mediaTypes = formatters[i].SupportedMediaTypes; + + for ( var j = 0; j < mediaTypes.Count; j++ ) + { + if ( mediaTypes[j].Equals( problemDetails ) ) + { + formatters.RemoveAt( i ); + return; + } + } + } + } + internal static IReportApiVersions GetApiVersionReporter( this HttpConfiguration configuration ) { var options = configuration.GetApiVersioningOptions(); diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs index f0b841c9..b568ff84 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs @@ -1,6 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -namespace Asp.Versioning; +namespace Asp.Versioning.Http; using Asp.Versioning.Conventions; using Microsoft.AspNetCore.Builder; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj index 9a6b5ea8..516d250a 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj @@ -1,8 +1,8 @@  - 7.0.1 - 7.0.0.0 + 7.1.0 + 7.1.0.0 net7.0 Asp.Versioning ASP.NET Core API Versioning diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs new file mode 100644 index 00000000..1b16255a --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs @@ -0,0 +1,184 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning; + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System.Text.Json.Serialization; +using static System.Text.Json.Serialization.JsonIgnoreCondition; + +/// +/// Represents a problem details writer that outputs error objects in responses. +/// +/// This enables backward compatibility by converting into Error Objects that +/// conform to the Error Responses +/// in the Microsoft REST API Guidelines and +/// OData Error Responses. +[CLSCompliant( false )] +public class ErrorObjectWriter : IProblemDetailsWriter +{ + /// + public virtual bool CanWrite( ProblemDetailsContext context ) + { + ArgumentNullException.ThrowIfNull( context ); + + var type = context.ProblemDetails.Type; + + return type == ProblemDetailsDefaults.Unsupported.Type || + type == ProblemDetailsDefaults.Unspecified.Type || + type == ProblemDetailsDefaults.Invalid.Type || + type == ProblemDetailsDefaults.Ambiguous.Type; + } + + /// + public virtual ValueTask WriteAsync( ProblemDetailsContext context ) + { + ArgumentNullException.ThrowIfNull( context ); + + var response = context.HttpContext.Response; + var obj = new ErrorObject( context.ProblemDetails ); + + OnBeforeWrite( context, ref obj ); + + return new( response.WriteAsJsonAsync( obj ) ); + } + + /// + /// Occurs just before an error will be written. + /// + /// The current context. + /// The current error object. + /// Note to inheritors: The default implementation performs no action. + protected virtual void OnBeforeWrite( ProblemDetailsContext context, ref ErrorObject errorObject ) + { + } + +#pragma warning disable CA1815 // Override equals and operator equals on value types + + /// + /// Represents an error object. + /// + protected readonly struct ErrorObject + { + internal ErrorObject( ProblemDetails problemDetails ) => + Error = new( problemDetails ); + + /// + /// Gets the top-level error. + /// + /// The top-level error. + [JsonPropertyName( "error" )] + public ErrorDetail Error { get; } + } + + /// + /// Represents the error detail. + /// + protected readonly struct ErrorDetail + { + private readonly ProblemDetails problemDetails; + private readonly InnerError? innerError; + private readonly Dictionary extensions = new(); + + internal ErrorDetail( ProblemDetails problemDetails ) + { + this.problemDetails = problemDetails; + innerError = string.IsNullOrEmpty( problemDetails.Detail ) ? default : new InnerError( problemDetails ); + } + + /// + /// Gets or sets one of a server-defined set of error codes. + /// + /// A server-defined error code. + [JsonPropertyName( "code" )] + [JsonIgnore( Condition = WhenWritingNull )] + public string? Code + { + get => problemDetails.Extensions.TryGetValue( "code", out var value ) && + value is string code ? + code : + default; + set + { + if ( value is null ) + { + problemDetails.Extensions.Remove( "code" ); + } + else + { + problemDetails.Extensions["code"] = value; + } + } + } + + /// + /// Gets or sets the error message. + /// + /// A human-readable representation of the error. + [JsonPropertyName( "message" )] + [JsonIgnore( Condition = WhenWritingNull )] + public string? Message + { + get => problemDetails.Title; + set => problemDetails.Title = value; + } + + /// + /// Gets or sets the target of the error. + /// + /// The error target of the error. + [JsonPropertyName( "target" )] + [JsonIgnore( Condition = WhenWritingNull )] + public string? Target + { + get => problemDetails.Title; + set => problemDetails.Title = value; + } + + /// + /// Gets an object containing more specific information than the current object about the error, if any. + /// + /// The inner error or null. + [JsonPropertyName( "innerError" )] + [JsonIgnore( Condition = WhenWritingNull )] + public InnerError? InnerError => innerError; + + /// + /// Gets a collection of extension key/value pair members. + /// + /// A collection of extension key/value pair members. + [JsonExtensionData] + public IDictionary Extensions => extensions; + } + + /// + /// Represents an inner error. + /// + protected readonly struct InnerError + { + private readonly ProblemDetails problemDetails; + private readonly Dictionary extensions = new(); + + internal InnerError( ProblemDetails problemDetails ) => + this.problemDetails = problemDetails; + + /// + /// Gets or sets the inner error message. + /// + /// The inner error message. + [JsonPropertyName( "message" )] + [JsonIgnore( Condition = WhenWritingNull )] + public string? Message + { + get => problemDetails.Detail; + set => problemDetails.Detail = value; + } + + /// + /// Gets a collection of extension key/value pair members. + /// + /// A collection of extension key/value pair members. + [JsonExtensionData] + public IDictionary Extensions => extensions; + } +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt index 54defc74..5cdc847a 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt @@ -1,4 +1 @@ -Publish NuGet symbol package -Resolved [#1011](https://github.com/dotnet/aspnet-api-versioning/issues/1011) -Fixed [#1015](https://github.com/dotnet/aspnet-api-versioning/issues/1015) -Fixed [#1017](https://github.com/dotnet/aspnet-api-versioning/issues/1017) \ No newline at end of file +Add backward compatibility for error objects \ No newline at end of file diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs new file mode 100644 index 00000000..eecd6663 --- /dev/null +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs @@ -0,0 +1,129 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning; + +using Microsoft.AspNetCore.Http; +using System.Text.Json; +using System.Threading.Tasks; + +public class ErrorObjectWriterTest +{ + [Theory] + [InlineData( "https://docs.api-versioning.org/problems#unsupported" )] + [InlineData( "https://docs.api-versioning.org/problems#unspecified" )] + [InlineData( "https://docs.api-versioning.org/problems#invalid" )] + [InlineData( "https://docs.api-versioning.org/problems#ambiguous" )] + public void can_write_should_be_true_for_api_versioning_problem_types( string type ) + { + // arrange + var writer = new ErrorObjectWriter(); + var context = new ProblemDetailsContext() + { + HttpContext = new DefaultHttpContext(), + ProblemDetails = + { + Type = type, + }, + }; + + // act + var result = writer.CanWrite( context ); + + // assert + result.Should().BeTrue(); + } + + [Fact] + public void can_write_should_be_false_for_other_problem_types() + { + // arrange + const string BadRequest = "https://tools.ietf.org/html/rfc7231#section-6.5.1"; + var writer = new ErrorObjectWriter(); + var context = new ProblemDetailsContext() + { + HttpContext = new DefaultHttpContext(), + ProblemDetails = + { + Type = BadRequest, + }, + }; + + // act + var result = writer.CanWrite( context ); + + // assert + result.Should().BeFalse(); + } + + [Fact] + public async Task write_async_should_output_expected_json() + { + // arrange + var example = new + { + error = new + { + code = default( string ), + message = default( string ), + target = default( string ), + innerError = new + { + message = default( string ), + }, + }, + }; + + var writer = new ErrorObjectWriter(); + using var stream = new MemoryStream(); + var response = new Mock() { CallBase = true }; + var httpContext = new Mock() { CallBase = true }; + + response.SetupGet( r => r.Body ).Returns( stream ); + response.SetupProperty( r => r.ContentType ); + response.SetupGet(r => r.HttpContext).Returns(() => httpContext.Object ); + httpContext.SetupGet( c => c.Response ).Returns( response.Object ); + + var context = new ProblemDetailsContext() + { + HttpContext = httpContext.Object, + ProblemDetails = + { + Type = ProblemDetailsDefaults.Unsupported.Type, + Title = ProblemDetailsDefaults.Unsupported.Title, + Status = 400, + Detail = "The HTTP resource that matches the request URI 'https://tempuri.org' does not support the API version '42.0'.", + Extensions = + { + ["code"] = ProblemDetailsDefaults.Unsupported.Code, + }, + }, + }; + + // act + await writer.WriteAsync( context ); + + await stream.FlushAsync(); + stream.Position = 0; + + var error = await DeserializeByExampleAsync( stream, example ); + + // assert + response.Object.ContentType.Should().Be( "application/json; charset=utf-8" ); + error.Should().BeEquivalentTo( + new + { + error = new + { + code = "UnsupportedApiVersion", + message = "Unsupported API version", + innerError = new + { + message = "The HTTP resource that matches the request URI 'https://tempuri.org' does not support the API version '42.0'.", + }, + }, + } ); + } + + private static ValueTask DeserializeByExampleAsync( Stream stream, T example ) => + JsonSerializer.DeserializeAsync( stream ); +} \ No newline at end of file From b47216ea7e6445d43266f5a11d953a77ad1e49aa Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 29 Aug 2023 21:42:56 -0700 Subject: [PATCH 047/131] Fix spelling --- .../DependencyInjection/IApiVersioningBuilderExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs index ee74e9ff..63b73b46 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -1,5 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +// Ignore Spelling: Mvc namespace Microsoft.Extensions.DependencyInjection; using Asp.Versioning; From e29d885c7414bfab4dd0b693b47c5eed301e229d Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Tue, 29 Aug 2023 21:43:14 -0700 Subject: [PATCH 048/131] Suppress unused parameter; by design --- .../test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs index eecd6663..ddfe96c4 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs @@ -124,6 +124,8 @@ public async Task write_async_should_output_expected_json() } ); } +#pragma warning disable IDE0060 // Remove unused parameter private static ValueTask DeserializeByExampleAsync( Stream stream, T example ) => JsonSerializer.DeserializeAsync( stream ); +#pragma warning restore IDE0060 // Remove unused parameter } \ No newline at end of file From f680ece1fa1e8b293c785a3e4e94ed8053242823 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Thu, 31 Aug 2023 16:36:13 -0700 Subject: [PATCH 049/131] Refactor to use a default internal container and dependency resolver instead of static singletons --- .../VersionedMetadataControllerTest.cs | 1 + .../ApiExplorer/VersionedApiExplorer.cs | 4 +- .../ApiVersionRequestProperties.cs | 2 +- .../DefaultApiVersionReporter.cs | 5 -- .../Dependencies/DefaultContainer.cs | 84 +++++++++++++++++++ .../DependencyResolverExtensions.cs | 47 +++++++---- .../ApiVersionControllerSelector.cs | 4 +- .../Dispatcher/HttpControllerTypeCache.cs | 2 +- .../HttpResponseExceptionFactory.cs | 2 +- .../DoNotReportApiVersions.cs | 6 -- .../ErrorObjectFactory.cs | 2 +- .../ProblemDetailsFactory.cs | 17 ++-- .../ReportApiVersionsAttribute.cs | 9 +- .../Routing/ApiVersionRouteConstraint.cs | 2 +- .../src/Asp.Versioning.WebApi/SR.Designer.cs | 9 ++ .../WebApi/src/Asp.Versioning.WebApi/SR.resx | 3 + .../SunsetPolicyManager.cs | 7 -- .../HttpConfigurationExtensions.cs | 26 ++---- .../ReportApiVersionsAttributeTest.cs | 6 +- 19 files changed, 160 insertions(+), 78 deletions(-) create mode 100644 src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dependencies/DefaultContainer.cs diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs index 60292852..c39e10b4 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Controllers/VersionedMetadataControllerTest.cs @@ -37,6 +37,7 @@ public async Task options_should_return_expected_headers() configuration.AddApiVersioning( options => { + options.ReportApiVersions = true; options.Policies.Sunset( "VersionedMetadata" ) .Link( "policies" ) .Title( "Versioning Policy" ) diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs index 36b1de41..c7585844 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/VersionedApiExplorer.cs @@ -101,7 +101,7 @@ public IDocumentationProvider DocumentationProvider /// The configured sunset policy manager. protected ISunsetPolicyManager SunsetPolicyManager { - get => sunsetPolicyManager ??= Configuration.DependencyResolver.GetSunsetPolicyManager(); + get => sunsetPolicyManager ??= Configuration.GetSunsetPolicyManager(); set => sunsetPolicyManager = value; } @@ -227,7 +227,7 @@ protected virtual ApiDescriptionGroupCollection InitializeApiDescriptions() } var routes = FlattenRoutes( Configuration.Routes ).ToArray(); - var policyManager = Configuration.DependencyResolver.GetSunsetPolicyManager(); + var policyManager = Configuration.GetSunsetPolicyManager(); foreach ( var apiVersion in FlattenApiVersions( controllerMappings ) ) { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ApiVersionRequestProperties.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ApiVersionRequestProperties.cs index 284c66ba..0383fb9c 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ApiVersionRequestProperties.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ApiVersionRequestProperties.cs @@ -88,7 +88,7 @@ public ApiVersion? RequestedApiVersion return apiVersion; } - var parser = request.GetConfiguration().DependencyResolver.GetApiVersionParser(); + var parser = request.GetConfiguration().GetApiVersionParser(); try { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DefaultApiVersionReporter.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DefaultApiVersionReporter.cs index dc05e257..caa7f0be 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DefaultApiVersionReporter.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DefaultApiVersionReporter.cs @@ -10,11 +10,6 @@ namespace Asp.Versioning; /// public partial class DefaultApiVersionReporter { - private static DefaultApiVersionReporter? instance; - - internal static IReportApiVersions GetOrCreate( ISunsetPolicyManager sunsetPolicyManager ) => - instance ??= new( sunsetPolicyManager ); - private static void AddApiVersionHeader( HttpResponseHeaders headers, string headerName, IReadOnlyList versions ) { if ( versions.Count == 0 || headers.Contains( headerName ) ) diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dependencies/DefaultContainer.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dependencies/DefaultContainer.cs new file mode 100644 index 00000000..7725393c --- /dev/null +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dependencies/DefaultContainer.cs @@ -0,0 +1,84 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.Dependencies; + +using Asp.Versioning; +using Asp.Versioning.Conventions; +using System.ComponentModel.Design; +using System.Web.Http.Dependencies; + +internal sealed class DefaultContainer : IDependencyResolver, IDependencyScope +{ + private readonly ServiceContainer container = new(); + private bool disposed; + + internal DefaultContainer() + { + container.AddService( typeof( ApiVersioningOptions ), static ( sc, t ) => new ApiVersioningOptions() ); + container.AddService( typeof( IApiVersionParser ), static ( sc, t ) => ApiVersionParser.Default ); + container.AddService( typeof( IControllerNameConvention ), static ( sc, t ) => ControllerNameConvention.Default ); + container.AddService( typeof( IProblemDetailsFactory ), static ( sc, t ) => new ProblemDetailsFactory() ); + container.AddService( typeof( ISunsetPolicyManager ), NewSunsetPolicyManager ); + container.AddService( typeof( IReportApiVersions ), NewApiVersionReporter ); + } + + public ApiVersioningOptions ApiVersioningOptions + { + get => GetApiVersioningOptions( container ); + set + { + container.RemoveService( typeof( ApiVersioningOptions ) ); + container.AddService( typeof( ApiVersioningOptions ), value ); + } + } + + public void Replace( Type serviceType, ServiceCreatorCallback activator ) + { + container.RemoveService( serviceType ); + container.AddService( serviceType, activator ); + } + + public IDependencyScope BeginScope() => this; + + public void Dispose() + { + if ( disposed ) + { + return; + } + + disposed = true; + container.Dispose(); + } + + public object GetService( Type serviceType ) => container.GetService( serviceType ); + + public IEnumerable GetServices( Type serviceType ) + { + var service = container.GetService( serviceType ); + + if ( service is not null ) + { + yield return service; + } + } + + private static ApiVersioningOptions GetApiVersioningOptions( IServiceProvider serviceProvider ) => + (ApiVersioningOptions) serviceProvider.GetService( typeof( ApiVersioningOptions ) ); + + private static ISunsetPolicyManager NewSunsetPolicyManager( IServiceProvider serviceProvider, Type type ) => + new SunsetPolicyManager( GetApiVersioningOptions( serviceProvider ) ); + + private static IReportApiVersions NewApiVersionReporter( IServiceProvider serviceProvider, Type type ) + { + var options = GetApiVersioningOptions( serviceProvider ); + + if ( options.ReportApiVersions ) + { + var sunsetPolicyManager = (ISunsetPolicyManager) serviceProvider.GetService( typeof( ISunsetPolicyManager ) ); + return new DefaultApiVersionReporter( sunsetPolicyManager ); + } + + return new DoNotReportApiVersions(); + } +} \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs index f8b86bcc..6cad7f56 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DependencyResolverExtensions.cs @@ -3,24 +3,39 @@ namespace Asp.Versioning; using Asp.Versioning.Conventions; +using System.Globalization; +using System.Web.Http; using System.Web.Http.Dependencies; internal static class DependencyResolverExtensions { - internal static TService? GetService( this IDependencyResolver resolver ) => (TService) resolver.GetService( typeof( TService ) ); - - internal static IApiVersionParser GetApiVersionParser( this IDependencyResolver resolver ) => - resolver.GetService() ?? ApiVersionParser.Default; - - internal static IReportApiVersions GetApiVersionReporter( this IDependencyResolver resolver ) => - resolver.GetService() ?? DefaultApiVersionReporter.GetOrCreate( resolver.GetSunsetPolicyManager() ); - - internal static IControllerNameConvention GetControllerNameConvention( this IDependencyResolver resolver ) => - resolver.GetService() ?? ControllerNameConvention.Default; - - internal static IProblemDetailsFactory GetProblemDetailsFactory( this IDependencyResolver resolver ) => - resolver.GetService() ?? ProblemDetailsFactory.Default; - - internal static ISunsetPolicyManager GetSunsetPolicyManager( this IDependencyResolver resolver ) => - resolver.GetService() ?? SunsetPolicyManager.Default; + internal static TService? GetService( this IDependencyResolver resolver ) => + (TService) resolver.GetService( typeof( TService ) ); + + internal static TService GetRequiredService( this IDependencyResolver resolver ) + { + var service = resolver.GetService(); + var message = string.Format( CultureInfo.CurrentCulture, SR.NoServiceRegistered, typeof( TService ) ); + return service ?? throw new InvalidOperationException( message ); + } + + internal static IApiVersionParser GetApiVersionParser( this HttpConfiguration configuration ) => + configuration.DependencyResolver.GetService() ?? + configuration.ApiVersioningServices().GetRequiredService(); + + internal static IReportApiVersions GetApiVersionReporter( this HttpConfiguration configuration ) => + configuration.DependencyResolver.GetService() ?? + configuration.ApiVersioningServices().GetRequiredService(); + + internal static IControllerNameConvention GetControllerNameConvention( this HttpConfiguration configuration ) => + configuration.DependencyResolver.GetService() ?? + configuration.ApiVersioningServices().GetRequiredService(); + + internal static IProblemDetailsFactory GetProblemDetailsFactory( this HttpConfiguration configuration ) => + configuration.DependencyResolver.GetService() ?? + configuration.ApiVersioningServices().GetRequiredService(); + + internal static ISunsetPolicyManager GetSunsetPolicyManager( this HttpConfiguration configuration ) => + configuration.DependencyResolver.GetService() ?? + configuration.ApiVersioningServices().GetRequiredService(); } \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs index d42bedfa..80a76858 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs @@ -221,7 +221,7 @@ private static void ApplyImplicitConventions( HttpControllerDescriptor controlle } var actions = mapping.SelectMany( g => g ); - var namingConvention = controller.Configuration.DependencyResolver.GetControllerNameConvention(); + var namingConvention = controller.Configuration.GetControllerNameConvention(); var name = namingConvention.GroupName( controller.ControllerName ); var metadata = new ApiVersionMetadata( implicitVersionModel, implicitVersionModel, name ); @@ -344,7 +344,7 @@ private static void ApplyCollatedModelsToActions( HttpConfiguration configuration, List> visitedActions ) { - var namingConvention = configuration.DependencyResolver.GetControllerNameConvention(); + var namingConvention = configuration.GetControllerNameConvention(); for ( var i = 0; i < visitedActions.Count; i++ ) { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpControllerTypeCache.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpControllerTypeCache.cs index 473f9811..5c5d3abb 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpControllerTypeCache.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpControllerTypeCache.cs @@ -33,7 +33,7 @@ private Dictionary> InitializeCache() var services = configuration.Services; var assembliesResolver = services.GetAssembliesResolver(); var typeResolver = services.GetHttpControllerTypeResolver(); - var convention = configuration.DependencyResolver.GetControllerNameConvention(); + var convention = configuration.GetControllerNameConvention(); var comparer = StringComparer.OrdinalIgnoreCase; return typeResolver.GetControllerTypes( assembliesResolver ) diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpResponseExceptionFactory.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpResponseExceptionFactory.cs index 4afb0774..22fac02a 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpResponseExceptionFactory.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/HttpResponseExceptionFactory.cs @@ -36,7 +36,7 @@ internal HttpResponseExceptionFactory( HttpRequestMessage request, ApiVersionMod private ApiVersioningOptions Options => configuration.GetApiVersioningOptions(); - private IProblemDetailsFactory ProblemDetails => configuration.DependencyResolver.GetProblemDetailsFactory(); + private IProblemDetailsFactory ProblemDetails => configuration.GetProblemDetailsFactory(); private ITraceWriter TraceWriter => configuration.Services.GetTraceWriter() ?? NullTraceWriter.Instance; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DoNotReportApiVersions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DoNotReportApiVersions.cs index 722073e5..db2b979d 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DoNotReportApiVersions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/DoNotReportApiVersions.cs @@ -6,12 +6,6 @@ namespace Asp.Versioning; internal sealed class DoNotReportApiVersions : IReportApiVersions { - private static DoNotReportApiVersions? instance; - - private DoNotReportApiVersions() { } - - internal static IReportApiVersions Instance => instance ??= new(); - public ApiVersionMapping Mapping => Explicit | Implicit; public void Report( HttpResponseMessage response, ApiVersionModel apiVersionModel ) { } diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ErrorObjectFactory.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ErrorObjectFactory.cs index dd8b3689..79bbe3cc 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ErrorObjectFactory.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ErrorObjectFactory.cs @@ -43,7 +43,7 @@ public ProblemDetails CreateProblemDetails( return ProblemDetailsFactory.AddUnsupportedExtensions( request, status, problem, ApplyMessage ); } - return ProblemDetailsFactory.Default.CreateProblemDetails( + return ProblemDetailsFactory.NewProblemDetails( request, statusCode, title, diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetailsFactory.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetailsFactory.cs index 30d28c9c..baaad563 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetailsFactory.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ProblemDetailsFactory.cs @@ -10,15 +10,16 @@ namespace Asp.Versioning; internal sealed class ProblemDetailsFactory : IProblemDetailsFactory { - private static IProblemDetailsFactory? @default; - - public static IProblemDetailsFactory Default - { - get => @default ??= new ProblemDetailsFactory(); - set => @default = value; - } - public ProblemDetails CreateProblemDetails( + HttpRequestMessage request, + int? statusCode = null, + string? title = null, + string? type = null, + string? detail = null, + string? instance = null ) => + NewProblemDetails( request, statusCode, title, type, detail, instance ); + + internal static ProblemDetails NewProblemDetails( HttpRequestMessage request, int? statusCode = null, string? title = null, diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReportApiVersionsAttribute.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReportApiVersionsAttribute.cs index 8208fd7e..2e7cfd8a 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReportApiVersionsAttribute.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReportApiVersionsAttribute.cs @@ -32,14 +32,7 @@ public override void OnActionExecuted( HttpActionExecutedContext actionExecutedC var context = actionExecutedContext.ActionContext; var action = context.ActionDescriptor; - var reporter = reportApiVersions; - - if ( reporter is null ) - { - var dependencyResolver = context.ControllerContext.Configuration.DependencyResolver; - reporter = dependencyResolver.GetApiVersionReporter(); - } - + var reporter = reportApiVersions ?? context.ControllerContext.Configuration.GetApiVersionReporter(); var model = action.GetApiVersionMetadata().Map( reporter.Mapping ); response.RequestMessage ??= actionExecutedContext.Request; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs index 65a77769..9d78028c 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs @@ -41,7 +41,7 @@ public bool Match( HttpRequestMessage request, IHttpRoute route, string paramete return !string.IsNullOrEmpty( value ); } - var parser = request.GetConfiguration().DependencyResolver.GetApiVersionParser(); + var parser = request.GetConfiguration().GetApiVersionParser(); var properties = request.ApiVersionProperties(); properties.RouteParameter = parameterName; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs index 6bf7e427..9dcdbed1 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs @@ -177,6 +177,15 @@ internal static string NoControllerSelected { } } + /// + /// Looks up a localized string similar to No service for type '{0}' has been registered.. + /// + internal static string NoServiceRegistered { + get { + return ResourceManager.GetString("NoServiceRegistered", resourceCulture); + } + } + /// /// Looks up a localized string similar to No HTTP resource was found that matches the request URI '{0}'.. /// diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx index a5b89e38..29ed1630 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx @@ -161,6 +161,9 @@ A controller was not selected for request URI '{0}' and API version '{1}'. + + No service for type '{0}' has been registered. + No HTTP resource was found that matches the request URI '{0}'. diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SunsetPolicyManager.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SunsetPolicyManager.cs index b15d2160..78af0cdf 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SunsetPolicyManager.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SunsetPolicyManager.cs @@ -8,17 +8,10 @@ namespace Asp.Versioning; public partial class SunsetPolicyManager { private readonly ApiVersioningOptions options; - private static ISunsetPolicyManager? @default; /// /// Initializes a new instance of the class. /// /// The associated API versioning options. public SunsetPolicyManager( ApiVersioningOptions options ) => this.options = options; - - internal static ISunsetPolicyManager Default - { - get => @default ?? new SunsetPolicyManager( new ApiVersioningOptions() ); - set => @default = value; - } } \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs index 8a225a96..94062c5b 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs @@ -4,6 +4,7 @@ namespace System.Web.Http; using Asp.Versioning; using Asp.Versioning.Controllers; +using Asp.Versioning.Dependencies; using Asp.Versioning.Dispatcher; using Asp.Versioning.Formatting; using System.Globalization; @@ -16,7 +17,7 @@ namespace System.Web.Http; /// public static class HttpConfigurationExtensions { - private const string ApiVersioningOptionsKey = "MS_ApiVersioningOptions"; + private const string ApiVersioningServicesKey = "MS_ApiVersioningServices"; /// /// Gets the current API versioning options. @@ -30,7 +31,7 @@ public static ApiVersioningOptions GetApiVersioningOptions( this HttpConfigurati throw new ArgumentNullException( nameof( configuration ) ); } - return (ApiVersioningOptions) configuration.Properties.GetOrAdd( ApiVersioningOptionsKey, key => new ApiVersioningOptions() ); + return configuration.ApiVersioningServices().ApiVersioningOptions; } /// @@ -113,11 +114,9 @@ private static void AddApiVersioning( this HttpConfiguration configuration, ApiV } } - configuration.Properties.AddOrUpdate( ApiVersioningOptionsKey, options, ( key, oldValue ) => options ); + configuration.ApiVersioningServices().ApiVersioningOptions = options; configuration.ParameterBindingRules.Add( typeof( ApiVersion ), ApiVersionParameterBinding.Create ); configuration.Formatters.Insert( 0, new ProblemDetailsMediaTypeFormatter( configuration.Formatters.JsonFormatter ?? new() ) ); - - SunsetPolicyManager.Default = new SunsetPolicyManager( options ); } // ApiVersion.Neutral does not have the same meaning as IApiVersionNeutral. setting @@ -146,7 +145,9 @@ private static void ValidateApiVersioningOptions( ApiVersioningOptions options ) private static void EnableErrorObjectResponses( HttpConfiguration configuration ) { - ProblemDetailsFactory.Default = new ErrorObjectFactory(); + configuration.ApiVersioningServices().Replace( + typeof( IProblemDetailsFactory ), + static ( sc, t ) => new ErrorObjectFactory() ); var formatters = configuration.Formatters; var problemDetails = ProblemDetailsMediaTypeFormatter.DefaultMediaType; @@ -166,15 +167,6 @@ private static void EnableErrorObjectResponses( HttpConfiguration configuration } } - internal static IReportApiVersions GetApiVersionReporter( this HttpConfiguration configuration ) - { - var options = configuration.GetApiVersioningOptions(); - - if ( options.ReportApiVersions ) - { - return configuration.DependencyResolver.GetApiVersionReporter(); - } - - return DoNotReportApiVersions.Instance; - } + internal static DefaultContainer ApiVersioningServices( this HttpConfiguration configuration ) => + (DefaultContainer) configuration.Properties.GetOrAdd( ApiVersioningServicesKey, key => new DefaultContainer() ); } \ No newline at end of file diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/ReportApiVersionsAttributeTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/ReportApiVersionsAttributeTest.cs index 57090ed2..45ec83cc 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/ReportApiVersionsAttributeTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/ReportApiVersionsAttributeTest.cs @@ -29,11 +29,12 @@ public void on_action_executed_should_add_version_headers() var method = controller.GetType().GetMethod( nameof( TestController.Get ) ); var controllerDescriptor = new Mock( configuration, "Test", controller.GetType() ) { CallBase = true }; var routeData = new HttpRouteData( new HttpRoute( "api/tests" ) ); - var controllerContext = new HttpControllerContext( new HttpConfiguration(), routeData, new HttpRequestMessage() ) { Controller = controller }; + var controllerContext = new HttpControllerContext( configuration, routeData, new HttpRequestMessage() ) { Controller = controller }; var actionDescriptor = new ReflectedHttpActionDescriptor( controllerDescriptor.Object, method ); var actionContext = new HttpActionContext( controllerContext, actionDescriptor ) { Response = new HttpResponseMessage() }; var context = new HttpActionExecutedContext( actionContext, null ); + configuration.AddApiVersioning( options => options.ReportApiVersions = true ); controllerContext.Request.SetConfiguration( new() ); controllerContext.Request.Properties["MS_HttpActionDescriptor"] = actionDescriptor; controllerDescriptor.Setup( cd => cd.GetCustomAttributes( It.IsAny() ) ).Returns( attributes ); @@ -69,11 +70,12 @@ public void on_action_executing_should_not_add_headers_for_versionX2Dneutral_con var method = controller.GetType().GetMethod( nameof( TestVersionNeutralController.Get ) ); var controllerDescriptor = new Mock( configuration, "Test", controller.GetType() ) { CallBase = true }; var routeData = new HttpRouteData( new HttpRoute( "api/tests" ) ); - var controllerContext = new HttpControllerContext( new HttpConfiguration(), routeData, new HttpRequestMessage() ) { Controller = new TestVersionNeutralController() }; + var controllerContext = new HttpControllerContext( configuration, routeData, new HttpRequestMessage() ) { Controller = new TestVersionNeutralController() }; var actionDescriptor = new ReflectedHttpActionDescriptor( controllerDescriptor.Object, method ); var actionContext = new HttpActionContext( controllerContext, actionDescriptor ) { Response = new HttpResponseMessage() }; var context = new HttpActionExecutedContext( actionContext, null ); + configuration.AddApiVersioning(); controllerDescriptor.Setup( cd => cd.GetCustomAttributes( It.IsAny() ) ).Returns( attributes ); controllerDescriptor.Object.Properties[typeof( ApiVersionModel )] = ApiVersionModel.Neutral; actionDescriptor.Properties[typeof( ApiVersionMetadata )] = ApiVersionMetadata.Neutral; From f97f94cdb1f516743b2ae3008367f24312d36346 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 15 Sep 2023 08:30:31 -0700 Subject: [PATCH 050/131] Update VSCode settings --- .vscode/settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4bce8bd4..86a81124 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -26,10 +26,10 @@ "dotnet-test-explorer.runInParallel": true, "dotnet-test-explorer.testProjectPath": "**/*Tests.csproj", "editor.formatOnType": true, - "omnisharp.enableImportCompletion": true, "omnisharp.enableRoslynAnalyzers": true, "omnisharp.organizeImportsOnFormat": true, "omnisharp.useModernNet": true, "omnisharp.enableMsBuildLoadProjectsOnDemand": true, - "omnisharp.enableEditorConfigSupport": true + "omnisharp.enableEditorConfigSupport": true, + "dotnet.completion.showCompletionItemsFromUnimportedNamespaces": true } \ No newline at end of file From ee996f24ae386f07c6bbf936d25b70af1ec684c6 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Fri, 15 Sep 2023 08:31:24 -0700 Subject: [PATCH 051/131] Add --no-build option to dotnet pack --- build/steps-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/steps-release.yml b/build/steps-release.yml index 50a7e9bf..ca070468 100644 --- a/build/steps-release.yml +++ b/build/steps-release.yml @@ -16,9 +16,9 @@ steps: command: pack projects: ${{ parameters.solution }} ${{ if eq(parameters.versionSuffix, '') }}: - arguments: --configuration ${{ parameters.configuration }} + arguments: --no-build --configuration ${{ parameters.configuration }} ${{ else }}: - arguments: --configuration ${{ parameters.configuration }} --version-suffix ${{ parameters.versionSuffix }} + arguments: --no-build --configuration ${{ parameters.configuration }} --version-suffix ${{ parameters.versionSuffix }} outputDir: $(Build.ArtifactStagingDirectory)/packages noBuild: true From 4c969b1ec102e05e0b169c50e4b6f76f7c2a0bc5 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 9 Oct 2023 07:08:47 -0700 Subject: [PATCH 052/131] Explicit API description should supersede implicit one. Relates to #1025 --- .../VersionedApiDescriptionProvider.cs | 43 ++++++++- .../VersionedApiDescriptionProviderTest.cs | 91 +++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs index c84f9d06..45a8c8fe 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs @@ -172,7 +172,7 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context ) groupResult.SetApiVersion( version ); PopulateApiVersionParameters( groupResult, version ); - groupResults.Add( groupResult ); + AddOrUpdateResult( groupResults, groupResult, metadata, version ); } } @@ -245,6 +245,47 @@ private static void TryUpdateControllerRouteValueForMinimalApi( ApiDescription d } } + private static void AddOrUpdateResult( + List results, + ApiDescription result, + ApiVersionMetadata metadata, + ApiVersion version ) + { + var comparer = StringComparer.OrdinalIgnoreCase; + + for ( var i = results.Count - 1; i >= 0; i-- ) + { + var other = results[i]; + + if ( comparer.Equals( result.GroupName, other.GroupName ) && + comparer.Equals( result.RelativePath, other.RelativePath ) && + comparer.Equals( result.HttpMethod, other.HttpMethod ) ) + { + var mapping = other.ActionDescriptor.GetApiVersionMetadata().MappingTo( version ); + + switch ( metadata.MappingTo( version ) ) + { + case Explicit: + if ( mapping == Implicit ) + { + results.RemoveAt( i ); + } + + break; + case Implicit: + if ( mapping == Explicit ) + { + return; + } + + break; + } + } + } + + results.Add( result ); + } + private IEnumerable FlattenApiVersions( IList descriptions ) { var versions = default( SortedSet ); diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/VersionedApiDescriptionProviderTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/VersionedApiDescriptionProviderTest.cs index 45a50e05..8af044ae 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/VersionedApiDescriptionProviderTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/VersionedApiDescriptionProviderTest.cs @@ -149,6 +149,97 @@ public void versioned_api_explorer_should_use_custom_group_name() context.Results.Single().GroupName.Should().Be( "Test-1.0" ); } + [Fact] + public void versioned_api_explorer_should_prefer_explicit_over_implicit_action_match() + { + // arrange + var @implicit = new ActionDescriptor() + { + DisplayName = "Implicit GET ~/test?api-version=[1.0,2.0]", + EndpointMetadata = new[] + { + new ApiVersionMetadata( + new ApiVersionModel( + new ApiVersion[] { new( 1.0 ), new( 2.0 ) }, + new ApiVersion[] { new( 1.0 ), new( 2.0 ) }, + Array.Empty(), + Array.Empty(), + Array.Empty() ), + new ApiVersionModel( + Array.Empty(), + new ApiVersion[] { new( 1.0 ), new( 2.0 ) }, + Array.Empty(), + Array.Empty(), + Array.Empty() ) ), + }, + }; + var @explicit = new ActionDescriptor() + { + DisplayName = "Explicit GET ~/test?api-version=2.0", + EndpointMetadata = new[] + { + new ApiVersionMetadata( + new ApiVersionModel( + new ApiVersion[] { new( 1.0 ), new( 2.0 ) }, + new ApiVersion[] { new( 1.0 ), new( 2.0 ) }, + Array.Empty(), + Array.Empty(), + Array.Empty() ), + new ApiVersionModel( + new ApiVersion[] { new( 2.0 ) }, + new ApiVersion[] { new( 1.0 ), new( 2.0 ) }, + Array.Empty(), + Array.Empty(), + Array.Empty() ) ), + }, + }; + var actionProvider = new TestActionDescriptorCollectionProvider( @implicit, @explicit ); + var context = new ApiDescriptionProviderContext( actionProvider.ActionDescriptors.Items ); + + context.Results.Add( + new() + { + HttpMethod = "GET", + RelativePath = "test", + ActionDescriptor = context.Actions[0], + } ); + + context.Results.Add( + new() + { + HttpMethod = "GET", + RelativePath = "test", + ActionDescriptor = context.Actions[1], + } ); + + var apiExplorer = new VersionedApiDescriptionProvider( + Mock.Of(), + NewModelMetadataProvider(), + Options.Create( new ApiExplorerOptions() { GroupNameFormat = "'v'VVV" } ) ); + + // act + apiExplorer.OnProvidersExecuted( context ); + + // assert + context.Results.Should().BeEquivalentTo( + new[] + { + new + { + GroupName = "v1", + ActionDescriptor = @implicit, + Properties = new Dictionary() { [typeof( ApiVersion )] = new ApiVersion( 1.0 ) }, + }, + new + { + GroupName = "v2", + ActionDescriptor = @explicit, + Properties = new Dictionary() { [typeof( ApiVersion )] = new ApiVersion( 2.0 ) }, + }, + }, + options => options.ExcludingMissingMembers() ); + } + private static IModelMetadataProvider NewModelMetadataProvider() { var provider = new Mock(); From e36fb1b5cda8e6e6807758fdade11ecfb487ec14 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 9 Oct 2023 07:16:13 -0700 Subject: [PATCH 053/131] Add version selector to API Explorer option. Relates to #1025 --- .../ApiExplorer/ApiExplorerOptions.cs | 10 ++++++++ .../ApiExplorerOptions.cs | 23 ++++++++++++++++++- .../Common.ApiExplorer/ApiExplorerOptions.cs | 8 +++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/ApiExplorerOptions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/ApiExplorerOptions.cs index 09df17e5..63aaa0ec 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/ApiExplorerOptions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ApiExplorer/ApiExplorerOptions.cs @@ -44,4 +44,14 @@ public partial class ApiExplorerOptions /// /// The name associated with the API version route constraint. public string RouteConstraintName => options.Value.RouteConstraintName; + + /// + /// Gets or sets the API version selector. + /// + /// An API version selector object. + public IApiVersionSelector ApiVersionSelector + { + get => apiVersionSelector ?? options.Value.ApiVersionSelector; + set => apiVersionSelector = value; + } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiExplorerOptions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiExplorerOptions.cs index 0e0aef50..b366513a 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiExplorerOptions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiExplorerOptions.cs @@ -3,6 +3,7 @@ namespace Asp.Versioning.ApiExplorer; using Asp.Versioning.Routing; +using Microsoft.AspNetCore.Http; /// /// Provides additional implementation specific to ASP.NET Core. @@ -37,7 +38,7 @@ public ApiVersion DefaultApiVersion /// The API version parameter source used to describe API version parameters. public IApiVersionParameterSource ApiVersionParameterSource { - get => parameterSource ??= ApiVersionReader.Combine( new QueryStringApiVersionReader(), new UrlSegmentApiVersionReader() ); + get => parameterSource ??= ApiVersionReader.Default; set => parameterSource = value; } @@ -47,6 +48,17 @@ public IApiVersionParameterSource ApiVersionParameterSource /// The name associated with the API version route constraint. public string RouteConstraintName { get; set; } = string.Empty; + /// + /// Gets or sets the API version selector. + /// + /// An API version selector object. + [CLSCompliant( false )] + public IApiVersionSelector ApiVersionSelector + { + get => apiVersionSelector ??= new DefaultApiVersionSelector( this ); + set => apiVersionSelector = value; + } + /// /// Gets or sets the function used to format the combination of a group name and API version. /// @@ -55,4 +67,13 @@ public IApiVersionParameterSource ApiVersionParameterSource /// The specified callback will only be invoked if a group name has been configured. The API /// version will be provided formatted according to the group name format. public FormatGroupNameCallback? FormatGroupName { get; set; } + + private sealed class DefaultApiVersionSelector : IApiVersionSelector + { + private readonly ApiExplorerOptions options; + + public DefaultApiVersionSelector( ApiExplorerOptions options ) => this.options = options; + + public ApiVersion SelectVersion( HttpRequest request, ApiVersionModel model ) => options.DefaultApiVersion; + } } \ No newline at end of file diff --git a/src/Common/src/Common.ApiExplorer/ApiExplorerOptions.cs b/src/Common/src/Common.ApiExplorer/ApiExplorerOptions.cs index 6cf69948..2cd629ce 100644 --- a/src/Common/src/Common.ApiExplorer/ApiExplorerOptions.cs +++ b/src/Common/src/Common.ApiExplorer/ApiExplorerOptions.cs @@ -2,11 +2,19 @@ namespace Asp.Versioning.ApiExplorer; +#if NETFRAMEWORK +using HttpRequest = System.Net.Http.HttpRequestMessage; +#else +using Microsoft.AspNetCore.Http; +#endif + /// /// Represents the possible API versioning options for the API explorer. /// public partial class ApiExplorerOptions { + private IApiVersionSelector? apiVersionSelector; + /// /// Gets or sets the format used to create group names from API versions. /// From b39c6d768b6cbc337afb78a29adbcb982e9fa893 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 9 Oct 2023 07:16:44 -0700 Subject: [PATCH 054/131] Fix copying source to target options --- .../ODataApiExplorerOptionsFactory.cs | 8 +++++-- .../ApiExplorerOptionsFactory{T}.cs | 23 ++++++++++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs index 261b6c54..5e494688 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiExplorerOptionsFactory.cs @@ -69,8 +69,12 @@ public ODataApiExplorerOptionsFactory( } /// - protected override ODataApiExplorerOptions CreateInstance( string name ) => - new( new( CollateApiVersions( providers, Options ), modelConfigurations ) ); + protected override ODataApiExplorerOptions CreateInstance( string name ) + { + var options = new ODataApiExplorerOptions( new( CollateApiVersions( providers, Options ), modelConfigurations ) ); + CopyOptions( Options, options ); + return options; + } private static ODataApiVersionCollectionProvider CollateApiVersions( IApiVersionMetadataCollationProvider[] providers, diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiExplorerOptionsFactory{T}.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiExplorerOptionsFactory{T}.cs index b2d866c9..1a79e0a3 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiExplorerOptionsFactory{T}.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiExplorerOptionsFactory{T}.cs @@ -55,14 +55,25 @@ public ApiExplorerOptionsFactory( /// protected override T CreateInstance( string name ) { - var apiVersioningOptions = Options; var options = base.CreateInstance( name ); + CopyOptions( Options, options ); + return options; + } - options.AssumeDefaultVersionWhenUnspecified = apiVersioningOptions.AssumeDefaultVersionWhenUnspecified; - options.ApiVersionParameterSource = apiVersioningOptions.ApiVersionReader; - options.DefaultApiVersion = apiVersioningOptions.DefaultApiVersion; - options.RouteConstraintName = apiVersioningOptions.RouteConstraintName; + /// + /// Copies the following source options to the target options. + /// + /// The source options. + /// The target options. + protected static void CopyOptions( ApiVersioningOptions sourceOptions, T targetOptions ) + { + ArgumentNullException.ThrowIfNull( targetOptions, nameof( targetOptions ) ); + ArgumentNullException.ThrowIfNull( sourceOptions, nameof( sourceOptions ) ); - return options; + targetOptions.AssumeDefaultVersionWhenUnspecified = sourceOptions.AssumeDefaultVersionWhenUnspecified; + targetOptions.ApiVersionParameterSource = sourceOptions.ApiVersionReader; + targetOptions.DefaultApiVersion = sourceOptions.DefaultApiVersion; + targetOptions.RouteConstraintName = sourceOptions.RouteConstraintName; + targetOptions.ApiVersionSelector = sourceOptions.ApiVersionSelector; } } \ No newline at end of file From a76647a4a3b32df79e7e2bb952e7f96ed099c5cd Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 9 Oct 2023 07:36:54 -0700 Subject: [PATCH 055/131] Use selector when determining if 1st version parameter is optional. Fixes #1025 --- .../ApiVersionParameterDescriptionContext.cs | 28 +++++++++++++++++-- ...iVersionParameterDescriptionContextTest.cs | 16 ++++++++--- .../ApiVersionParameterDescriptionContext.cs | 20 ++++++++++++- ...iVersionParameterDescriptionContextTest.cs | 7 +++-- .../TestActionDescriptorCollectionProvider.cs | 6 ++-- 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiVersionParameterDescriptionContext.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiVersionParameterDescriptionContext.cs index c1e5fdf6..8e685601 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiVersionParameterDescriptionContext.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiVersionParameterDescriptionContext.cs @@ -30,9 +30,9 @@ public class ApiVersionParameterDescriptionContext : IApiVersionParameterDescrip public ApiVersionParameterDescriptionContext( ApiDescription apiDescription, ApiVersion apiVersion, ApiExplorerOptions options ) { Options = options ?? throw new ArgumentNullException( nameof( options ) ); - ApiDescription = apiDescription; - ApiVersion = apiVersion; - optional = options.AssumeDefaultVersionWhenUnspecified && apiVersion == options.DefaultApiVersion; + ApiDescription = apiDescription ?? throw new ArgumentNullException( nameof( apiDescription ) ); + ApiVersion = apiVersion ?? throw new ArgumentNullException( nameof( apiVersion ) ); + optional = FirstParameterIsOptional( apiDescription, apiVersion, options ); } /// @@ -242,4 +242,26 @@ private static void CloneFormattersAndAddMediaTypeParameter( NameValueHeaderValu formatters.Add( formatter ); } } + + private static bool FirstParameterIsOptional( + ApiDescription apiDescription, + ApiVersion apiVersion, + ApiExplorerOptions options ) + { + if ( !options.AssumeDefaultVersionWhenUnspecified ) + { + return false; + } + + var mapping = ApiVersionMapping.Explicit | ApiVersionMapping.Implicit; + var model = apiDescription.ActionDescriptor.GetApiVersionMetadata().Map( mapping ); + ApiVersion defaultApiVersion; + + using ( var request = new HttpRequestMessage() ) + { + defaultApiVersion = options.ApiVersionSelector.SelectVersion( request, model ); + } + + return apiVersion == defaultApiVersion; + } } \ No newline at end of file diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiVersionParameterDescriptionContextTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiVersionParameterDescriptionContextTest.cs index dca4f3b8..da8e4bf6 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiVersionParameterDescriptionContextTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiVersionParameterDescriptionContextTest.cs @@ -234,11 +234,19 @@ public void add_parameter_should_add_optional_parameter_when_allowed() var configuration = new HttpConfiguration(); var action = NewActionDescriptor(); var description = new ApiDescription() { ActionDescriptor = action }; - var version = new ApiVersion( 1, 0 ); - var options = new ApiExplorerOptions( configuration ); + var version = new ApiVersion( 2.0 ); + var options = new ApiExplorerOptions( configuration ) + { + ApiVersionSelector = new ConstantApiVersionSelector( version ), + }; action.Configuration = configuration; - configuration.AddApiVersioning( o => o.AssumeDefaultVersionWhenUnspecified = true ); + configuration.AddApiVersioning( + options => + { + options.DefaultApiVersion = ApiVersion.Default; + options.AssumeDefaultVersionWhenUnspecified = true; + } ); var context = new ApiVersionParameterDescriptionContext( description, version, options ); @@ -255,7 +263,7 @@ public void add_parameter_should_add_optional_parameter_when_allowed() ParameterDescriptor = new { ParameterName = "api-version", - DefaultValue = "1.0", + DefaultValue = "2.0", IsOptional = true, Configuration = configuration, ActionDescriptor = action, diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs index c95bc1fc..40c48639 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs @@ -41,7 +41,7 @@ public ApiVersionParameterDescriptionContext( ApiDescription = apiDescription ?? throw new ArgumentNullException( nameof( apiDescription ) ); ApiVersion = apiVersion ?? throw new ArgumentNullException( nameof( apiVersion ) ); ModelMetadata = modelMetadata ?? throw new ArgumentNullException( nameof( modelMetadata ) ); - optional = options.AssumeDefaultVersionWhenUnspecified && apiVersion == options.DefaultApiVersion; + optional = FirstParameterIsOptional( apiDescription, apiVersion, options ); } // intentionally an internal property so the public contract doesn't change. this will be removed @@ -440,4 +440,22 @@ private void RemoveAllParametersExcept( ApiParameterDescription parameter ) } } } + + private static bool FirstParameterIsOptional( + ApiDescription apiDescription, + ApiVersion apiVersion, + ApiExplorerOptions options ) + { + if ( !options.AssumeDefaultVersionWhenUnspecified ) + { + return false; + } + + var mapping = ApiVersionMapping.Explicit | ApiVersionMapping.Implicit; + var model = apiDescription.ActionDescriptor.GetApiVersionMetadata().Map( mapping ); + var context = new Microsoft.AspNetCore.Http.DefaultHttpContext(); + var defaultApiVersion = options.ApiVersionSelector.SelectVersion( context.Request, model ); + + return apiVersion == defaultApiVersion; + } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/ApiVersionParameterDescriptionContextTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/ApiVersionParameterDescriptionContextTest.cs index 1aec0e18..ec26b099 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/ApiVersionParameterDescriptionContextTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/ApiVersionParameterDescriptionContextTest.cs @@ -249,13 +249,14 @@ public void add_parameter_should_add_descriptor_for_media_type_parameter() public void add_parameter_should_add_optional_parameter_when_allowed() { // arrange - var version = new ApiVersion( 1, 0 ); + var version = new ApiVersion( 2.0 ); var description = NewApiDescription( version ); var modelMetadata = new Mock( ModelMetadataIdentity.ForType( typeof( string ) ) ).Object; var options = new ApiExplorerOptions() { - DefaultApiVersion = version, + DefaultApiVersion = ApiVersion.Default, ApiVersionParameterSource = new QueryStringApiVersionReader(), + ApiVersionSelector = new ConstantApiVersionSelector( version ), AssumeDefaultVersionWhenUnspecified = true, }; var context = new ApiVersionParameterDescriptionContext( description, version, modelMetadata, options ); @@ -270,7 +271,7 @@ public void add_parameter_should_add_optional_parameter_when_allowed() Name = "api-version", ModelMetadata = modelMetadata, Source = BindingSource.Query, - DefaultValue = (object) "1.0", + DefaultValue = (object) "2.0", IsRequired = false, Type = typeof( string ), }, diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestActionDescriptorCollectionProvider.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestActionDescriptorCollectionProvider.cs index 0ccce57b..be8ff384 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestActionDescriptorCollectionProvider.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestActionDescriptorCollectionProvider.cs @@ -7,9 +7,9 @@ namespace Asp.Versioning.ApiExplorer; internal sealed class TestActionDescriptorCollectionProvider : IActionDescriptorCollectionProvider { - private readonly Lazy collection = new( CreateActionDescriptors ); + private readonly Lazy collection; - public TestActionDescriptorCollectionProvider() { } + public TestActionDescriptorCollectionProvider() => collection = new( CreateActionDescriptors ); public TestActionDescriptorCollectionProvider( ActionDescriptor action, params ActionDescriptor[] otherActions ) { @@ -21,7 +21,7 @@ public TestActionDescriptorCollectionProvider( ActionDescriptor action, params A } else { - actions = new ActionDescriptor[otherActions.Length]; + actions = new ActionDescriptor[otherActions.Length + 1]; actions[0] = action; Array.Copy( otherActions, 0, actions, 1, otherActions.Length ); } From 471eb044500495beb8a4f009d9a1c2bf4d5fde63 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 9 Oct 2023 08:47:12 -0700 Subject: [PATCH 056/131] Bump version numbers and update release notes --- .../Asp.Versioning.WebApi.OData.ApiExplorer.csproj | 4 ++-- .../Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt | 2 +- .../Asp.Versioning.WebApi.OData.csproj | 4 ++-- .../OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt | 2 +- .../Asp.Versioning.WebApi.ApiExplorer.csproj | 4 ++-- .../src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt | 2 +- src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt | 2 +- .../Asp.Versioning.OData.ApiExplorer.csproj | 4 ++-- .../src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt | 2 +- .../src/Asp.Versioning.OData/Asp.Versioning.OData.csproj | 4 ++-- .../OData/src/Asp.Versioning.OData/ReleaseNotes.txt | 2 +- .../WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt | 2 +- .../Asp.Versioning.Mvc.ApiExplorer.csproj | 4 ++-- .../src/Asp.Versioning.Mvc.ApiExplorer/ReleaseNotes.txt | 2 +- .../WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj | 4 ++-- src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt | 2 +- 16 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj index f5978313..f500db36 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Asp.Versioning.WebApi.OData.ApiExplorer.csproj @@ -1,8 +1,8 @@  - 7.0.4 - 7.0.0.0 + 7.1.0 + 7.1.0.0 net45;net472 Asp.Versioning ASP.NET Web API Versioning API Explorer for OData v4.0 diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt index 1672692d..5f282702 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ -Publish NuGet symbol package \ No newline at end of file + \ No newline at end of file diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj index 370667d7..7229ae69 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/Asp.Versioning.WebApi.OData.csproj @@ -1,8 +1,8 @@  - 7.0.2 - 7.0.0.0 + 7.1.0 + 7.1.0.0 net45;net472 Asp.Versioning API Versioning for ASP.NET Web API with OData v4.0 diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt index 1672692d..5f282702 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/ReleaseNotes.txt @@ -1 +1 @@ -Publish NuGet symbol package \ No newline at end of file + \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj index df2190eb..9622bbfd 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Asp.Versioning.WebApi.ApiExplorer.csproj @@ -1,8 +1,8 @@  - 7.0.1 - 7.0.0.0 + 7.1.0 + 7.1.0.0 net45;net472 ASP.NET Web API Versioning API Explorer The API Explorer extensions for ASP.NET Web API Versioning. diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt index 1672692d..5f282702 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ -Publish NuGet symbol package \ No newline at end of file + \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt index 5cdc847a..5f282702 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReleaseNotes.txt @@ -1 +1 @@ -Add backward compatibility for error objects \ No newline at end of file + \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj index 62d2a48e..14511f77 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj @@ -1,8 +1,8 @@  - 7.0.4 - 7.0.0.0 + 7.1.0 + 7.1.0.0 net7.0 Asp.Versioning ASP.NET Core API Versioning API Explorer for OData v4.0 diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt index 1672692d..5f282702 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ -Publish NuGet symbol package \ No newline at end of file + \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj index 7e1ac67c..7caf7217 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj @@ -1,8 +1,8 @@  - 7.0.2 - 7.0.0.0 + 7.1.0 + 7.1.0.0 net7.0 Asp.Versioning ASP.NET Core API Versioning with OData v4.0 diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt b/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt index 1672692d..5f282702 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt @@ -1 +1 @@ -Publish NuGet symbol package \ No newline at end of file + \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt index 5cdc847a..5f282702 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ReleaseNotes.txt @@ -1 +1 @@ -Add backward compatibility for error objects \ No newline at end of file + \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj index e696251b..4b9587a2 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj @@ -1,8 +1,8 @@  - 7.0.1 - 7.0.0.0 + 7.1.0 + 7.1.0.0 net7.0 Asp.Versioning.ApiExplorer ASP.NET Core API Versioning API Explorer diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ReleaseNotes.txt b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ReleaseNotes.txt index 1672692d..5f282702 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ -Publish NuGet symbol package \ No newline at end of file + \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj index 44a4dde0..a11e441f 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj @@ -1,8 +1,8 @@  - 7.0.1 - 7.0.0.0 + 7.1.0 + 7.1.0.0 net7.0 Asp.Versioning ASP.NET Core API Versioning diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt index 1672692d..5f282702 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt @@ -1 +1 @@ -Publish NuGet symbol package \ No newline at end of file + \ No newline at end of file From 8b0a70dfcc10a4d84e1661809e9b91307f04be72 Mon Sep 17 00:00:00 2001 From: Xavier Date: Tue, 21 Nov 2023 16:48:16 -0800 Subject: [PATCH 057/131] If Controller Attribute is set, use it for the controller's name --- .github/workflows/codeql-analysis.yml | 4 ++++ .../ApiVersioningApplicationModelProvider.cs | 9 ++++++++- .../src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj | 2 +- .../WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f9b40dff..ef4ad775 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -29,9 +29,13 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 + id: installdotnet with: dotnet-version: 7.0.x + - name: Create temporary global.json + run: echo '{"sdk":{"version":"${{ steps.installdotnet.outputs.dotnet-version }}"}}' > ./global.json + # build a temporary *.slnf file that only contains source projects and put it in ~/obj # so that it is not tracked by git. then run 'dotnet build' using the *.slnf, which # will preserve the dependency order and configuration of the *.sln diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersioningApplicationModelProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersioningApplicationModelProvider.cs index ed1e996b..f675197b 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersioningApplicationModelProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersioningApplicationModelProvider.cs @@ -81,7 +81,14 @@ public virtual void OnProvidersExecuting( ApplicationModelProviderContext contex { var controller = controllers[i]; - controller.ControllerName = NamingConvention.NormalizeName( controller.ControllerName ); + if ( controller.RouteValues.TryGetValue( "controller", out var name ) ) + { + controller.ControllerName = name!; + } + else + { + controller.ControllerName = NamingConvention.NormalizeName( controller.ControllerName ); + } if ( !ConventionBuilder.ApplyTo( controller ) ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj index a11e441f..f08faa61 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj @@ -1,7 +1,7 @@  - 7.1.0 + 7.1.1 7.1.0.0 net7.0 Asp.Versioning diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt index 5f282702..525fc7a3 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +If Controller Attribute is set, use it for the controller name ([#1033](https://github.com/dotnet/aspnet-api-versioning/issues/1033)) \ No newline at end of file From b797ea0d834f84459a2d75e43cae3a9e93772eab Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Sat, 2 Dec 2023 11:38:58 -0800 Subject: [PATCH 058/131] Re-add removed by tooling --- src/Directory.Build.targets | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 468f1f01..53ff4535 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -21,6 +21,10 @@ + + + + From 5350b0f479b801883519a6c08829f36a711733f7 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Sat, 2 Dec 2023 11:39:11 -0800 Subject: [PATCH 059/131] Add explicit using statements --- .../Controllers/Values2Controller.cs | 9 ++++--- .../Controllers/Values2Controller.cs | 27 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/examples/AspNet/WebApi/BasicWebApiExample/Controllers/Values2Controller.cs b/examples/AspNet/WebApi/BasicWebApiExample/Controllers/Values2Controller.cs index 7dd4690b..3b6e7461 100644 --- a/examples/AspNet/WebApi/BasicWebApiExample/Controllers/Values2Controller.cs +++ b/examples/AspNet/WebApi/BasicWebApiExample/Controllers/Values2Controller.cs @@ -1,17 +1,18 @@ namespace ApiVersioning.Examples.Controllers; using Asp.Versioning; +using System.Net.Http; using System.Web.Http; -[ApiVersion( 2.0 )] -[Route( "api/values" )] +[ApiVersion(2.0)] +[Route("api/values")] public class Values2Controller : ApiController { // GET api/values?api-version=2.0 public IHttpActionResult Get() => - Ok( new + Ok(new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString(), - } ); + }); } \ No newline at end of file diff --git a/examples/AspNet/WebApi/ConventionsWebApiExample/Controllers/Values2Controller.cs b/examples/AspNet/WebApi/ConventionsWebApiExample/Controllers/Values2Controller.cs index e8ceaf54..338f390b 100644 --- a/examples/AspNet/WebApi/ConventionsWebApiExample/Controllers/Values2Controller.cs +++ b/examples/AspNet/WebApi/ConventionsWebApiExample/Controllers/Values2Controller.cs @@ -1,45 +1,46 @@ namespace ApiVersioning.Examples.Controllers; +using System.Net.Http; using System.Web.Http; -[RoutePrefix( "api/values" )] +[RoutePrefix("api/values")] public class Values2Controller : ApiController { // GET api/values?api-version=2.0 [Route] public IHttpActionResult Get() => - Ok( new + Ok(new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString(), - } ); + }); // GET api/values/{id}?api-version=2.0 - [Route( "{id:int}" )] - public IHttpActionResult Get( int id ) => - Ok( new + [Route("{id:int}")] + public IHttpActionResult Get(int id) => + Ok(new { controller = GetType().Name, id, version = Request.GetRequestedApiVersion().ToString(), - } ); + }); // GET api/values?api-version=3.0 [Route] public IHttpActionResult GetV3() => - Ok( new + Ok(new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString(), - } ); + }); // GET api/values/{id}?api-version=3.0 - [Route( "{id:int}" )] - public IHttpActionResult GetV3( int id ) => - Ok( new + [Route("{id:int}")] + public IHttpActionResult GetV3(int id) => + Ok(new { controller = GetType().Name, id, version = Request.GetRequestedApiVersion().ToString(), - } ); + }); } \ No newline at end of file From c74f40088d77a126df638eb873791b4c8ed60b72 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Sat, 2 Dec 2023 11:39:24 -0800 Subject: [PATCH 060/131] Update SourceLink version --- build/nuget.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/nuget.props b/build/nuget.props index c783c23a..f679d6d3 100644 --- a/build/nuget.props +++ b/build/nuget.props @@ -36,7 +36,7 @@ - + From 6060b03a5bca0b2b90d87dbda70b423b05abb143 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 4 Dec 2023 20:47:19 -0800 Subject: [PATCH 061/131] Fix formatting --- .../Controllers/Values2Controller.cs | 8 +++--- .../Controllers/Values2Controller.cs | 26 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/AspNet/WebApi/BasicWebApiExample/Controllers/Values2Controller.cs b/examples/AspNet/WebApi/BasicWebApiExample/Controllers/Values2Controller.cs index 3b6e7461..ef7aa0ba 100644 --- a/examples/AspNet/WebApi/BasicWebApiExample/Controllers/Values2Controller.cs +++ b/examples/AspNet/WebApi/BasicWebApiExample/Controllers/Values2Controller.cs @@ -4,15 +4,15 @@ using System.Net.Http; using System.Web.Http; -[ApiVersion(2.0)] -[Route("api/values")] +[ApiVersion( 2.0 )] +[Route( "api/values" )] public class Values2Controller : ApiController { // GET api/values?api-version=2.0 public IHttpActionResult Get() => - Ok(new + Ok( new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString(), - }); + } ); } \ No newline at end of file diff --git a/examples/AspNet/WebApi/ConventionsWebApiExample/Controllers/Values2Controller.cs b/examples/AspNet/WebApi/ConventionsWebApiExample/Controllers/Values2Controller.cs index 338f390b..1d700c5c 100644 --- a/examples/AspNet/WebApi/ConventionsWebApiExample/Controllers/Values2Controller.cs +++ b/examples/AspNet/WebApi/ConventionsWebApiExample/Controllers/Values2Controller.cs @@ -3,44 +3,44 @@ using System.Net.Http; using System.Web.Http; -[RoutePrefix("api/values")] +[RoutePrefix( "api/values" )] public class Values2Controller : ApiController { // GET api/values?api-version=2.0 [Route] public IHttpActionResult Get() => - Ok(new + Ok( new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString(), - }); + } ); // GET api/values/{id}?api-version=2.0 - [Route("{id:int}")] - public IHttpActionResult Get(int id) => - Ok(new + [Route( "{id:int}" )] + public IHttpActionResult Get( int id ) => + Ok( new { controller = GetType().Name, id, version = Request.GetRequestedApiVersion().ToString(), - }); + } ); // GET api/values?api-version=3.0 [Route] public IHttpActionResult GetV3() => - Ok(new + Ok( new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString(), - }); + } ); // GET api/values/{id}?api-version=3.0 - [Route("{id:int}")] - public IHttpActionResult GetV3(int id) => - Ok(new + [Route( "{id:int}" )] + public IHttpActionResult GetV3( int id ) => + Ok( new { controller = GetType().Name, id, version = Request.GetRequestedApiVersion().ToString(), - }); + } ); } \ No newline at end of file From e3b8c01dc78f6d7a281cc528885f5276f05cd2c4 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 4 Dec 2023 16:09:34 -0800 Subject: [PATCH 062/131] Rename folders --- .../Asp.Versioning.Abstractions/{net7.0 => net#.0}/ApiVersion.cs | 0 .../{net7.0 => net#.0}/ApiVersionTest.cs | 0 .../{net7.0 => net#.0}/ApiVersionHandlerLogger{T}.cs | 0 .../DependencyInjection/IHttpClientBuilderExtensions.cs | 0 .../{net7.0 => net#.0}/ILoggerExtensions.cs | 0 .../{net7.0 => net#.0}/ApiVersionHandlerLoggerTTest.cs | 0 .../DependencyInjection/IHttpClientBuilderExtensionsTest.cs | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename src/Abstractions/src/Asp.Versioning.Abstractions/{net7.0 => net#.0}/ApiVersion.cs (100%) rename src/Abstractions/test/Asp.Versioning.Abstractions.Tests/{net7.0 => net#.0}/ApiVersionTest.cs (100%) rename src/Client/src/Asp.Versioning.Http.Client/{net7.0 => net#.0}/ApiVersionHandlerLogger{T}.cs (100%) rename src/Client/src/Asp.Versioning.Http.Client/{net7.0 => net#.0}/DependencyInjection/IHttpClientBuilderExtensions.cs (100%) rename src/Client/src/Asp.Versioning.Http.Client/{net7.0 => net#.0}/ILoggerExtensions.cs (100%) rename src/Client/test/Asp.Versioning.Http.Client.Tests/{net7.0 => net#.0}/ApiVersionHandlerLoggerTTest.cs (100%) rename src/Client/test/Asp.Versioning.Http.Client.Tests/{net7.0 => net#.0}/DependencyInjection/IHttpClientBuilderExtensionsTest.cs (100%) diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/net#.0/ApiVersion.cs similarity index 100% rename from src/Abstractions/src/Asp.Versioning.Abstractions/net7.0/ApiVersion.cs rename to src/Abstractions/src/Asp.Versioning.Abstractions/net#.0/ApiVersion.cs diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net7.0/ApiVersionTest.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net#.0/ApiVersionTest.cs similarity index 100% rename from src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net7.0/ApiVersionTest.cs rename to src/Abstractions/test/Asp.Versioning.Abstractions.Tests/net#.0/ApiVersionTest.cs diff --git a/src/Client/src/Asp.Versioning.Http.Client/net7.0/ApiVersionHandlerLogger{T}.cs b/src/Client/src/Asp.Versioning.Http.Client/net#.0/ApiVersionHandlerLogger{T}.cs similarity index 100% rename from src/Client/src/Asp.Versioning.Http.Client/net7.0/ApiVersionHandlerLogger{T}.cs rename to src/Client/src/Asp.Versioning.Http.Client/net#.0/ApiVersionHandlerLogger{T}.cs diff --git a/src/Client/src/Asp.Versioning.Http.Client/net7.0/DependencyInjection/IHttpClientBuilderExtensions.cs b/src/Client/src/Asp.Versioning.Http.Client/net#.0/DependencyInjection/IHttpClientBuilderExtensions.cs similarity index 100% rename from src/Client/src/Asp.Versioning.Http.Client/net7.0/DependencyInjection/IHttpClientBuilderExtensions.cs rename to src/Client/src/Asp.Versioning.Http.Client/net#.0/DependencyInjection/IHttpClientBuilderExtensions.cs diff --git a/src/Client/src/Asp.Versioning.Http.Client/net7.0/ILoggerExtensions.cs b/src/Client/src/Asp.Versioning.Http.Client/net#.0/ILoggerExtensions.cs similarity index 100% rename from src/Client/src/Asp.Versioning.Http.Client/net7.0/ILoggerExtensions.cs rename to src/Client/src/Asp.Versioning.Http.Client/net#.0/ILoggerExtensions.cs diff --git a/src/Client/test/Asp.Versioning.Http.Client.Tests/net7.0/ApiVersionHandlerLoggerTTest.cs b/src/Client/test/Asp.Versioning.Http.Client.Tests/net#.0/ApiVersionHandlerLoggerTTest.cs similarity index 100% rename from src/Client/test/Asp.Versioning.Http.Client.Tests/net7.0/ApiVersionHandlerLoggerTTest.cs rename to src/Client/test/Asp.Versioning.Http.Client.Tests/net#.0/ApiVersionHandlerLoggerTTest.cs diff --git a/src/Client/test/Asp.Versioning.Http.Client.Tests/net7.0/DependencyInjection/IHttpClientBuilderExtensionsTest.cs b/src/Client/test/Asp.Versioning.Http.Client.Tests/net#.0/DependencyInjection/IHttpClientBuilderExtensionsTest.cs similarity index 100% rename from src/Client/test/Asp.Versioning.Http.Client.Tests/net7.0/DependencyInjection/IHttpClientBuilderExtensionsTest.cs rename to src/Client/test/Asp.Versioning.Http.Client.Tests/net#.0/DependencyInjection/IHttpClientBuilderExtensionsTest.cs From a7c23adbe87a9271e34b659ef2e60b124ca98959 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 4 Dec 2023 16:11:19 -0800 Subject: [PATCH 063/131] Update .NET version for tools --- .devcontainer/Dockerfile | 2 +- .devcontainer/devcontainer.json | 2 +- .github/workflows/codeql-analysis.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 378b3123..e5eb2a2a 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,5 +1,5 @@ # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.177.0/containers/dotnet/.devcontainer/base.Dockerfile # [Choice] .NET version: 7.0, 6.0, 5.0 -ARG VARIANT="6.0" +ARG VARIANT="8.0" FROM mcr.microsoft.com/vscode/devcontainers/dotnet \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c40d7ef5..0ee6f006 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -20,7 +20,7 @@ "remoteEnv": { "PATH": "${containerWorkspaceFolder}/.dotnet:${containerEnv:PATH}", "DOTNET_MULTILEVEL_LOOKUP": "0", - "TARGET": "net6.0", + "TARGET": "net8.0", "DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER": "true" }, // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ef4ad775..ec3be18c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -31,7 +31,7 @@ jobs: uses: actions/setup-dotnet@v3 id: installdotnet with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Create temporary global.json run: echo '{"sdk":{"version":"${{ steps.installdotnet.outputs.dotnet-version }}"}}' > ./global.json From 7ebdad78b40d0a2bfed8e01e354c89bc0dcde63d Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 4 Dec 2023 16:11:31 -0800 Subject: [PATCH 064/131] Update sign tool --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index a05db39d..e5d8caad 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "sign": { - "version": "0.9.1-beta.23356.1", + "version": "0.9.1-beta.23530.1", "commands": [ "sign" ] From 2bc1b951c8afaf67711224d630d8260f5c00da84 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 4 Dec 2023 16:11:53 -0800 Subject: [PATCH 065/131] Update branches that trigger builds --- .github/dependabot.yml | 4 ++-- azure-pipelines.yml | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6978ee55..4cedbb39 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -19,7 +19,7 @@ updates: # only servicing 5.x release - package-ecosystem: "nuget" directory: "/" - target-branch: "release/5.0" + target-branch: "release/5.1" schedule: interval: "monthly" allow: @@ -29,5 +29,5 @@ updates: reviewers: - "commonsensesoftware" commit-message: - prefix: "[release/5.0] " + prefix: "[release/5.1] " include: scope \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a9aa3dc4..f75e041a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -2,6 +2,7 @@ trigger: branches: include: - main + - release/* paths: exclude: - .config @@ -16,6 +17,7 @@ trigger: pr: - main +- release/* # build at least once a month so the build badge is up-to-date schedules: From 2174334eb2771d5820ccea97904b8fdb3462239b Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 4 Dec 2023 16:12:39 -0800 Subject: [PATCH 066/131] Update code analysis rules --- .editorconfig | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index fe5a4a9e..31fcbe59 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,6 +6,8 @@ root = true # don't use tabs for indentation [*] indent_style = space +vsspell_section_id = 41b65011239a40959ccaae2a4ec7044a +vsspell_ignored_words_41b65011239a40959ccaae2a4ec7044a = clr|Edm|middleware|Mvc|odata|Validator|Accessor|app|inline # code files [*.{cs,csx,vb,vbx}] @@ -110,6 +112,13 @@ dotnet_diagnostic.SA1502.severity = none dotnet_diagnostic.SA1516.severity = none dotnet_diagnostic.SA1600.severity = none +# TEMP: currently suppressed rules due to false positives +# REF: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 +# REF: https://github.com/dotnet/aspnetcore/issues/52556 +dotnet_diagnostic.SA1010.severity = none # Opening square brackets should be spaced correctly +dotnet_diagnostic.ASP0022.severity = none # Route conflict detected between route handlers +dotnet_diagnostic.ASP0023.severity = none # Route conflict detected between route handlers + # test settings # Default severity for analyzer diagnostics with category 'Reliability' @@ -128,6 +137,7 @@ dotnet_diagnostic.CA1707.severity = none dotnet_diagnostic.CA1711.severity = none dotnet_diagnostic.CA1716.severity = none dotnet_diagnostic.CA1806.severity = none +dotnet_diagnostic.CA1861.severity = none dotnet_diagnostic.CA2007.severity = none dotnet_diagnostic.CA2234.severity = none dotnet_code_quality.CA2000.excluded_symbol_names = HttpRequestMessage|HttpResponseMessage|HttpConfiguration|HttpRouteCollection|HostedHttpRouteCollection|HttpServer|HttpClient @@ -138,6 +148,13 @@ dotnet_diagnostic.SA1300.severity = none dotnet_diagnostic.SA1507.severity = none dotnet_diagnostic.SA1601.severity = none +# TEMP: currently suppressed rules due to false positives +# REF: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 +# REF: https://github.com/dotnet/aspnetcore/issues/52556 +dotnet_diagnostic.SA1010.severity = none # Opening square brackets should be spaced correctly +dotnet_diagnostic.ASP0022.severity = none # Route conflict detected between route handlers +dotnet_diagnostic.ASP0023.severity = none # Route conflict detected between route handlers + # test methods should use all lowercase characters dotnet_naming_symbols.test_methods.applicable_kinds = method dotnet_naming_symbols.test_methods.applicable_accessibilities = public @@ -180,4 +197,4 @@ dotnet_naming_style.test_methods.word_separator = _ dotnet_naming_rule.test_methods.style = test_methods dotnet_naming_rule.test_methods.symbols = test_methods -dotnet_naming_rule.test_methods.severity = error \ No newline at end of file +dotnet_naming_rule.test_methods.severity = error From efd4845fed71bbce417b83dae636e031b7831071 Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 4 Dec 2023 16:32:37 -0800 Subject: [PATCH 067/131] Centralize .NET versions and make major releases easier to update --- build/test.targets | 4 ++-- examples/AspNetCore/Directory.Build.props | 10 ++++++++++ examples/AspNetCore/OData/Directory.Build.props | 5 ++--- .../ODataAdvancedExample/ODataAdvancedExample.csproj | 4 ---- .../OData/ODataBasicExample/ODataBasicExample.csproj | 4 ---- .../ODataConventionsExample.csproj | 4 ---- .../ODataOpenApiExample/ODataOpenApiExample.csproj | 5 ++--- .../SomeODataOpenApiExample.csproj | 5 ++--- .../WebApi/BasicExample/BasicExample.csproj | 4 ---- .../ByNamespaceExample/ByNamespaceExample.csproj | 4 ---- .../ConventionsExample/ConventionsExample.csproj | 4 ---- .../MinimalApiExample/MinimalApiExample.csproj | 1 - .../MinimalOpenApiExample.csproj | 6 +----- .../WebApi/OpenApiExample/OpenApiExample.csproj | 3 +-- .../Asp.Versioning.Abstractions.csproj | 12 ++++++------ .../Asp.Versioning.Abstractions.Tests.csproj | 10 +++++----- .../Asp.Versioning.Mvc.Acceptance.Tests.csproj | 4 ++-- .../Asp.Versioning.OData.ApiExplorer.csproj | 2 +- .../Asp.Versioning.OData/Asp.Versioning.OData.csproj | 2 +- .../Asp.Versioning.OData.ApiExplorer.Tests.csproj | 2 +- .../Asp.Versioning.OData.Tests.csproj | 2 +- .../Asp.Versioning.Http/Asp.Versioning.Http.csproj | 2 +- .../Asp.Versioning.Mvc.ApiExplorer.csproj | 2 +- .../src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj | 2 +- .../Asp.Versioning.Http.Tests.csproj | 2 +- .../Asp.Versioning.Mvc.ApiExplorer.Tests.csproj | 2 +- .../Asp.Versioning.Mvc.Tests.csproj | 2 +- .../Asp.Versioning.Http.Client.csproj | 11 ++++++----- .../Asp.Versioning.Http.Client.Tests.csproj | 10 +++++----- src/Directory.Build.props | 4 ++++ 30 files changed, 58 insertions(+), 76 deletions(-) create mode 100644 examples/AspNetCore/Directory.Build.props diff --git a/build/test.targets b/build/test.targets index b5c39483..a3fc5fea 100644 --- a/build/test.targets +++ b/build/test.targets @@ -3,7 +3,7 @@ 6.8.0 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The value cannot be an empty string. + + \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionActionSelector.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionActionSelector.cs index a8b5a729..e23f72a8 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionActionSelector.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionActionSelector.cs @@ -24,11 +24,7 @@ public class ApiVersionActionSelector : IHttpActionSelector /// controller context. public virtual HttpActionDescriptor? SelectAction( HttpControllerContext controllerContext ) { - if ( controllerContext == null ) - { - throw new ArgumentNullException( nameof( controllerContext ) ); - } - + ArgumentNullException.ThrowIfNull( controllerContext ); var internalSelector = GetInternalSelector( controllerContext.ControllerDescriptor ); return internalSelector.SelectAction( controllerContext, SelectActionVersion ); } @@ -41,10 +37,7 @@ public class ApiVersionActionSelector : IHttpActionSelector /// specified controller descriptor. public virtual ILookup GetActionMapping( HttpControllerDescriptor controllerDescriptor ) { - if ( controllerDescriptor == null ) - { - throw new ArgumentNullException( nameof( controllerDescriptor ) ); - } + ArgumentNullException.ThrowIfNull( controllerDescriptor ); var actionMappings = ( from descriptor in controllerDescriptor.AsEnumerable( includeCandidates: true ) let selector = GetInternalSelector( descriptor ) @@ -65,15 +58,8 @@ public virtual ILookup GetActionMapping( HttpContr /// ambiguous among the provided list of candidate actions. protected virtual HttpActionDescriptor? SelectActionVersion( HttpControllerContext controllerContext, IReadOnlyList candidateActions ) { - if ( controllerContext == null ) - { - throw new ArgumentNullException( nameof( controllerContext ) ); - } - - if ( candidateActions == null ) - { - throw new ArgumentNullException( nameof( candidateActions ) ); - } + ArgumentNullException.ThrowIfNull( controllerContext ); + ArgumentNullException.ThrowIfNull( candidateActions ); if ( candidateActions.Count == 0 ) { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionParameterBinding.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionParameterBinding.cs index cc34b6a8..a9f4d9af 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionParameterBinding.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ApiVersionParameterBinding.cs @@ -25,11 +25,7 @@ public override Task ExecuteBindingAsync( HttpActionContext actionContext, CancellationToken cancellationToken ) { - if ( actionContext == null ) - { - throw new ArgumentNullException( nameof( actionContext ) ); - } - + ArgumentNullException.ThrowIfNull( actionContext ); var value = actionContext.Request.ApiVersionProperties().RequestedApiVersion; SetValue( actionContext, value ); return CompletedTask; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/HttpControllerDescriptorGroup.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/HttpControllerDescriptorGroup.cs index 4e3aab24..884a1747 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/HttpControllerDescriptorGroup.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/HttpControllerDescriptorGroup.cs @@ -32,7 +32,7 @@ public class HttpControllerDescriptorGroup : HttpControllerDescriptor, IReadOnly /// HTTP controller descriptors. public HttpControllerDescriptorGroup( HttpConfiguration configuration, string controllerName, params HttpControllerDescriptor[] controllerDescriptors ) : base( configuration, controllerName, controllerDescriptors?[0].ControllerType ) => - descriptors = controllerDescriptors ?? throw new ArgumentNullException( nameof( controllerDescriptors ) ); + descriptors = controllerDescriptors ?? throw new System.ArgumentNullException( nameof( controllerDescriptors ) ); /// /// Initializes a new instance of the class. @@ -50,7 +50,7 @@ public HttpControllerDescriptorGroup( HttpConfiguration configuration, string co /// HTTP controller descriptors. public HttpControllerDescriptorGroup( HttpConfiguration configuration, string controllerName, IReadOnlyList controllerDescriptors ) : base( configuration, controllerName, controllerDescriptors?[0].ControllerType ) => - descriptors = controllerDescriptors ?? throw new ArgumentNullException( nameof( controllerDescriptors ) ); + descriptors = controllerDescriptors ?? throw new System.ArgumentNullException( nameof( controllerDescriptors ) ); /// /// Creates and returns a controller for the specified request. diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ActionApiVersionConventionBuilderBase.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ActionApiVersionConventionBuilderBase.cs index aea7e59b..9dd43313 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ActionApiVersionConventionBuilderBase.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ActionApiVersionConventionBuilderBase.cs @@ -13,10 +13,7 @@ public partial class ActionApiVersionConventionBuilderBase : IApiVersionConventi /// public virtual void ApplyTo( HttpActionDescriptor item ) { - if ( item == null ) - { - throw new ArgumentNullException( nameof( item ) ); - } + ArgumentNullException.ThrowIfNull( item ); var attributes = new List(); diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ControllerApiVersionConventionBuilderBase.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ControllerApiVersionConventionBuilderBase.cs index a75dc789..1480d3ba 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ControllerApiVersionConventionBuilderBase.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Conventions/ControllerApiVersionConventionBuilderBase.cs @@ -35,10 +35,7 @@ public abstract class ControllerApiVersionConventionBuilderBase : ApiVersionConv /// to apply the conventions to. public virtual void ApplyTo( HttpControllerDescriptor item ) { - if ( item == null ) - { - throw new ArgumentNullException( nameof( item ) ); - } + ArgumentNullException.ThrowIfNull( item ); var attributes = new List(); diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/HeaderApiVersionReader.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/HeaderApiVersionReader.cs index b18405aa..a3294be2 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/HeaderApiVersionReader.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/HeaderApiVersionReader.cs @@ -10,10 +10,7 @@ public partial class HeaderApiVersionReader /// public virtual IReadOnlyList Read( HttpRequestMessage request ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } + ArgumentNullException.ThrowIfNull( request ); var count = HeaderNames.Count; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReader.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReader.cs index d748aa0b..30bd1303 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReader.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReader.cs @@ -10,10 +10,7 @@ public partial class MediaTypeApiVersionReader /// public virtual IReadOnlyList Read( HttpRequestMessage request ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } + ArgumentNullException.ThrowIfNull( request ); var contentType = request.Content?.Headers.ContentType; var version = contentType is null ? default : ReadContentTypeHeader( contentType ); diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs index 78f24891..29e19c6b 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs @@ -22,10 +22,7 @@ public partial class MediaTypeApiVersionReaderBuilder /// The template syntax is the same used by route templates; however, constraints are not supported. public virtual MediaTypeApiVersionReaderBuilder Template( string template, string? parameterName = default ) { - if ( string.IsNullOrEmpty( template ) ) - { - throw new ArgumentNullException( nameof( template ) ); - } + ArgumentException.ThrowIfNullOrEmpty( template ); if ( string.IsNullOrEmpty( parameterName ) ) { @@ -38,7 +35,7 @@ from segment in content.Subsegments.OfType() if ( segments.Count() > 1 ) { var message = string.Format( CultureInfo.CurrentCulture, CommonSR.InvalidMediaTypeTemplate, template ); - throw new ArgumentException( message, nameof( template ) ); + throw new System.ArgumentException( message, nameof( template ) ); } } diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/QueryStringApiVersionReader.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/QueryStringApiVersionReader.cs index 3f119177..a4d4b148 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/QueryStringApiVersionReader.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/QueryStringApiVersionReader.cs @@ -10,10 +10,7 @@ public partial class QueryStringApiVersionReader /// public virtual IReadOnlyList Read( HttpRequestMessage request ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } + ArgumentNullException.ThrowIfNull(request); var count = ParameterNames.Count; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReportApiVersionsAttribute.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReportApiVersionsAttribute.cs index 2e7cfd8a..782118fa 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReportApiVersionsAttribute.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/ReportApiVersionsAttribute.cs @@ -18,10 +18,7 @@ public sealed partial class ReportApiVersionsAttribute /// response provided that there is a response and the executed action was not version-neutral. public override void OnActionExecuted( HttpActionExecutedContext actionExecutedContext ) { - if ( actionExecutedContext == null ) - { - throw new ArgumentNullException( nameof( actionExecutedContext ) ); - } + ArgumentNullException.ThrowIfNull( actionExecutedContext ); var response = actionExecutedContext.Response; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs index 9d78028c..ad930d65 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionRouteConstraint.cs @@ -2,7 +2,6 @@ namespace Asp.Versioning.Routing; -using System.Web.Http; using System.Web.Http.Routing; /// @@ -21,10 +20,7 @@ public sealed class ApiVersionRouteConstraint : IHttpRouteConstraint /// True if the route constraint is matched; otherwise, false. public bool Match( HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection ) { - if ( values == null ) - { - throw new ArgumentNullException( nameof( values ) ); - } + ArgumentNullException.ThrowIfNull( values ); if ( string.IsNullOrEmpty( parameterName ) ) { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionUrlHelper.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionUrlHelper.cs index cb414b6e..8a1beea4 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionUrlHelper.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/ApiVersionUrlHelper.cs @@ -15,7 +15,7 @@ public class ApiVersionUrlHelper : UrlHelper /// The inner URL helper. public ApiVersionUrlHelper( UrlHelper url ) { - Url = url ?? throw new ArgumentNullException( nameof( url ) ); + Url = url ?? throw new System.ArgumentNullException( nameof( url ) ); if ( url.Request != null ) { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/UrlHelperExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/UrlHelperExtensions.cs index a53f0f3e..121d6e75 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/UrlHelperExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Routing/UrlHelperExtensions.cs @@ -3,6 +3,7 @@ namespace System.Web.Http.Routing; using Asp.Versioning.Routing; +using Backport; /// /// Provides extension methods for . @@ -22,10 +23,7 @@ public static class UrlHelperExtensions /// it would be erroneously added as a query string parameter. public static UrlHelper WithoutApiVersion( this UrlHelper urlHelper ) { - if ( urlHelper == null ) - { - throw new ArgumentNullException( nameof( urlHelper ) ); - } + ArgumentNullException.ThrowIfNull( urlHelper ); if ( urlHelper is WithoutApiVersionUrlHelper ) { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs index 9dcdbed1..c272abc8 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs @@ -114,6 +114,15 @@ internal static string ApiVersionUnspecified { } } + /// + /// Looks up a localized string similar to The value cannot be an empty string.. + /// + internal static string Argument_EmptyString { + get { + return ResourceManager.GetString("Argument_EmptyString", resourceCulture); + } + } + /// /// Looks up a localized string similar to No route providing a controller name was found to match request URI '{0}'.. /// diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx index 29ed1630..dced0e03 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx @@ -135,6 +135,9 @@ An API version is required, but was not specified. + + The value cannot be an empty string. + No route providing a controller name was found to match request URI '{0}'. diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs index b1f43605..e6163773 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpRequestMessageExtensions.cs @@ -3,6 +3,7 @@ namespace System.Net.Http; using Asp.Versioning; +using Backport; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Web.Http; @@ -73,10 +74,7 @@ public static ApiVersioningOptions GetApiVersioningOptions( this HttpRequestMess /// The current API versioning properties. public static ApiVersionRequestProperties ApiVersionProperties( this HttpRequestMessage request ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } + ArgumentNullException.ThrowIfNull( request ); if ( request.Properties.TryGetValue( ApiVersionPropertiesKey, out ApiVersionRequestProperties? properties ) ) { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpResponseMessageExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpResponseMessageExtensions.cs index 4b9d2354..2f9e1510 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpResponseMessageExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Net.Http/HttpResponseMessageExtensions.cs @@ -3,7 +3,7 @@ namespace System.Net.Http; using Asp.Versioning; -using System; +using Backport; using System.Collections.Generic; using System.Net.Http.Headers; @@ -22,15 +22,8 @@ public static class HttpResponseMessageExtensions /// The sunset policy to write. public static void WriteSunsetPolicy( this HttpResponseMessage response, SunsetPolicy sunsetPolicy ) { - if ( response == null ) - { - throw new ArgumentNullException( nameof( response ) ); - } - - if ( sunsetPolicy == null ) - { - throw new ArgumentNullException( nameof( sunsetPolicy ) ); - } + ArgumentNullException.ThrowIfNull( response ); + ArgumentNullException.ThrowIfNull( sunsetPolicy ); var headers = response.Headers; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpActionDescriptorExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpActionDescriptorExtensions.cs index 1e523558..77a1f230 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpActionDescriptorExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpActionDescriptorExtensions.cs @@ -3,6 +3,7 @@ namespace System.Web.Http; using Asp.Versioning; +using Backport; using System.ComponentModel; using System.Web.Http.Controllers; @@ -20,10 +21,7 @@ public static class HttpActionDescriptorExtensions /// The API version information for the action. public static ApiVersionMetadata GetApiVersionMetadata( this HttpActionDescriptor action ) { - if ( action == null ) - { - throw new ArgumentNullException( nameof( action ) ); - } + ArgumentNullException.ThrowIfNull( action ); if ( action.Properties.TryGetValue( typeof( ApiVersionMetadata ), out ApiVersionMetadata? value ) ) { @@ -42,11 +40,7 @@ public static ApiVersionMetadata GetApiVersionMetadata( this HttpActionDescripto [EditorBrowsable( EditorBrowsableState.Never )] public static void SetApiVersionMetadata( this HttpActionDescriptor action, ApiVersionMetadata value ) { - if ( action == null ) - { - throw new ArgumentNullException( nameof( action ) ); - } - + ArgumentNullException.ThrowIfNull( action ); action.Properties.AddOrUpdate( typeof( ApiVersionMetadata ), value, ( key, oldValue ) => value ); } diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs index 94062c5b..0051de64 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpConfigurationExtensions.cs @@ -7,6 +7,7 @@ namespace System.Web.Http; using Asp.Versioning.Dependencies; using Asp.Versioning.Dispatcher; using Asp.Versioning.Formatting; +using Backport; using System.Globalization; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; @@ -26,11 +27,7 @@ public static class HttpConfigurationExtensions /// The current API versioning options. public static ApiVersioningOptions GetApiVersioningOptions( this HttpConfiguration configuration ) { - if ( configuration == null ) - { - throw new ArgumentNullException( nameof( configuration ) ); - } - + ArgumentNullException.ThrowIfNull( configuration ); return configuration.ApiVersioningServices().ApiVersioningOptions; } @@ -44,11 +41,7 @@ public static ApiVersioningOptions GetApiVersioningOptions( this HttpConfigurati /// OData Error Responses. public static void ConvertProblemDetailsToErrorObject( this HttpConfiguration configuration ) { - if ( configuration == null ) - { - throw new ArgumentNullException( nameof( configuration ) ); - } - + ArgumentNullException.ThrowIfNull( configuration ); configuration.Initializer += EnableErrorObjectResponses; } @@ -58,11 +51,7 @@ public static void ConvertProblemDetailsToErrorObject( this HttpConfiguration co /// The configuration that will use service versioning. public static void AddApiVersioning( this HttpConfiguration configuration ) { - if ( configuration == null ) - { - throw new ArgumentNullException( nameof( configuration ) ); - } - + ArgumentNullException.ThrowIfNull( configuration ); configuration.AddApiVersioning( new ApiVersioningOptions() ); } @@ -73,15 +62,8 @@ public static void AddApiVersioning( this HttpConfiguration configuration ) /// An action used to configure the provided options. public static void AddApiVersioning( this HttpConfiguration configuration, Action setupAction ) { - if ( configuration == null ) - { - throw new ArgumentNullException( nameof( configuration ) ); - } - - if ( setupAction == null ) - { - throw new ArgumentNullException( nameof( setupAction ) ); - } + ArgumentNullException.ThrowIfNull( configuration ); + ArgumentNullException.ThrowIfNull( setupAction ); var options = new ApiVersioningOptions(); diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpControllerDescriptorExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpControllerDescriptorExtensions.cs index 66d1454f..8efe2d8c 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpControllerDescriptorExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpControllerDescriptorExtensions.cs @@ -4,6 +4,7 @@ namespace System.Web.Http; using Asp.Versioning; using Asp.Versioning.Controllers; +using Backport; using System.ComponentModel; using System.Web.Http.Controllers; using System.Web.Http.Description; @@ -34,10 +35,7 @@ public static class HttpControllerDescriptorExtensions [EditorBrowsable( EditorBrowsableState.Never )] public static ApiVersionModel GetApiVersionModel( this HttpControllerDescriptor controllerDescriptor ) { - if ( controllerDescriptor == null ) - { - throw new ArgumentNullException( nameof( controllerDescriptor ) ); - } + ArgumentNullException.ThrowIfNull( controllerDescriptor ); if ( controllerDescriptor.Properties.TryGetValue( typeof( ApiVersionModel ), out ApiVersionModel? value ) ) { @@ -56,10 +54,7 @@ public static ApiVersionModel GetApiVersionModel( this HttpControllerDescriptor [EditorBrowsable( EditorBrowsableState.Never )] public static void SetApiVersionModel( this HttpControllerDescriptor controllerDescriptor, ApiVersionModel value ) { - if ( controllerDescriptor == null ) - { - throw new ArgumentNullException( nameof( controllerDescriptor ) ); - } + ArgumentNullException.ThrowIfNull( controllerDescriptor ); controllerDescriptor.Properties.AddOrUpdate( typeof( ApiVersionModel ), value, ( key, oldValue ) => value ); @@ -84,10 +79,7 @@ public static IEnumerable AsEnumerable( this HttpContr internal static IEnumerable AsEnumerable( this HttpControllerDescriptor controllerDescriptor, bool includeCandidates ) { - if ( controllerDescriptor == null ) - { - throw new ArgumentNullException( nameof( controllerDescriptor ) ); - } + ArgumentNullException.ThrowIfNull( controllerDescriptor ); var visited = new HashSet(); diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteCollectionExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteCollectionExtensions.cs index d2d3a7c1..64310188 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteCollectionExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteCollectionExtensions.cs @@ -2,6 +2,7 @@ namespace System.Web.Http; +using Backport; using System.Reflection; using System.Web.Http.Routing; using static System.Reflection.BindingFlags; @@ -19,10 +20,7 @@ public static class HttpRouteCollectionExtensions /// routes mapped to their name. public static IReadOnlyDictionary ToDictionary( this HttpRouteCollection routes ) { - if ( routes == null ) - { - throw new ArgumentNullException( nameof( routes ) ); - } + ArgumentNullException.ThrowIfNull( routes ); const string HostedHttpRouteCollection = "System.Web.Http.WebHost.Routing.HostedHttpRouteCollection"; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/UrlSegmentApiVersionReader.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/UrlSegmentApiVersionReader.cs index 04e31809..5cfa1e1e 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/UrlSegmentApiVersionReader.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/UrlSegmentApiVersionReader.cs @@ -11,10 +11,7 @@ public partial class UrlSegmentApiVersionReader /// public virtual IReadOnlyList Read( HttpRequestMessage request ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } + ArgumentNullException.ThrowIfNull(request); if ( reentrant ) { diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs index 426272bc..b6ba5605 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/CustomerModelConfiguration.cs @@ -25,15 +25,8 @@ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBui public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - - if ( apiVersion == null ) - { - throw new ArgumentNullException( nameof( apiVersion ) ); - } + ArgumentNullException.ThrowIfNull( builder ); + ArgumentNullException.ThrowIfNull( apiVersion ); switch ( apiVersion.MajorVersion ) { diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs index e125f126..20cbfd11 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/OrderModelConfiguration.cs @@ -22,10 +22,7 @@ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuilde public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); if ( supportedApiVersion == null || supportedApiVersion == apiVersion ) { diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs index 79646c06..0c49a498 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/PersonModelConfiguration.cs @@ -25,15 +25,8 @@ private static EntityTypeConfiguration ConfigureCurrent( ODataModelBuild public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - - if ( apiVersion == null ) - { - throw new ArgumentNullException( nameof( apiVersion ) ); - } + ArgumentNullException.ThrowIfNull( builder ); + ArgumentNullException.ThrowIfNull( apiVersion ); switch ( apiVersion.MajorVersion ) { diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs index 1d485b8b..4456fa39 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/Configuration/WeatherForecastModelConfiguration.cs @@ -22,10 +22,7 @@ private static EntityTypeConfiguration ConfigureCurrent( ODataM public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); if ( supportedApiVersion == null || supportedApiVersion == apiVersion ) { diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs index 06611d5b..06ad7201 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs @@ -92,10 +92,7 @@ public ODataApiDescriptionProvider( /// The default implementation performs no action. public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var results = context.Results; var visited = new HashSet( capacity: results.Count, new ApiDescriptionComparer() ); diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataQueryOptionModelMetadata.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataQueryOptionModelMetadata.cs index 062a95aa..bc2a8825 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataQueryOptionModelMetadata.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataQueryOptionModelMetadata.cs @@ -23,10 +23,7 @@ public sealed class ODataQueryOptionModelMetadata : ModelMetadata public ODataQueryOptionModelMetadata( IModelMetadataProvider modelMetadataProvider, Type modelType, string description ) : base( ModelMetadataIdentity.ForType( modelType ) ) { - if ( modelMetadataProvider == null ) - { - throw new ArgumentNullException( nameof( modelMetadataProvider ) ); - } + ArgumentNullException.ThrowIfNull( modelMetadataProvider ); inner = modelMetadataProvider.GetMetadataForType( modelType ); Description = description; diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs index 84873316..bd419e18 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs @@ -87,10 +87,7 @@ protected ODataApiExplorerOptions Options /// public virtual void OnProvidersExecuting( ApiDescriptionProviderContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var results = FilterResults( context.Results, Conventions ); @@ -125,10 +122,7 @@ public virtual void OnProvidersExecuting( ApiDescriptionProviderContext context /// public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var actions = context.Actions; diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs index 1e7fe4e0..6ca75048 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs @@ -13,10 +13,7 @@ public partial class ImplicitModelBoundSettingsConvention /// public void ApplyTo( ApiDescription apiDescription ) { - if ( apiDescription == null ) - { - throw new ArgumentNullException( nameof( apiDescription ) ); - } + ArgumentNullException.ThrowIfNull( apiDescription ); var responses = apiDescription.SupportedResponseTypes; diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs index cef80319..bfab96ce 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataValidationSettingsConvention.cs @@ -16,10 +16,7 @@ public partial class ODataValidationSettingsConvention /// public virtual void ApplyTo( ApiDescription apiDescription ) { - if ( apiDescription == null ) - { - throw new ArgumentNullException( nameof( apiDescription ) ); - } + ArgumentNullException.ThrowIfNull( apiDescription ); if ( !IsSupported( apiDescription.HttpMethod ) ) { diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs index c2702f8f..159717b1 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -24,11 +24,7 @@ public static class IApiVersioningBuilderExtensions /// The original . public static IApiVersioningBuilder AddODataApiExplorer( this IApiVersioningBuilder builder ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); AddApiExplorerServices( builder ); return builder; } @@ -41,11 +37,7 @@ public static IApiVersioningBuilder AddODataApiExplorer( this IApiVersioningBuil /// The original . public static IApiVersioningBuilder AddODataApiExplorer( this IApiVersioningBuilder builder, Action setupAction ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); AddApiExplorerServices( builder ); builder.Services.Configure( setupAction ); return builder; diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/ApplicationModels/ODataControllerSpecification.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/ApplicationModels/ODataControllerSpecification.cs index 2a778ee8..eb1cebd0 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/ApplicationModels/ODataControllerSpecification.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/ApplicationModels/ODataControllerSpecification.cs @@ -14,10 +14,7 @@ public sealed class ODataControllerSpecification : IApiControllerSpecification /// public bool IsSatisfiedBy( ControllerModel controller ) { - if ( controller == null ) - { - throw new ArgumentNullException( nameof( controller ) ); - } + ArgumentNullException.ThrowIfNull( controller ); if ( ODataControllerSpecification.IsSatisfiedBy( controller ) ) { diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Builder/IApplicationBuilderExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Builder/IApplicationBuilderExtensions.cs index 0ce54373..c6248429 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Builder/IApplicationBuilderExtensions.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Builder/IApplicationBuilderExtensions.cs @@ -17,11 +17,7 @@ public static class IApplicationBuilderExtensions /// The original . public static IApplicationBuilder UseVersionedODataBatching( this IApplicationBuilder app ) { - if ( app == null ) - { - throw new ArgumentNullException( nameof( app ) ); - } - + ArgumentNullException.ThrowIfNull( app ); return app.UseMiddleware(); } } \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs index 79bba8dd..aba443f5 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -31,11 +31,7 @@ public static class IApiVersioningBuilderExtensions /// The original . public static IApiVersioningBuilder AddOData( this IApiVersioningBuilder builder ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); AddServices( builder.AddMvc().Services ); return builder; } @@ -49,10 +45,7 @@ public static IApiVersioningBuilder AddOData( this IApiVersioningBuilder builder [CLSCompliant( false )] public static IApiVersioningBuilder AddOData( this IApiVersioningBuilder builder, Action setupAction ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); var services = builder.AddMvc().Services; AddServices( services ); diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IServiceCollectionExtensions.cs index cb86af84..16432665 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IServiceCollectionExtensions.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IServiceCollectionExtensions.cs @@ -61,10 +61,7 @@ internal static bool ConfigureDefaultFeatureProviders( this ApplicationPartManag /// The extended . public static void AddModelConfigurationsAsServices( this IServiceCollection services ) { - if ( services == null ) - { - throw new ArgumentNullException( nameof( services ) ); - } + ArgumentNullException.ThrowIfNull( services ); var partManager = services.GetOrCreateApplicationPartManager(); diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/VersionedODataBatchMiddleware.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/VersionedODataBatchMiddleware.cs index 1eff0220..df89490d 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/VersionedODataBatchMiddleware.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/VersionedODataBatchMiddleware.cs @@ -31,10 +31,7 @@ public VersionedODataBatchMiddleware( RequestDelegate next, VersionedODataOption /// A task representing the asynchronous operation. public Task Invoke( HttpContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); if ( HttpMethods.IsPost( context.Request.Method ) && options.TryGetBatchHandler( context, out var handler ) ) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeatureProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeatureProvider.cs index 7f6254b3..66cf48e9 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeatureProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeatureProvider.cs @@ -15,15 +15,8 @@ public class ModelConfigurationFeatureProvider : IApplicationFeatureProvider public void PopulateFeature( IEnumerable parts, ModelConfigurationFeature feature ) { - if ( parts == null ) - { - throw new ArgumentNullException( nameof( parts ) ); - } - - if ( feature == null ) - { - throw new ArgumentNullException( nameof( feature ) ); - } + ArgumentNullException.ThrowIfNull( parts ); + ArgumentNullException.ThrowIfNull( feature ); var types = from part in parts.OfType() from type in part.Types @@ -43,10 +36,7 @@ where IsModelConfiguration( type ) /// True if the type is a model configuration; otherwise false. protected virtual bool IsModelConfiguration( Type type ) { - if ( type == null ) - { - throw new ArgumentNullException( nameof( type ) ); - } + ArgumentNullException.ThrowIfNull( type ); if ( !type.IsClass || type.IsAbstract || !type.IsPublic || type.ContainsGenericParameters ) { diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs index 0d9cdcc0..8b1dd45e 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs @@ -62,10 +62,7 @@ public virtual void OnProvidersExecuted( ApplicationModelProviderContext context /// public virtual void OnProvidersExecuting( ApplicationModelProviderContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var (metadataControllers, supported, deprecated) = CollateApiVersions( context.Result ); diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataOptionsPostSetup.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataOptionsPostSetup.cs index a8e4f1ff..dfe7314f 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataOptionsPostSetup.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataOptionsPostSetup.cs @@ -35,10 +35,7 @@ public ODataOptionsPostSetup( /// public void PostConfigure( string? name, ODataOptions options ) { - if ( options == null ) - { - throw new ArgumentNullException( nameof( options ) ); - } + ArgumentNullException.ThrowIfNull( options ); var conventions = options.Conventions; var replacements = 0; diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataModelBuilder.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataModelBuilder.cs index 8f560845..9b576129 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataModelBuilder.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataModelBuilder.cs @@ -20,12 +20,10 @@ public VersionedODataModelBuilder( IODataApiVersionCollectionProvider apiVersionCollectionProvider, IEnumerable modelConfigurations ) { - if ( modelConfigurations == null ) - { - throw new ArgumentNullException( nameof( modelConfigurations ) ); - } + ArgumentNullException.ThrowIfNull( apiVersionCollectionProvider ); + ArgumentNullException.ThrowIfNull( modelConfigurations ); - this.apiVersionCollectionProvider = apiVersionCollectionProvider ?? throw new ArgumentNullException( nameof( apiVersionCollectionProvider ) ); + this.apiVersionCollectionProvider = apiVersionCollectionProvider; foreach ( var configuration in modelConfigurations ) { diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataOptions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataOptions.cs index 7319d4a7..64df342f 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataOptions.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataOptions.cs @@ -87,10 +87,7 @@ public IReadOnlyDictionary Mapping /// True if the was successfully retrieved; otherwise, false. public virtual bool TryGetBatchHandler( HttpContext context, [NotNullWhen( true )] out ODataBatchHandler? handler ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); if ( batchMapping is null ) { diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs index 8f29de7b..93d92b9c 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataTemplateTranslator.cs @@ -17,15 +17,8 @@ public sealed class VersionedODataTemplateTranslator : IODataTemplateTranslator /// public ODataPath? Translate( ODataPathTemplate path, ODataTemplateTranslateContext context ) { - if ( path == null ) - { - throw new ArgumentNullException( nameof( path ) ); - } - - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( path ); + ArgumentNullException.ThrowIfNull( context ); var apiVersion = context.HttpContext.GetRequestedApiVersion(); diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs index b1e7c390..14b3aeca 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs @@ -36,11 +36,7 @@ public DefaultMetadataMatcherPolicy( IApiVersionParameterSource parameterSource, IOptions options ) { - if ( parameterSource == null ) - { - throw new ArgumentNullException( nameof( parameterSource ) ); - } - + ArgumentNullException.ThrowIfNull( parameterSource ); versionsByUrl = parameterSource.VersionsByUrl(); this.options = options; } @@ -51,10 +47,7 @@ public DefaultMetadataMatcherPolicy( /// public virtual bool AppliesToEndpoints( IReadOnlyList endpoints ) { - if ( endpoints == null ) - { - throw new ArgumentNullException( nameof( endpoints ) ); - } + ArgumentNullException.ThrowIfNull( endpoints ); for ( var i = 0; i < endpoints.Count; i++ ) { @@ -70,10 +63,7 @@ public virtual bool AppliesToEndpoints( IReadOnlyList endpoints ) /// public IReadOnlyList GetEdges( IReadOnlyList endpoints ) { - if ( endpoints == null ) - { - throw new ArgumentNullException( nameof( endpoints ) ); - } + ArgumentNullException.ThrowIfNull( endpoints ); var edges = default( List ); var lowestApiVersion = default( ApiVersion ); @@ -138,10 +128,7 @@ public IReadOnlyList GetEdges( IReadOnlyList endpoints /// public PolicyJumpTable BuildJumpTable( int exitDestination, IReadOnlyList edges ) { - if ( edges == null ) - { - throw new ArgumentNullException( nameof( edges ) ); - } + ArgumentNullException.ThrowIfNull( edges ); Debug.Assert( edges.Count == 1, $"Only a single edge was expected, but {edges.Count} edges were provided" ); diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs index 8ea4c5a2..230017ed 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedAttributeRoutingConvention.cs @@ -3,7 +3,6 @@ namespace Asp.Versioning.Routing; using Asp.Versioning.ApplicationModels; -using Asp.Versioning.OData; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.OData.Routing.Conventions; using Microsoft.AspNetCore.OData.Routing.Parser; @@ -29,10 +28,7 @@ public VersionedAttributeRoutingConvention( /// public override bool AppliesToAction( ODataControllerActionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var metadata = context.Action .Selectors diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedMetadataRoutingConvention.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedMetadataRoutingConvention.cs index 77ff8f71..c506e50f 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedMetadataRoutingConvention.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/VersionedMetadataRoutingConvention.cs @@ -19,11 +19,7 @@ public class VersionedMetadataRoutingConvention : MetadataRoutingConvention /// public override bool AppliesToController( ODataControllerActionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } - + ArgumentNullException.ThrowIfNull( context ); metadataController ??= typeof( VersionedMetadataController ); return metadataController.IsAssignableFrom( context.Controller.ControllerType ); } @@ -31,10 +27,7 @@ public override bool AppliesToController( ODataControllerActionContext context ) /// public override bool AppliesToAction( ODataControllerActionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var action = context.Action; var actionName = action.ActionMethod.Name; diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs index d28d6728..a70422b7 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/AllConfigurations.cs @@ -13,10 +13,7 @@ public class AllConfigurations : IModelConfiguration /// public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); builder.Function( "GetSalesTaxRate" ).Returns().Parameter( "PostalCode" ); } diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs index 5f16263e..573b67a0 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/OrderModelConfiguration.cs @@ -14,10 +14,7 @@ public class OrderModelConfiguration : IModelConfiguration /// public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); var order = builder.EntitySet( "Orders" ).EntityType.HasKey( o => o.Id ); diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs index 19db9f7f..662bf0ba 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/PersonModelConfiguration.cs @@ -14,10 +14,7 @@ public class PersonModelConfiguration : IModelConfiguration /// public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); var person = builder.EntitySet( "People" ).EntityType.HasKey( p => p.Id ); diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs index dc988cc2..eb71daf6 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/ProductConfiguration.cs @@ -14,10 +14,7 @@ public class ProductConfiguration : IModelConfiguration /// public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); if ( apiVersion < ApiVersions.V3 ) { diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs index 0ec3e700..9bcd5bbc 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/Configuration/SupplierConfiguration.cs @@ -14,10 +14,7 @@ public class SupplierConfiguration : IModelConfiguration /// public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); if ( apiVersion < ApiVersions.V3 ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/EndpointApiVersionMetadataCollationProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/EndpointApiVersionMetadataCollationProvider.cs index 3ad5433a..45c14725 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/EndpointApiVersionMetadataCollationProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/EndpointApiVersionMetadataCollationProvider.cs @@ -30,10 +30,7 @@ public EndpointApiVersionMetadataCollationProvider( EndpointDataSource endpointD /// public void Execute( ApiVersionMetadataCollationContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var endpoints = endpointDataSource.Endpoints; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/ApiVersionSetBuilder.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/ApiVersionSetBuilder.cs index 3a0303f6..e24853fc 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/ApiVersionSetBuilder.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/ApiVersionSetBuilder.cs @@ -110,10 +110,7 @@ public virtual ApiVersionSetBuilder AdvertisesDeprecatedApiVersion( ApiVersion a /// A new API version model. protected internal virtual ApiVersionModel BuildApiVersionModel( ApiVersioningOptions options ) { - if ( options == null ) - { - throw new ArgumentNullException( nameof( options ) ); - } + ArgumentNullException.ThrowIfNull( options ); if ( VersionNeutral ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs index 7fab3aa7..154a8f39 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs @@ -27,10 +27,7 @@ public static TBuilder WithApiVersionSet( ApiVersionSet apiVersionSet ) where TBuilder : notnull, IEndpointConventionBuilder { - if ( apiVersionSet == null ) - { - throw new ArgumentNullException( nameof( apiVersionSet ) ); - } + ArgumentNullException.ThrowIfNull( apiVersionSet ); builder.Add( endpoint => AddMetadata( endpoint, apiVersionSet ) ); builder.Finally( EndpointBuilderFinalizer.FinalizeEndpoints ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointRouteBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointRouteBuilderExtensions.cs index 2aad4196..e6054e34 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointRouteBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointRouteBuilderExtensions.cs @@ -23,14 +23,9 @@ public static class IEndpointRouteBuilderExtensions /// A new API version set builder. public static ApiVersionSetBuilder NewApiVersionSet( this IEndpointRouteBuilder endpoints, string? name = default ) { - if ( endpoints == null ) - { - throw new ArgumentNullException( nameof( endpoints ) ); - } - + ArgumentNullException.ThrowIfNull( endpoints ); var create = endpoints.ServiceProvider.GetService(); - - return create is null ? new ApiVersionSetBuilder( name ) : create( name ); + return create is null ? new( name ) : create( name ); } /// @@ -43,10 +38,7 @@ public static class IEndpointRouteBuilderExtensions public static IVersionedEndpointRouteBuilder WithApiVersionSet( this TBuilder builder, string? name = default ) where TBuilder : notnull, IEndpointRouteBuilder, IEndpointConventionBuilder { - if ( builder is null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); if ( builder.HasMetadata() ) { @@ -71,10 +63,7 @@ public static class IEndpointRouteBuilderExtensions /// A new instance. public static IVersionedEndpointRouteBuilder NewVersionedApi( this IEndpointRouteBuilder builder, string? name = default ) { - if ( builder is null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); if ( builder.IsNestedGroup() ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs index 66c1152a..615b505a 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs @@ -49,10 +49,7 @@ public static IApiVersioningBuilder AddApiVersioning( this IServiceCollection se /// The original . public static IApiVersioningBuilder EnableApiVersionBinding( this IApiVersioningBuilder builder ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); // currently required because there is no other hook. // 1. TryParse does not work because: @@ -79,10 +76,7 @@ public static IApiVersioningBuilder EnableApiVersionBinding( this IApiVersioning private static void AddApiVersioningServices( IServiceCollection services ) { - if ( services == null ) - { - throw new ArgumentNullException( nameof( services ) ); - } + ArgumentNullException.ThrowIfNull( services ); services.TryAddSingleton(); services.AddSingleton( sp => sp.GetRequiredService>().Value.ApiVersionReader ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/HeaderApiVersionReader.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/HeaderApiVersionReader.cs index 674cb196..f6602402 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/HeaderApiVersionReader.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/HeaderApiVersionReader.cs @@ -14,10 +14,7 @@ public partial class HeaderApiVersionReader /// public virtual IReadOnlyList Read( HttpRequest request ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } + ArgumentNullException.ThrowIfNull( request ); var count = HeaderNames.Count; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpContextExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpContextExtensions.cs index e504004c..60167f90 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpContextExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpContextExtensions.cs @@ -18,10 +18,7 @@ public static class HttpContextExtensions /// The current API versioning feature. public static IApiVersioningFeature ApiVersioningFeature( this HttpContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var feature = context.Features.Get(); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpRequestExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpRequestExtensions.cs index 209d5832..406e7a61 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpRequestExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpRequestExtensions.cs @@ -3,9 +3,9 @@ namespace Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Template; using System.ComponentModel; +using RoutePattern = Microsoft.AspNetCore.Routing.Patterns.RoutePattern; /// /// Provides extension methods for . @@ -29,10 +29,7 @@ public static bool TryGetApiVersionFromPath( string constraintName, [NotNullWhen( true )] out string? apiVersion ) { - if ( routePatterns == null ) - { - throw new ArgumentNullException( nameof( routePatterns ) ); - } + ArgumentNullException.ThrowIfNull( routePatterns ); if ( string.IsNullOrEmpty( constraintName ) || routePatterns.Count == 0 ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpResponseExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpResponseExtensions.cs index 85b375fa..bd0ffefc 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpResponseExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpResponseExtensions.cs @@ -23,15 +23,8 @@ public static class HttpResponseExtensions [CLSCompliant( false )] public static void WriteSunsetPolicy( this HttpResponse response, SunsetPolicy sunsetPolicy ) { - if ( response == null ) - { - throw new ArgumentNullException( nameof( response ) ); - } - - if ( sunsetPolicy == null ) - { - throw new ArgumentNullException( nameof( sunsetPolicy ) ); - } + ArgumentNullException.ThrowIfNull( response ); + ArgumentNullException.ThrowIfNull( sunsetPolicy ); var headers = response.Headers; @@ -81,10 +74,7 @@ private static void AddLinkHeaders( IHeaderDictionary headers, IList public static void AddApiVersionToContentType( this HttpResponse response, string name ) { - if ( response == null ) - { - throw new ArgumentNullException( nameof( response ) ); - } + ArgumentNullException.ThrowIfNull( response ); if ( response.StatusCode < 200 && response.StatusCode > 299 ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReader.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReader.cs index 19d3a291..48cdd92c 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReader.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReader.cs @@ -13,10 +13,7 @@ public partial class MediaTypeApiVersionReader /// public virtual IReadOnlyList Read( HttpRequest request ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } + ArgumentNullException.ThrowIfNull( request ); var headers = request.GetTypedHeaders(); var contentType = headers.ContentType; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs index 6947cf0c..b7055397 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs @@ -28,10 +28,7 @@ public partial class MediaTypeApiVersionReaderBuilder #pragma warning restore CA1716 // Identifiers should not match keywords #pragma warning restore IDE0079 { - if ( string.IsNullOrEmpty( template ) ) - { - throw new ArgumentNullException( nameof( template ) ); - } + ArgumentException.ThrowIfNullOrEmpty( template ); var routePattern = RoutePatternFactory.Parse( template ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/QueryStringApiVersionReader.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/QueryStringApiVersionReader.cs index 29d0d03e..6d167d33 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/QueryStringApiVersionReader.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/QueryStringApiVersionReader.cs @@ -14,10 +14,7 @@ public partial class QueryStringApiVersionReader /// public virtual IReadOnlyList Read( HttpRequest request ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } + ArgumentNullException.ThrowIfNull( request ); var count = ParameterNames.Count; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionLinkGenerator.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionLinkGenerator.cs index c3040163..abdaa172 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionLinkGenerator.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionLinkGenerator.cs @@ -65,7 +65,7 @@ public class ApiVersionLinkGenerator : LinkGenerator public override string? GetUriByAddress( TAddress address, RouteValueDictionary values, - string? scheme, + string scheme, HostString host, PathString pathBase = default, FragmentString fragment = default, @@ -73,15 +73,8 @@ public class ApiVersionLinkGenerator : LinkGenerator private static void AddApiVersionRouteValueIfNecessary( HttpContext httpContext, RouteValueDictionary values ) { - if ( httpContext == null ) - { - throw new ArgumentNullException( nameof( httpContext ) ); - } - - if ( values == null ) - { - throw new ArgumentNullException( nameof( values ) ); - } + ArgumentNullException.ThrowIfNull( httpContext ); + ArgumentNullException.ThrowIfNull( values ); var feature = httpContext.ApiVersioningFeature(); var key = feature.RouteParameter; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs index 1ba4358e..e2eaaaff 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs @@ -40,10 +40,15 @@ public ApiVersionMatcherPolicy( IOptions options, ILogger logger ) { - this.apiVersionParser = apiVersionParser ?? throw new ArgumentNullException( nameof( apiVersionParser ) ); - collator = new( providers ?? throw new ArgumentNullException( nameof( providers ) ), options ); - this.options = options ?? throw new ArgumentNullException( nameof( options ) ); - this.logger = logger ?? throw new ArgumentNullException( nameof( logger ) ); + ArgumentNullException.ThrowIfNull( apiVersionParser ); + ArgumentNullException.ThrowIfNull( providers ); + ArgumentNullException.ThrowIfNull( options ); + ArgumentNullException.ThrowIfNull( logger ); + + this.apiVersionParser = apiVersionParser; + collator = new( providers, options ); + this.options = options; + this.logger = logger; } /// @@ -58,10 +63,7 @@ public ApiVersionMatcherPolicy( /// public bool AppliesToEndpoints( IReadOnlyList endpoints ) { - if ( endpoints == null ) - { - throw new ArgumentNullException( nameof( endpoints ) ); - } + ArgumentNullException.ThrowIfNull( endpoints ); for ( var i = 0; i < endpoints.Count; i++ ) { @@ -77,15 +79,8 @@ public bool AppliesToEndpoints( IReadOnlyList endpoints ) /// public Task ApplyAsync( HttpContext httpContext, CandidateSet candidates ) { - if ( httpContext == null ) - { - throw new ArgumentNullException( nameof( httpContext ) ); - } - - if ( candidates == null ) - { - throw new ArgumentNullException( nameof( candidates ) ); - } + ArgumentNullException.ThrowIfNull( httpContext ); + ArgumentNullException.ThrowIfNull( candidates ); var feature = httpContext.ApiVersioningFeature(); var apiVersion = feature.RequestedApiVersion; @@ -110,10 +105,7 @@ public Task ApplyAsync( HttpContext httpContext, CandidateSet candidates ) /// public PolicyJumpTable BuildJumpTable( int exitDestination, IReadOnlyList edges ) { - if ( edges == null ) - { - throw new ArgumentNullException( nameof( edges ) ); - } + ArgumentNullException.ThrowIfNull( edges ); var rejection = new RouteDestination( exitDestination ); var capacity = edges.Count - EdgeBuilder.NumberOfRejectionEndpoints; @@ -178,10 +170,7 @@ public PolicyJumpTable BuildJumpTable( int exitDestination, IReadOnlyList public IReadOnlyList GetEdges( IReadOnlyList endpoints ) { - if ( endpoints == null ) - { - throw new ArgumentNullException( nameof( endpoints ) ); - } + ArgumentNullException.ThrowIfNull( endpoints ); var capacity = endpoints.Count; var builder = new EdgeBuilder( capacity, ApiVersionSource, Options, logger ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionRouteConstraint.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionRouteConstraint.cs index 943315f5..6b4cfd76 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionRouteConstraint.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionRouteConstraint.cs @@ -23,10 +23,7 @@ public sealed class ApiVersionRouteConstraint : IRouteConstraint /// True if the route constraint is matched; otherwise, false. public bool Match( HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection ) { - if ( values == null ) - { - throw new ArgumentNullException( nameof( values ) ); - } + ArgumentNullException.ThrowIfNull( values ); if ( string.IsNullOrEmpty( routeKey ) ) { @@ -43,10 +40,7 @@ public bool Match( HttpContext? httpContext, IRouter? route, string routeKey, Ro return !string.IsNullOrEmpty( value ); } - if ( httpContext == null ) - { - throw new ArgumentNullException( nameof( httpContext ) ); - } + ArgumentNullException.ThrowIfNull( httpContext ); var parser = httpContext.RequestServices.GetRequiredService(); var feature = httpContext.ApiVersioningFeature(); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersioningRouteOptionsSetup.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersioningRouteOptionsSetup.cs index f41b6ed3..332c1842 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersioningRouteOptionsSetup.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersioningRouteOptionsSetup.cs @@ -2,8 +2,8 @@ namespace Asp.Versioning.Routing; -using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Options; +using RouteOptions = Microsoft.AspNetCore.Routing.RouteOptions; /// /// Represents the API versioning configuration for ASP.NET Core routing options. @@ -22,10 +22,7 @@ public class ApiVersioningRouteOptionsSetup : IPostConfigureOptions public virtual void PostConfigure( string? name, RouteOptions options ) { - if ( options == null ) - { - throw new ArgumentNullException( nameof( options ) ); - } + ArgumentNullException.ThrowIfNull( options ); var key = versioningOptions.Value.RouteConstraintName; options.ConstraintMap.Add( key, typeof( ApiVersionRouteConstraint ) ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/RoutePatternComparer.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/RoutePatternComparer.cs index 04d0a9d0..ca635586 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/RoutePatternComparer.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/RoutePatternComparer.cs @@ -2,7 +2,7 @@ namespace Asp.Versioning.Routing; -using Microsoft.AspNetCore.Routing.Patterns; +using RoutePattern = Microsoft.AspNetCore.Routing.Patterns.RoutePattern; /// /// Represents a comparer for comparing instances. diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/RoutePatternExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/RoutePatternExtensions.cs index d7582b1d..fa467989 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/RoutePatternExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/RoutePatternExtensions.cs @@ -2,8 +2,8 @@ namespace Asp.Versioning.Routing; -using Microsoft.AspNetCore.Routing.Patterns; using System.ComponentModel; +using RoutePattern = Microsoft.AspNetCore.Routing.Patterns.RoutePattern; /// /// Provides extension methods for . @@ -20,10 +20,7 @@ public static class RoutePatternExtensions /// True if the has the ; otherwise, false. public static bool HasVersionConstraint( this RoutePattern routePattern, string constraintName ) { - if ( routePattern == null ) - { - throw new ArgumentNullException( nameof( routePattern ) ); - } + ArgumentNullException.ThrowIfNull( routePattern ); if ( string.IsNullOrEmpty( constraintName ) ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/UrlSegmentApiVersionReader.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/UrlSegmentApiVersionReader.cs index 3ddb892b..ba9eec82 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/UrlSegmentApiVersionReader.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/UrlSegmentApiVersionReader.cs @@ -13,10 +13,7 @@ public partial class UrlSegmentApiVersionReader /// public virtual IReadOnlyList Read( HttpRequest request ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } + ArgumentNullException.ThrowIfNull( request ); if ( reentrant ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiDescriptionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiDescriptionExtensions.cs index ce48b2a1..a0cf3835 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiDescriptionExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiDescriptionExtensions.cs @@ -37,10 +37,7 @@ public static class ApiDescriptionExtensions /// True if the API description is deprecated; otherwise, false. public static bool IsDeprecated( this ApiDescription apiDescription ) { - if ( apiDescription == null ) - { - throw new ArgumentNullException( nameof( apiDescription ) ); - } + ArgumentNullException.ThrowIfNull( apiDescription ); var metatadata = apiDescription.ActionDescriptor.GetApiVersionMetadata(); @@ -81,15 +78,8 @@ public static bool IsDeprecated( this ApiDescription apiDescription ) /// True if the API description was updated; otherwise, false. public static bool TryUpdateRelativePathAndRemoveApiVersionParameter( this ApiDescription apiDescription, ApiExplorerOptions options ) { - if ( apiDescription == null ) - { - throw new ArgumentNullException( nameof( apiDescription ) ); - } - - if ( options == null ) - { - throw new ArgumentNullException( nameof( options ) ); - } + ArgumentNullException.ThrowIfNull( apiDescription ); + ArgumentNullException.ThrowIfNull( options ); if ( !options.SubstituteApiVersionInUrl ) { @@ -137,10 +127,7 @@ public static bool TryUpdateRelativePathAndRemoveApiVersionParameter( this ApiDe /// A new API description. public static ApiDescription Clone( this ApiDescription apiDescription ) { - if ( apiDescription == null ) - { - throw new ArgumentNullException( nameof( apiDescription ) ); - } + ArgumentNullException.ThrowIfNull( apiDescription ); var clone = new ApiDescription() { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionModelMetadata.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionModelMetadata.cs index 44b08cd4..bdd9020f 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionModelMetadata.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionModelMetadata.cs @@ -23,11 +23,7 @@ public sealed class ApiVersionModelMetadata : ModelMetadata public ApiVersionModelMetadata( IModelMetadataProvider modelMetadataProvider, string description ) : base( ModelMetadataIdentity.ForType( typeof( string ) ) ) { - if ( modelMetadataProvider == null ) - { - throw new ArgumentNullException( nameof( modelMetadataProvider ) ); - } - + ArgumentNullException.ThrowIfNull( modelMetadataProvider ); inner = modelMetadataProvider.GetMetadataForType( typeof( string ) ); this.description = description; } diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs index 8730244c..3b01026b 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs @@ -56,10 +56,7 @@ public DefaultApiVersionDescriptionProvider( /// A read-only list of API version descriptions. protected virtual IReadOnlyList Describe( IReadOnlyList metadata ) { - if ( metadata == null ) - { - throw new ArgumentNullException( nameof( metadata ) ); - } + ArgumentNullException.ThrowIfNull( metadata ); var descriptions = new List( capacity: metadata.Count ); var supported = new HashSet(); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs index f806121d..1c1d2e63 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -25,11 +25,7 @@ public static class IApiVersioningBuilderExtensions /// The original . public static IApiVersioningBuilder AddApiExplorer( this IApiVersioningBuilder builder ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); AddApiExplorerServices( builder ); return builder; } @@ -42,14 +38,9 @@ public static IApiVersioningBuilder AddApiExplorer( this IApiVersioningBuilder b /// The original . public static IApiVersioningBuilder AddApiExplorer( this IApiVersioningBuilder builder, Action setupAction ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); AddApiExplorerServices( builder ); builder.Services.Configure( setupAction ); - return builder; } diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs index 716c321c..7af19256 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs @@ -58,10 +58,7 @@ public GroupedApiVersionDescriptionProvider( /// version descriptions. protected virtual IReadOnlyList Describe( IReadOnlyList metadata ) { - if ( metadata == null ) - { - throw new ArgumentNullException( nameof( metadata ) ); - } + ArgumentNullException.ThrowIfNull( metadata ); var descriptions = new SortedSet( new ApiVersionDescriptionComparer() ); var supported = new HashSet(); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/IEndpointRouteBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/IEndpointRouteBuilderExtensions.cs index e42c66d6..d20148f0 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/IEndpointRouteBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/IEndpointRouteBuilderExtensions.cs @@ -20,10 +20,7 @@ public static class IEndpointRouteBuilderExtensions /// A new read-only list ofAPI version descriptions. public static IReadOnlyList DescribeApiVersions( this IEndpointRouteBuilder endpoints ) { - if ( endpoints == null ) - { - throw new ArgumentNullException( nameof( endpoints ) ); - } + ArgumentNullException.ThrowIfNull( endpoints ); var services = endpoints.ServiceProvider; var factory = services.GetRequiredService(); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs index 45a8c8fe..776251d8 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs @@ -34,7 +34,13 @@ public VersionedApiDescriptionProvider( ISunsetPolicyManager sunsetPolicyManager, IModelMetadataProvider modelMetadataProvider, IOptions options ) - : this( sunsetPolicyManager, modelMetadataProvider, new SimpleConstraintResolver( options ), options ) { } + : this( + sunsetPolicyManager, + modelMetadataProvider, + new SimpleConstraintResolver( options ?? throw new ArgumentNullException( nameof( options ) ) ), + options ) + { + } // intentionally hiding IInlineConstraintResolver from public signature until ASP.NET Core fixes their bug // BUG: https://github.com/dotnet/aspnetcore/issues/41773 @@ -110,10 +116,7 @@ protected virtual void PopulateApiVersionParameters( ApiDescription apiDescripti /// The default implementation performs no action. public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var results = context.Results; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Abstractions/ActionDescriptorExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Abstractions/ActionDescriptorExtensions.cs index 61fb9fc0..c5afef16 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Abstractions/ActionDescriptorExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Abstractions/ActionDescriptorExtensions.cs @@ -17,10 +17,7 @@ public static class ActionDescriptorExtensions /// The API version information for the action. public static ApiVersionMetadata GetApiVersionMetadata( this ActionDescriptor action ) { - if ( action == null ) - { - throw new ArgumentNullException( nameof( action ) ); - } + ArgumentNullException.ThrowIfNull( action ); var endpointMetadata = action.EndpointMetadata; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiExplorer/ActionApiVersionMetadataCollationProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiExplorer/ActionApiVersionMetadataCollationProvider.cs index a4527467..6614986a 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiExplorer/ActionApiVersionMetadataCollationProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiExplorer/ActionApiVersionMetadataCollationProvider.cs @@ -29,10 +29,7 @@ public ActionApiVersionMetadataCollationProvider( IActionDescriptorCollectionPro /// public void Execute( ApiVersionMetadataCollationContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var actions = provider.ActionDescriptors.Items; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionCollator.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionCollator.cs index bbbbcd71..88c2c521 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionCollator.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionCollator.cs @@ -32,10 +32,7 @@ public class ApiVersionCollator : IActionDescriptorProvider /// public virtual void OnProvidersExecuted( ActionDescriptorProviderContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); foreach ( var actions in GroupActionsByController( context.Results ) ) { @@ -82,10 +79,7 @@ public virtual void OnProvidersExecuting( ActionDescriptorProviderContext contex /// protected virtual string GetControllerName( ActionDescriptor action ) { - if ( action == null ) - { - throw new ArgumentNullException( nameof( action ) ); - } + ArgumentNullException.ThrowIfNull( action ); if ( !action.RouteValues.TryGetValue( "controller", out var name ) || name is null ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionModelBinder.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionModelBinder.cs index 0e4f4618..965ed1bd 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionModelBinder.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionModelBinder.cs @@ -15,10 +15,7 @@ public class ApiVersionModelBinder : IModelBinder /// public virtual Task BindModelAsync( ModelBindingContext bindingContext ) { - if ( bindingContext == null ) - { - throw new ArgumentNullException( nameof( bindingContext ) ); - } + ArgumentNullException.ThrowIfNull( bindingContext ); var feature = bindingContext.HttpContext.ApiVersioningFeature(); var model = feature.RequestedApiVersion; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionModelBinderProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionModelBinderProvider.cs index 25955ba2..f878eab0 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionModelBinderProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionModelBinderProvider.cs @@ -10,10 +10,7 @@ internal sealed class ApiVersionModelBinderProvider : IModelBinderProvider public IModelBinder? GetBinder( ModelBinderProviderContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); if ( typeof( ApiVersion ).IsAssignableFrom( context.Metadata.ModelType ) ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersioningApplicationModelProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersioningApplicationModelProvider.cs index f675197b..72023770 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersioningApplicationModelProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersioningApplicationModelProvider.cs @@ -69,10 +69,7 @@ public virtual void OnProvidersExecuted( ApplicationModelProviderContext context /// public virtual void OnProvidersExecuting( ApplicationModelProviderContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var application = context.Result; var controllers = ControllerFilter.Apply( application.Controllers ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersioningMvcOptionsSetup.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersioningMvcOptionsSetup.cs index d7ff47fd..bf7290ba 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersioningMvcOptionsSetup.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersioningMvcOptionsSetup.cs @@ -25,10 +25,7 @@ public class ApiVersioningMvcOptionsSetup : IPostConfigureOptions /// public virtual void PostConfigure( string? name, MvcOptions options ) { - if ( options == null ) - { - throw new ArgumentNullException( nameof( options ) ); - } + ArgumentNullException.ThrowIfNull( options ); var value = versioningOptions.Value; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplicationModels/ApiBehaviorSpecification.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplicationModels/ApiBehaviorSpecification.cs index 438a76f0..69206a57 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplicationModels/ApiBehaviorSpecification.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplicationModels/ApiBehaviorSpecification.cs @@ -15,10 +15,7 @@ public sealed class ApiBehaviorSpecification : IApiControllerSpecification /// public bool IsSatisfiedBy( ControllerModel controller ) { - if ( controller == null ) - { - throw new ArgumentNullException( nameof( controller ) ); - } + ArgumentNullException.ThrowIfNull( controller ); // REF: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ApplicationModels/ApiBehaviorApplicationModelProvider.cs if ( controller.Attributes.OfType().Any() ) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplicationModels/ModelExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplicationModels/ModelExtensions.cs index 043435f8..9ccb55d5 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplicationModels/ModelExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplicationModels/ModelExtensions.cs @@ -21,10 +21,7 @@ public static class ModelExtensions [EditorBrowsable( EditorBrowsableState.Never )] public static ApiVersionModel GetApiVersionModel( this ControllerModel controller ) { - if ( controller == null ) - { - throw new ArgumentNullException( nameof( controller ) ); - } + ArgumentNullException.ThrowIfNull( controller ); if ( controller.Properties.TryGetValue( typeof( ApiVersionModel ), out var value ) && value is ApiVersionModel model ) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplyContentTypeVersionActionFilter.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplyContentTypeVersionActionFilter.cs index 20c64805..a1d110ed 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplyContentTypeVersionActionFilter.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplyContentTypeVersionActionFilter.cs @@ -20,13 +20,8 @@ public void OnActionExecuted( ActionExecutedContext context ) { } public void OnActionExecuting( ActionExecutingContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } - + ArgumentNullException.ThrowIfNull( context ); var httpContext = context.HttpContext; - httpContext.Response.OnStarting( AddContentTypeApiVersion, httpContext ); } diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs index bba2494e..fb9933b1 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs @@ -16,10 +16,7 @@ public partial class ActionApiVersionConventionBuilderBase : IApiVersionConventi /// The action model to apply the conventions to. public virtual void ApplyTo( ActionModel item ) { - if ( item == null ) - { - throw new ArgumentNullException( nameof( item ) ); - } + ArgumentNullException.ThrowIfNull( item ); MergeAttributesWithConventions( item.Attributes ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ControllerApiVersionConventionBuilderBase.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ControllerApiVersionConventionBuilderBase.cs index 6c7e548d..5222b4a7 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ControllerApiVersionConventionBuilderBase.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ControllerApiVersionConventionBuilderBase.cs @@ -34,11 +34,7 @@ public abstract class ControllerApiVersionConventionBuilderBase : ApiVersionConv /// The controller model to apply the conventions to. public virtual void ApplyTo( ControllerModel item ) { - if ( item == null ) - { - throw new ArgumentNullException( nameof( item ) ); - } - + ArgumentNullException.ThrowIfNull( item ); MergeAttributesWithConventions( item.Attributes ); ApplyActionConventions( item ); } diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs index 63b73b46..894648c4 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -28,13 +28,8 @@ public static class IApiVersioningBuilderExtensions /// The original . public static IApiVersioningBuilder AddMvc( this IApiVersioningBuilder builder ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); AddServices( builder.Services ); - return builder; } @@ -46,10 +41,7 @@ public static IApiVersioningBuilder AddMvc( this IApiVersioningBuilder builder ) /// The original . public static IApiVersioningBuilder AddMvc( this IApiVersioningBuilder builder, Action setupAction ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); var services = builder.Services; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReportApiVersionsAttribute.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReportApiVersionsAttribute.cs index 7a60aafe..90348fff 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReportApiVersionsAttribute.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReportApiVersionsAttribute.cs @@ -22,10 +22,7 @@ public sealed partial class ReportApiVersionsAttribute /// streaming to the client. public override void OnActionExecuting( ActionExecutingContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var httpContext = context.HttpContext; var endpoint = httpContext.GetEndpoint(); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelper.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelper.cs index 5752e107..bbf03793 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelper.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelper.cs @@ -51,11 +51,7 @@ public ApiVersionUrlHelper( ActionContext actionContext, IUrlHelper url ) /// public virtual string? Action( UrlActionContext actionContext ) { - if ( actionContext == null ) - { - throw new ArgumentNullException( nameof( actionContext ) ); - } - + ArgumentNullException.ThrowIfNull( actionContext ); actionContext.Values = AddApiVersionRouteValueIfNecessary( actionContext.Values ); return Url.Action( actionContext ); } @@ -81,11 +77,7 @@ public ApiVersionUrlHelper( ActionContext actionContext, IUrlHelper url ) #pragma warning restore CA1055 // URI-like return values should not be strings #pragma warning restore IDE0079 { - if ( routeContext == null ) - { - throw new ArgumentNullException( nameof( routeContext ) ); - } - + ArgumentNullException.ThrowIfNull( routeContext ); routeContext.Values = AddApiVersionRouteValueIfNecessary( routeContext.Values ); return Url.RouteUrl( routeContext ); } diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelperFactory.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelperFactory.cs index 2f3a50f5..3c69680e 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelperFactory.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/ApiVersionUrlHelperFactory.cs @@ -26,10 +26,7 @@ public class ApiVersionUrlHelperFactory : IUrlHelperFactory /// public virtual IUrlHelper GetUrlHelper( ActionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var items = context.HttpContext.Items; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/IUrlHelperExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/IUrlHelperExtensions.cs index 46ad8f33..29fd1648 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/IUrlHelperExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/IUrlHelperExtensions.cs @@ -24,10 +24,7 @@ public static class IUrlHelperExtensions /// it would be erroneously added as a query string parameter. public static IUrlHelper WithoutApiVersion( this IUrlHelper urlHelper ) { - if ( urlHelper == null ) - { - throw new ArgumentNullException( nameof( urlHelper ) ); - } + ArgumentNullException.ThrowIfNull( urlHelper ); if ( urlHelper is WithoutApiVersionUrlHelper || urlHelper.ActionContext.HttpContext.Features.Get() is null ) diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiInformation.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiInformation.cs index 98cdde75..cc75c6e5 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiInformation.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiInformation.cs @@ -22,10 +22,10 @@ public ApiInformation( SunsetPolicy sunsetPolicy, IReadOnlyDictionary openApiDocumentUrls ) { - SupportedApiVersions = supportedVersions ?? throw new ArgumentNullException( nameof( supportedVersions ) ); - DeprecatedApiVersions = deprecatedVersions ?? throw new ArgumentNullException( nameof( deprecatedVersions ) ); - SunsetPolicy = sunsetPolicy ?? throw new ArgumentNullException( nameof( sunsetPolicy ) ); - OpenApiDocumentUrls = openApiDocumentUrls ?? throw new ArgumentNullException( nameof( openApiDocumentUrls ) ); + SupportedApiVersions = supportedVersions ?? throw new System.ArgumentNullException( nameof( supportedVersions ) ); + DeprecatedApiVersions = deprecatedVersions ?? throw new System.ArgumentNullException( nameof( deprecatedVersions ) ); + SunsetPolicy = sunsetPolicy ?? throw new System.ArgumentNullException( nameof( sunsetPolicy ) ); + OpenApiDocumentUrls = openApiDocumentUrls ?? throw new System.ArgumentNullException( nameof( openApiDocumentUrls ) ); } private ApiInformation() diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiNotificationContext.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiNotificationContext.cs index 7a1db927..449e249d 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiNotificationContext.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiNotificationContext.cs @@ -16,8 +16,8 @@ public class ApiNotificationContext /// The requested API version. public ApiNotificationContext( HttpResponseMessage response, ApiVersion apiVersion ) { - Response = response ?? throw new ArgumentNullException( nameof( response ) ); - ApiVersion = apiVersion ?? throw new ArgumentNullException( nameof( apiVersion ) ); + Response = response ?? throw new System.ArgumentNullException( nameof( response ) ); + ApiVersion = apiVersion ?? throw new System.ArgumentNullException( nameof( apiVersion ) ); } /// diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs index 08dd678e..49139052 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs @@ -26,15 +26,8 @@ public ApiVersionEnumerator( string headerName, IApiVersionParser? parser = default ) { - if ( response == null ) - { - throw new ArgumentNullException( nameof( response ) ); - } - - if ( string.IsNullOrEmpty( headerName ) ) - { - throw new ArgumentNullException( nameof( headerName ) ); - } + ArgumentNullException.ThrowIfNull( response ); + ArgumentException.ThrowIfNullOrEmpty( headerName ); this.values = response.Headers.TryGetValues( headerName, out var values ) diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHandler.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHandler.cs index 38563a84..5595251b 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHandler.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHandler.cs @@ -32,8 +32,8 @@ public ApiVersionHandler( IApiVersionParser? parser = default, ApiVersionHeaderEnumerable? enumerable = default ) { - this.apiVersionWriter = apiVersionWriter ?? throw new ArgumentNullException( nameof( apiVersionWriter ) ); - this.apiVersion = apiVersion ?? throw new ArgumentNullException( nameof( apiVersion ) ); + this.apiVersionWriter = apiVersionWriter ?? throw new System.ArgumentNullException( nameof( apiVersionWriter ) ); + this.apiVersion = apiVersion ?? throw new System.ArgumentNullException( nameof( apiVersion ) ); this.notification = notification ?? ApiNotification.None; this.parser = parser ?? ApiVersionParser.Default; this.enumerable = enumerable ?? new(); @@ -67,10 +67,7 @@ protected override async Task SendAsync( HttpRequestMessage /// True if the requested API has been deprecated; otherwise, false. protected virtual bool IsDeprecatedApi( HttpResponseMessage response ) { - if ( response == null ) - { - throw new ArgumentNullException( nameof( response ) ); - } + ArgumentNullException.ThrowIfNull( response ); foreach ( var reportedApiVersion in enumerable.Deprecated( response, parser ) ) { @@ -91,10 +88,7 @@ protected virtual bool IsDeprecatedApi( HttpResponseMessage response ) /// True if the requested API has a newer, supported version than the one requested; otherwise, false. protected virtual bool IsNewApiAvailable( HttpResponseMessage response ) { - if ( response == null ) - { - throw new ArgumentNullException( nameof( response ) ); - } + ArgumentNullException.ThrowIfNull( response ); foreach ( var reportedApiVersion in enumerable.Supported( response, parser ) ) { diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHeaderEnumerable.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHeaderEnumerable.cs index f06248aa..32dc0c69 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHeaderEnumerable.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionHeaderEnumerable.cs @@ -23,15 +23,11 @@ public ApiVersionHeaderEnumerable( string supportedHeaderName = ApiSupportedVersions, string deprecatedHeaderName = ApiDeprecatedVersions ) { - if ( string.IsNullOrEmpty( apiSupportedVersionsName = supportedHeaderName ) ) - { - throw new ArgumentNullException( nameof( supportedHeaderName ) ); - } + ArgumentException.ThrowIfNullOrEmpty( supportedHeaderName ); + ArgumentException.ThrowIfNullOrEmpty( deprecatedHeaderName ); - if ( string.IsNullOrEmpty( apiDeprecatedVersionsName = deprecatedHeaderName ) ) - { - throw new ArgumentNullException( nameof( deprecatedHeaderName ) ); - } + apiSupportedVersionsName = supportedHeaderName; + apiDeprecatedVersionsName = deprecatedHeaderName; } /// diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionWriter.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionWriter.cs index 3c0ab935..25e03fc9 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionWriter.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionWriter.cs @@ -18,10 +18,7 @@ public static IApiVersionWriter Combine( IApiVersionWriter apiVersionWriter, params IApiVersionWriter[] otherApiVersionwriters ) { - if ( apiVersionWriter == null ) - { - throw new ArgumentNullException( nameof( apiVersionWriter ) ); - } + ArgumentNullException.ThrowIfNull( apiVersionWriter ); int count; IApiVersionWriter[] apiVersionWriters; @@ -52,7 +49,7 @@ public static IApiVersionWriter Combine( IEnumerable apiVersi if ( writers is null || writers.Length == 0 ) { - throw new ArgumentException( SR.ZeroApiVersionWriters, nameof( apiVersionWriters ) ); + throw new System.ArgumentException( SR.ZeroApiVersionWriters, nameof( apiVersionWriters ) ); } return new CombinedApiVersionWriter( writers ); diff --git a/src/Client/src/Asp.Versioning.Http.Client/HeaderApiVersionWriter.cs b/src/Client/src/Asp.Versioning.Http.Client/HeaderApiVersionWriter.cs index 3735420d..5055db6a 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/HeaderApiVersionWriter.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/HeaderApiVersionWriter.cs @@ -15,26 +15,15 @@ public sealed class HeaderApiVersionWriter : IApiVersionWriter /// The HTTP header name to write the API version to. public HeaderApiVersionWriter( string headerName ) { - if ( string.IsNullOrEmpty( headerName ) ) - { - throw new ArgumentNullException( headerName ); - } - + ArgumentException.ThrowIfNullOrEmpty( headerName ); this.headerName = headerName; } /// public void Write( HttpRequestMessage request, ApiVersion apiVersion ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } - - if ( apiVersion == null ) - { - throw new ArgumentNullException( nameof( apiVersion ) ); - } + ArgumentNullException.ThrowIfNull( request ); + ArgumentNullException.ThrowIfNull( apiVersion ); var headers = request.Headers; diff --git a/src/Client/src/Asp.Versioning.Http.Client/MediaTypeApiVersionWriter.cs b/src/Client/src/Asp.Versioning.Http.Client/MediaTypeApiVersionWriter.cs index c9901c23..792c6379 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/MediaTypeApiVersionWriter.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/MediaTypeApiVersionWriter.cs @@ -24,26 +24,15 @@ public sealed class MediaTypeApiVersionWriter : IApiVersionWriter /// The name of the media type parameter to write the API version to. public MediaTypeApiVersionWriter( string parameterName ) { - if ( string.IsNullOrEmpty( parameterName ) ) - { - throw new ArgumentNullException( parameterName ); - } - + ArgumentException.ThrowIfNullOrEmpty( parameterName ); this.parameterName = parameterName; } /// public void Write( HttpRequestMessage request, ApiVersion apiVersion ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } - - if ( apiVersion == null ) - { - throw new ArgumentNullException( nameof( apiVersion ) ); - } + ArgumentNullException.ThrowIfNull( request ); + ArgumentNullException.ThrowIfNull( apiVersion ); UpdateAccept( request, apiVersion ); diff --git a/src/Client/src/Asp.Versioning.Http.Client/QueryStringApiVersionWriter.cs b/src/Client/src/Asp.Versioning.Http.Client/QueryStringApiVersionWriter.cs index eb72b775..caaba7d0 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/QueryStringApiVersionWriter.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/QueryStringApiVersionWriter.cs @@ -23,26 +23,15 @@ public sealed class QueryStringApiVersionWriter : IApiVersionWriter /// The query string parameter name to write the API version to. public QueryStringApiVersionWriter( string parameterName ) { - if ( string.IsNullOrEmpty( parameterName ) ) - { - throw new ArgumentNullException( parameterName ); - } - + ArgumentException.ThrowIfNullOrEmpty( parameterName ); this.parameterName = parameterName; } /// public void Write( HttpRequestMessage request, ApiVersion apiVersion ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } - - if ( apiVersion == null ) - { - throw new ArgumentNullException( nameof( apiVersion ) ); - } + ArgumentNullException.ThrowIfNull( request ); + ArgumentNullException.ThrowIfNull( apiVersion ); if ( request.RequestUri is not Uri url || url.Query.Contains( parameterName, OrdinalIgnoreCase ) ) diff --git a/src/Client/src/Asp.Versioning.Http.Client/SR.Designer.cs b/src/Client/src/Asp.Versioning.Http.Client/SR.Designer.cs index 8bdaa5bd..85db8768 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/SR.Designer.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/SR.Designer.cs @@ -61,6 +61,15 @@ internal SR() { } } + /// + /// Looks up a localized string similar to The value cannot be an empty string.. + /// + internal static string Argument_EmptyString { + get { + return ResourceManager.GetString("Argument_EmptyString", resourceCulture); + } + } + /// /// Looks up a localized string similar to At least one IApiVersionWriter must be specified.. /// diff --git a/src/Client/src/Asp.Versioning.Http.Client/SR.resx b/src/Client/src/Asp.Versioning.Http.Client/SR.resx index d283a951..4f8009c1 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/SR.resx +++ b/src/Client/src/Asp.Versioning.Http.Client/SR.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + The value cannot be an empty string. + At least one IApiVersionWriter must be specified. diff --git a/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpClientExtensions.cs b/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpClientExtensions.cs index dc38f355..b42c2d28 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpClientExtensions.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpClientExtensions.cs @@ -5,6 +5,9 @@ namespace System.Net.Http; using Asp.Versioning; using Asp.Versioning.Http; using static System.Net.HttpStatusCode; +#if NETSTANDARD +using ArgumentNullException = Backport.ArgumentNullException; +#endif /// /// Provides extension methods for . @@ -58,10 +61,7 @@ public static async Task GetApiInformationAsync( ApiVersionHeaderEnumerable? enumerable = default, CancellationToken cancellationToken = default ) { - if ( client == null ) - { - throw new ArgumentNullException( nameof( client ) ); - } + ArgumentNullException.ThrowIfNull( client ); using var request = new HttpRequestMessage( HttpMethod.Options, requestUrl ); var response = await client.SendAsync( request, cancellationToken ).ConfigureAwait( false ); diff --git a/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpResponseMessageExtensions.cs b/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpResponseMessageExtensions.cs index 5c70fd20..dd9f90fd 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpResponseMessageExtensions.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpResponseMessageExtensions.cs @@ -5,6 +5,9 @@ namespace System.Net.Http; using Asp.Versioning; using System.Globalization; using static System.StringComparison; +#if NETSTANDARD +using ArgumentNullException = Backport.ArgumentNullException; +#endif /// /// Provides extension methods for . @@ -21,10 +24,7 @@ public static class HttpResponseMessageExtensions /// A new sunset policy. public static SunsetPolicy ReadSunsetPolicy( this HttpResponseMessage response ) { - if ( response == null ) - { - throw new ArgumentNullException( nameof( response ) ); - } + ArgumentNullException.ThrowIfNull( response ); var headers = response.Headers; var date = default( DateTimeOffset ); @@ -80,10 +80,7 @@ public static IReadOnlyDictionary GetOpenApiDocumentUrls( this HttpResponseMessage response, IApiVersionParser? parser = default ) { - if ( response == null ) - { - throw new ArgumentNullException( nameof( response ) ); - } + ArgumentNullException.ThrowIfNull( response ); var urls = default( Dictionary ); diff --git a/src/Client/src/Asp.Versioning.Http.Client/UrlSegmentApiVersionWriter.cs b/src/Client/src/Asp.Versioning.Http.Client/UrlSegmentApiVersionWriter.cs index 4d8ac553..7237924b 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/UrlSegmentApiVersionWriter.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/UrlSegmentApiVersionWriter.cs @@ -18,26 +18,15 @@ public sealed class UrlSegmentApiVersionWriter : IApiVersionWriter /// The replacement token to write the API version to. public UrlSegmentApiVersionWriter( string token ) { - if ( string.IsNullOrEmpty( token ) ) - { - throw new ArgumentNullException( token ); - } - + ArgumentException.ThrowIfNullOrEmpty( token ); this.token = token; } /// public void Write( HttpRequestMessage request, ApiVersion apiVersion ) { - if ( request == null ) - { - throw new ArgumentNullException( nameof( request ) ); - } - - if ( apiVersion == null ) - { - throw new ArgumentNullException( nameof( apiVersion ) ); - } + ArgumentNullException.ThrowIfNull( request ); + ArgumentNullException.ThrowIfNull( apiVersion ); if ( request.RequestUri is not Uri url ) { diff --git a/src/Client/src/Asp.Versioning.Http.Client/net#.0/ApiVersionHandlerLogger{T}.cs b/src/Client/src/Asp.Versioning.Http.Client/net#.0/ApiVersionHandlerLogger{T}.cs index 0de53c22..4f8a8b86 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/net#.0/ApiVersionHandlerLogger{T}.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/net#.0/ApiVersionHandlerLogger{T}.cs @@ -23,18 +23,15 @@ public class ApiVersionHandlerLogger : ApiNotification /// The enumerable used to enumerate API versions. public ApiVersionHandlerLogger( ILogger logger, IApiVersionParser parser, ApiVersionHeaderEnumerable enumerable ) { - this.logger = logger ?? throw new ArgumentNullException( nameof( logger ) ); - this.parser = parser ?? throw new ArgumentNullException( nameof( parser ) ); - this.enumerable = enumerable ?? throw new ArgumentNullException( nameof( enumerable ) ); + this.logger = logger ?? throw new System.ArgumentNullException( nameof( logger ) ); + this.parser = parser ?? throw new System.ArgumentNullException( nameof( parser ) ); + this.enumerable = enumerable ?? throw new System.ArgumentNullException( nameof( enumerable ) ); } /// protected override void OnApiDeprecated( ApiNotificationContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var requestUrl = context.Response.RequestMessage!.RequestUri!; var apiVersion = context.ApiVersion; @@ -46,10 +43,7 @@ protected override void OnApiDeprecated( ApiNotificationContext context ) /// protected override void OnNewApiAvailable( ApiNotificationContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var requestUrl = context.Response.RequestMessage!.RequestUri!; var currentApiVersion = context.ApiVersion; diff --git a/src/Client/src/Asp.Versioning.Http.Client/net#.0/DependencyInjection/IHttpClientBuilderExtensions.cs b/src/Client/src/Asp.Versioning.Http.Client/net#.0/DependencyInjection/IHttpClientBuilderExtensions.cs index 37f744eb..62195d91 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/net#.0/DependencyInjection/IHttpClientBuilderExtensions.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/net#.0/DependencyInjection/IHttpClientBuilderExtensions.cs @@ -103,10 +103,7 @@ public static IHttpClientBuilder AddApiVersion( ApiVersion apiVersion, IApiVersionWriter? apiVersionWriter = default ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); var services = builder.Services; diff --git a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder.cs b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder.cs index 7e751efe..c72ece92 100644 --- a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder.cs +++ b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder.cs @@ -18,7 +18,7 @@ public class ActionApiVersionConventionBuilder : ActionApiVersionConventionBuild /// The controller builder /// the action builder belongs to. public ActionApiVersionConventionBuilder( ControllerApiVersionConventionBuilder controllerBuilder ) - : base( ( controllerBuilder ?? throw new ArgumentNullException( nameof( controllerBuilder ) ) ).NamingConvention ) + : base( ( controllerBuilder ?? throw new System.ArgumentNullException( nameof( controllerBuilder ) ) ).NamingConvention ) { ControllerBuilder = controllerBuilder; } diff --git a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs index d391caad..0c77a225 100644 --- a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs +++ b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs @@ -40,10 +40,7 @@ public abstract partial class ActionApiVersionConventionBuilderBase : ApiVersion /// protected override void MergeAttributesWithConventions( IReadOnlyList attributes ) { - if ( attributes == null ) - { - throw new ArgumentNullException( nameof( attributes ) ); - } + ArgumentNullException.ThrowIfNull( attributes ); base.MergeAttributesWithConventions( attributes ); diff --git a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder{T}.cs b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder{T}.cs index bc886b83..0531a70a 100644 --- a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder{T}.cs +++ b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilder{T}.cs @@ -31,7 +31,7 @@ public class ActionApiVersionConventionBuilder : /// The controller builder /// the action builder belongs to. public ActionApiVersionConventionBuilder( ControllerApiVersionConventionBuilder controllerBuilder ) - : base( ( controllerBuilder ?? throw new ArgumentNullException( nameof( controllerBuilder ) ) ).NamingConvention ) + : base( ( controllerBuilder ?? throw new System.ArgumentNullException( nameof( controllerBuilder ) ) ).NamingConvention ) { ControllerBuilder = controllerBuilder; } diff --git a/src/Common/src/Common.Mvc/Conventions/ActionConventionBuilderExtensions.cs b/src/Common/src/Common.Mvc/Conventions/ActionConventionBuilderExtensions.cs index c4c82a76..98cb8aa3 100644 --- a/src/Common/src/Common.Mvc/Conventions/ActionConventionBuilderExtensions.cs +++ b/src/Common/src/Common.Mvc/Conventions/ActionConventionBuilderExtensions.cs @@ -35,16 +35,8 @@ public static IActionConventionBuilder Action( where TController : notnull #endif { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - - if ( actionExpression == null ) - { - throw new ArgumentNullException( nameof( actionExpression ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); + ArgumentNullException.ThrowIfNull( actionExpression ); return builder.Action( actionExpression.ExtractMethod() ); } @@ -65,16 +57,8 @@ public static IActionConventionBuilder Action where TController : notnull #endif { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - - if ( actionExpression == null ) - { - throw new ArgumentNullException( nameof( actionExpression ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); + ArgumentNullException.ThrowIfNull( actionExpression ); return builder.Action( actionExpression.ExtractMethod() ); } @@ -91,11 +75,7 @@ public static IActionConventionBuilder Action /// methods that have the applied will also be ignored. public static IActionConventionBuilder Action( this IActionConventionBuilder builder, string methodName, params Type[] argumentTypes ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); var method = ActionMethodResolver.Resolve( builder.ControllerType, methodName, argumentTypes ); return builder.Action( method ); } diff --git a/src/Common/src/Common.Mvc/Conventions/ApiVersionConventionBuilder.cs b/src/Common/src/Common.Mvc/Conventions/ApiVersionConventionBuilder.cs index 1f94e935..ded967a0 100644 --- a/src/Common/src/Common.Mvc/Conventions/ApiVersionConventionBuilder.cs +++ b/src/Common/src/Common.Mvc/Conventions/ApiVersionConventionBuilder.cs @@ -100,10 +100,7 @@ public virtual IControllerConventionBuilder Controller( Type controllerType ) /// public virtual bool ApplyTo( ControllerModel controller ) { - if ( controller == null ) - { - throw new ArgumentNullException( nameof( controller ) ); - } + ArgumentNullException.ThrowIfNull( controller ); IControllerConventionBuilder? builder; bool hasExplicitConventions; diff --git a/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder.cs b/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder.cs index 277f9cd4..fedac8b2 100644 --- a/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder.cs +++ b/src/Common/src/Common.Mvc/Conventions/ControllerApiVersionConventionBuilder.cs @@ -44,7 +44,7 @@ public ControllerApiVersionConventionBuilder( Type controllerType, IControllerNa if ( !webApiController.IsAssignableFrom( controllerType ) ) { var message = string.Format( CultureInfo.CurrentCulture, MvcSR.RequiredInterfaceNotImplemented, controllerType, webApiController ); - throw new ArgumentException( message, nameof( controllerType ) ); + throw new System.ArgumentException( message, nameof( controllerType ) ); } #endif ControllerType = controllerType; diff --git a/src/Common/src/Common.Mvc/Conventions/ControllerConventionBuilderExtensions.cs b/src/Common/src/Common.Mvc/Conventions/ControllerConventionBuilderExtensions.cs index 562fb144..328b8b70 100644 --- a/src/Common/src/Common.Mvc/Conventions/ControllerConventionBuilderExtensions.cs +++ b/src/Common/src/Common.Mvc/Conventions/ControllerConventionBuilderExtensions.cs @@ -35,16 +35,8 @@ public static IActionConventionBuilder Action( where TController : notnull #endif { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - - if ( actionExpression == null ) - { - throw new ArgumentNullException( nameof( actionExpression ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); + ArgumentNullException.ThrowIfNull( actionExpression ); return builder.Action( actionExpression.ExtractMethod() ); } @@ -65,16 +57,8 @@ public static IActionConventionBuilder Action where TController : notnull #endif { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - - if ( actionExpression == null ) - { - throw new ArgumentNullException( nameof( actionExpression ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); + ArgumentNullException.ThrowIfNull( actionExpression ); return builder.Action( actionExpression.ExtractMethod() ); } @@ -91,11 +75,7 @@ public static IActionConventionBuilder Action /// methods that have the applied will also be ignored. public static IActionConventionBuilder Action( this IControllerConventionBuilder builder, string methodName, params Type[] argumentTypes ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); var method = ActionMethodResolver.Resolve( builder.ControllerType, methodName, argumentTypes ); return builder.Action( method ); } diff --git a/src/Common/src/Common.Mvc/Conventions/VersionByNamespaceConvention.cs b/src/Common/src/Common.Mvc/Conventions/VersionByNamespaceConvention.cs index a4103d0c..64f42f90 100644 --- a/src/Common/src/Common.Mvc/Conventions/VersionByNamespaceConvention.cs +++ b/src/Common/src/Common.Mvc/Conventions/VersionByNamespaceConvention.cs @@ -35,15 +35,8 @@ public class VersionByNamespaceConvention : IControllerConvention /// public virtual bool Apply( IControllerConventionBuilder builder, ControllerModel controller ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - - if ( controller == null ) - { - throw new ArgumentNullException( nameof( controller ) ); - } + ArgumentNullException.ThrowIfNull( builder ); + ArgumentNullException.ThrowIfNull( controller ); var type = controller.ControllerType; var versions = parser.Parse( type ); diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs index c35d1db0..f4d65d65 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs @@ -36,7 +36,7 @@ public virtual string Describe( AllowedQueryOptions queryOption, ODataQueryOptio if ( ( queryOption < Filter || queryOption > Supported ) || ( queryOption != Filter && ( (int) queryOption % 2 != 0 ) ) ) { - throw new ArgumentException( ODataExpSR.MultipleQueryOptionsNotAllowed, nameof( queryOption ) ); + throw new System.ArgumentException( ODataExpSR.MultipleQueryOptionsNotAllowed, nameof( queryOption ) ); } return queryOption switch @@ -48,7 +48,7 @@ public virtual string Describe( AllowedQueryOptions queryOption, ODataQueryOptio Top => DescribeTop( context ), Skip => DescribeSkip( context ), Count => DescribeCount( context ), - _ => throw new ArgumentException( + _ => throw new System.ArgumentException( string.Format( CurrentCulture, ODataExpSR.UnsupportedQueryOption, @@ -68,10 +68,7 @@ public virtual string Describe( AllowedQueryOptions queryOption, ODataQueryOptio /// The query option description. protected virtual string DescribeFilter( ODataQueryOptionDescriptionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var description = new StringBuilder(); @@ -104,10 +101,7 @@ protected virtual string DescribeFilter( ODataQueryOptionDescriptionContext cont /// The query option description. protected virtual string DescribeExpand( ODataQueryOptionDescriptionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); bool hasMaxExpansionDepth; @@ -144,10 +138,7 @@ protected virtual string DescribeExpand( ODataQueryOptionDescriptionContext cont /// The query option description. protected virtual string DescribeSelect( ODataQueryOptionDescriptionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); if ( context.AllowedSelectProperties.Count <= 0 ) { @@ -171,10 +162,7 @@ protected virtual string DescribeSelect( ODataQueryOptionDescriptionContext cont /// The query option description. protected virtual string DescribeOrderBy( ODataQueryOptionDescriptionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); bool hasMaxOrderByNodeCount; @@ -211,10 +199,7 @@ protected virtual string DescribeOrderBy( ODataQueryOptionDescriptionContext con /// The query option description. protected virtual string DescribeTop( ODataQueryOptionDescriptionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); if ( context.MaxTop.NoLimitOrNone() ) { @@ -235,10 +220,7 @@ protected virtual string DescribeTop( ODataQueryOptionDescriptionContext context /// The query option description. protected virtual string DescribeSkip( ODataQueryOptionDescriptionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); if ( context.MaxSkip.NoLimitOrNone() ) { diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs index 555fd9bc..fa2f7a07 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs @@ -22,10 +22,7 @@ public sealed partial class ImplicitModelBoundSettingsConvention : IModelConfigu /// public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string? routePrefix ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); if ( types.Count == 0 ) { diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilder.cs index 3a1215b9..1da8b0b5 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilder.cs @@ -61,11 +61,7 @@ public ODataActionQueryOptionsConventionBuilder( ODataControllerQueryOptionsConv /// The original . public virtual ODataActionQueryOptionsConventionBuilder Use( ODataValidationSettings validationSettings ) { - if ( validationSettings == null ) - { - throw new ArgumentNullException( nameof( validationSettings ) ); - } - + ArgumentNullException.ThrowIfNull( validationSettings ); ValidationSettings.CopyFrom( validationSettings ); return this; } @@ -209,10 +205,7 @@ public virtual ODataActionQueryOptionsConventionBuilder AllowFilter( int maxNode /// The original . public virtual ODataActionQueryOptionsConventionBuilder AllowOrderBy( int maxNodeCount, IEnumerable properties ) { - if ( properties == null ) - { - throw new ArgumentNullException( nameof( properties ) ); - } + ArgumentNullException.ThrowIfNull( properties ); ValidationSettings.AllowedQueryOptions |= OrderBy; diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderExtensions.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderExtensions.cs index 78073220..fb997141 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderExtensions.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderExtensions.cs @@ -39,11 +39,7 @@ public static ODataActionQueryOptionsConventionBuilder AllowOrderBy( int maxNodeCount, params string[] properties ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); return builder.AllowOrderBy( maxNodeCount, properties.AsEnumerable() ); } @@ -58,11 +54,7 @@ public static ODataActionQueryOptionsConventionBuilder AllowOrderBy( this ODataActionQueryOptionsConventionBuilder builder, IEnumerable properties ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); return builder.AllowOrderBy( default, properties ); } @@ -77,11 +69,7 @@ public static ODataActionQueryOptionsConventionBuilder AllowOrderBy( this ODataActionQueryOptionsConventionBuilder builder, params string[] properties ) { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); return builder.AllowOrderBy( default, properties.AsEnumerable() ); } @@ -103,11 +91,7 @@ public static ODataActionQueryOptionsConventionBuilder AllowOrderBy( , IHttpController #endif { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); return builder.AllowOrderBy( maxNodeCount, properties.AsEnumerable() ); } @@ -127,11 +111,7 @@ public static ODataActionQueryOptionsConventionBuilder AllowOrderBy( , IHttpController #endif { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); return builder.AllowOrderBy( default, properties ); } @@ -151,11 +131,7 @@ public static ODataActionQueryOptionsConventionBuilder AllowOrderBy( , IHttpController #endif { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } - + ArgumentNullException.ThrowIfNull( builder ); return builder.AllowOrderBy( default, properties.AsEnumerable() ); } @@ -174,16 +150,8 @@ public static ODataActionQueryOptionsConventionBuilder Action ActionThe original . public virtual ODataActionQueryOptionsConventionBuilder Use( ODataValidationSettings validationSettings ) { - if ( validationSettings == null ) - { - throw new ArgumentNullException( nameof( validationSettings ) ); - } - + ArgumentNullException.ThrowIfNull( validationSettings ); ValidationSettings.CopyFrom( validationSettings ); return this; } @@ -214,10 +210,7 @@ public virtual ODataActionQueryOptionsConventionBuilder AllowFilter( int maxN /// The original . public virtual ODataActionQueryOptionsConventionBuilder AllowOrderBy( int maxNodeCount, IEnumerable properties ) { - if ( properties == null ) - { - throw new ArgumentNullException( nameof( properties ) ); - } + ArgumentNullException.ThrowIfNull( properties ); ValidationSettings.AllowedQueryOptions |= OrderBy; diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionsConventionBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionsConventionBuilder.cs index 6a327147..d400bc20 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionsConventionBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionsConventionBuilder.cs @@ -28,7 +28,7 @@ public ODataControllerQueryOptionsConventionBuilder( Type controllerType ) if ( !webApiController.IsAssignableFrom( controllerType ) ) { var message = string.Format( CultureInfo.CurrentCulture, ODataExpSR.RequiredInterfaceNotImplemented, controllerType, webApiController ); - throw new ArgumentException( message ); + throw new System.ArgumentException( message ); } #endif ControllerType = controllerType; diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs index c766317d..72f8c712 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs @@ -34,7 +34,7 @@ public partial class ODataQueryOptionDescriptionContext /// /// The associated API description. public ODataQueryOptionDescriptionContext( ApiDescription apiDescription ) => - ApiDescription = apiDescription ?? throw new ArgumentNullException( nameof( apiDescription ) ); + ApiDescription = apiDescription ?? throw new System.ArgumentNullException( nameof( apiDescription ) ); /// /// Initializes a new instance of the class. @@ -46,12 +46,10 @@ protected internal ODataQueryOptionDescriptionContext( ApiDescription apiDescription, ODataValidationSettings validationSettings ) { - if ( validationSettings == null ) - { - throw new ArgumentNullException( nameof( validationSettings ) ); - } + ArgumentNullException.ThrowIfNull( apiDescription ); + ArgumentNullException.ThrowIfNull( validationSettings ); - ApiDescription = apiDescription ?? throw new ArgumentNullException( nameof( apiDescription ) ); + ApiDescription = apiDescription; AllowedArithmeticOperators = validationSettings.AllowedArithmeticOperators; AllowedFunctions = validationSettings.AllowedFunctions; AllowedLogicalOperators = validationSettings.AllowedLogicalOperators; diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs index 054f5641..cf275e86 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs @@ -89,10 +89,7 @@ public virtual ODataControllerQueryOptionsConventionBuilder Control /// A new or existing . public virtual ODataControllerQueryOptionsConventionBuilder Controller( Type controllerType ) { - if ( controllerType == null ) - { - throw new ArgumentNullException( nameof( controllerType ) ); - } + ArgumentNullException.ThrowIfNull( controllerType ); if ( !ConventionBuilders.TryGetValue( controllerType, out var builder ) ) { @@ -124,10 +121,7 @@ public virtual ODataControllerQueryOptionsConventionBuilder Controller( Type con /// The settings used to apply OData query option conventions. public virtual void ApplyTo( IEnumerable apiDescriptions, ODataQueryOptionSettings queryOptionSettings ) { - if ( apiDescriptions == null ) - { - throw new ArgumentNullException( nameof( apiDescriptions ) ); - } + ArgumentNullException.ThrowIfNull( apiDescriptions ); var controllerConventions = default( Dictionary ); diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs index 04dab8ec..dc4e8c1d 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs @@ -61,10 +61,7 @@ private DefaultModelTypeBuilder( bool excludeAdHocModels, bool adHoc ) /// public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredType, Type clrType, ApiVersion apiVersion ) { - if ( model == null ) - { - throw new ArgumentNullException( nameof( model ) ); - } + ArgumentNullException.ThrowIfNull( model ); if ( model.IsAdHoc() ) { @@ -79,20 +76,9 @@ public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredTyp } } - if ( structuredType == null ) - { - throw new ArgumentNullException( nameof( structuredType ) ); - } - - if ( clrType == null ) - { - throw new ArgumentNullException( nameof( clrType ) ); - } - - if ( apiVersion == null ) - { - throw new ArgumentNullException( nameof( apiVersion ) ); - } + ArgumentNullException.ThrowIfNull( structuredType ); + ArgumentNullException.ThrowIfNull( clrType ); + ArgumentNullException.ThrowIfNull( apiVersion ); generatedEdmTypesPerVersion ??= new(); @@ -104,10 +90,7 @@ public Type NewStructuredType( IEdmModel model, IEdmStructuredType structuredTyp /// public Type NewActionParameters( IEdmModel model, IEdmAction action, string controllerName, ApiVersion apiVersion ) { - if ( model == null ) - { - throw new ArgumentNullException( nameof( model ) ); - } + ArgumentNullException.ThrowIfNull( model ); if ( !adHoc && model.IsAdHoc() ) { @@ -115,20 +98,9 @@ public Type NewActionParameters( IEdmModel model, IEdmAction action, string cont return adHocBuilder.NewActionParameters( model, action, controllerName, apiVersion ); } - if ( action == null ) - { - throw new ArgumentNullException( nameof( action ) ); - } - - if ( string.IsNullOrEmpty( controllerName ) ) - { - throw new ArgumentNullException( nameof( controllerName ) ); - } - - if ( apiVersion == null ) - { - throw new ArgumentNullException( nameof( apiVersion ) ); - } + ArgumentNullException.ThrowIfNull( action ); + ArgumentException.ThrowIfNullOrEmpty( controllerName ); + ArgumentNullException.ThrowIfNull( apiVersion ); generatedActionParamsPerVersion ??= new(); diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs b/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs index a312e307..654b4033 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs @@ -43,15 +43,8 @@ public static partial class TypeExtensions /// provided . public static Type SubstituteIfNecessary( this Type type, TypeSubstitutionContext context ) { - if ( type == null ) - { - throw new ArgumentNullException( nameof( type ) ); - } - - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( type ); + ArgumentNullException.ThrowIfNull( context ); var openTypes = new Stack(); var apiVersion = context.ApiVersion; diff --git a/src/Common/src/Common/ApiVersionReader.cs b/src/Common/src/Common/ApiVersionReader.cs index 276aea06..7762c6f7 100644 --- a/src/Common/src/Common/ApiVersionReader.cs +++ b/src/Common/src/Common/ApiVersionReader.cs @@ -35,10 +35,7 @@ public static IApiVersionReader Combine( IApiVersionReader apiVersionReader, params IApiVersionReader[] otherApiVersionReaders ) { - if ( apiVersionReader == null ) - { - throw new ArgumentNullException( nameof( apiVersionReader ) ); - } + ArgumentNullException.ThrowIfNull( apiVersionReader ); int count; IApiVersionReader[] apiVersionReaders; @@ -69,7 +66,7 @@ public static IApiVersionReader Combine( IEnumerable apiVersi if ( readers is null || readers.Length == 0 ) { - throw new ArgumentException( CommonSR.ZeroApiVersionReaders, nameof( apiVersionReaders ) ); + throw new System.ArgumentException( CommonSR.ZeroApiVersionReaders, nameof( apiVersionReaders ) ); } return new CombinedApiVersionReader( readers ); diff --git a/src/Common/src/Common/ApiVersioningPolicyBuilder.cs b/src/Common/src/Common/ApiVersioningPolicyBuilder.cs index c0894ab6..908a0342 100644 --- a/src/Common/src/Common/ApiVersioningPolicyBuilder.cs +++ b/src/Common/src/Common/ApiVersioningPolicyBuilder.cs @@ -28,7 +28,7 @@ public virtual ISunsetPolicyBuilder Sunset( string? name, ApiVersion? apiVersion if ( string.IsNullOrEmpty( name ) && apiVersion == null ) { var message = string.Format( CultureInfo.CurrentCulture, CommonSR.InvalidPolicyKey, nameof( name ), nameof( apiVersion ) ); - throw new ArgumentException( message ); + throw new System.ArgumentException( message ); } var key = new PolicyKey( name, apiVersion ); diff --git a/src/Common/src/Common/CurrentImplementationApiVersionSelector.cs b/src/Common/src/Common/CurrentImplementationApiVersionSelector.cs index 1fd29702..8cd7510f 100644 --- a/src/Common/src/Common/CurrentImplementationApiVersionSelector.cs +++ b/src/Common/src/Common/CurrentImplementationApiVersionSelector.cs @@ -34,10 +34,7 @@ public class CurrentImplementationApiVersionSelector : IApiVersionSelector /// This method always returns the default API version. public virtual ApiVersion SelectVersion( HttpRequest request, ApiVersionModel model ) { - if ( model == null ) - { - throw new ArgumentNullException( nameof( model ) ); - } + ArgumentNullException.ThrowIfNull( model ); return model.ImplementedApiVersions.Count switch { diff --git a/src/Common/src/Common/DefaultApiVersionReporter.cs b/src/Common/src/Common/DefaultApiVersionReporter.cs index 02237016..95e4d017 100644 --- a/src/Common/src/Common/DefaultApiVersionReporter.cs +++ b/src/Common/src/Common/DefaultApiVersionReporter.cs @@ -42,18 +42,13 @@ public DefaultApiVersionReporter( string deprecatedHeaderName = ApiDeprecatedVersions, ApiVersionMapping mapping = Explicit | Implicit ) { - this.sunsetPolicyManager = sunsetPolicyManager ?? throw new ArgumentNullException( nameof( sunsetPolicyManager ) ); - - if ( string.IsNullOrEmpty( apiSupportedVersionsName = supportedHeaderName ) ) - { - throw new ArgumentNullException( nameof( supportedHeaderName ) ); - } - - if ( string.IsNullOrEmpty( apiDeprecatedVersionsName = deprecatedHeaderName ) ) - { - throw new ArgumentNullException( nameof( deprecatedHeaderName ) ); - } + ArgumentNullException.ThrowIfNull( sunsetPolicyManager ); + ArgumentException.ThrowIfNullOrEmpty( supportedHeaderName ); + ArgumentException.ThrowIfNullOrEmpty( deprecatedHeaderName ); + this.sunsetPolicyManager = sunsetPolicyManager; + apiSupportedVersionsName = supportedHeaderName; + apiDeprecatedVersionsName = deprecatedHeaderName; Mapping = mapping; } @@ -63,15 +58,8 @@ public DefaultApiVersionReporter( /// public void Report( HttpResponse response, ApiVersionModel apiVersionModel ) { - if ( response == null ) - { - throw new ArgumentNullException( nameof( response ) ); - } - - if ( apiVersionModel == null ) - { - throw new ArgumentNullException( nameof( apiVersionModel ) ); - } + ArgumentNullException.ThrowIfNull( response ); + ArgumentNullException.ThrowIfNull( apiVersionModel ); if ( apiVersionModel.IsApiVersionNeutral ) { diff --git a/src/Common/src/Common/HeaderApiVersionReader.cs b/src/Common/src/Common/HeaderApiVersionReader.cs index 8b80ec88..2bffbf31 100644 --- a/src/Common/src/Common/HeaderApiVersionReader.cs +++ b/src/Common/src/Common/HeaderApiVersionReader.cs @@ -22,7 +22,7 @@ public HeaderApiVersionReader() { } /// /// A sequence of HTTP header names to read the API version from. public HeaderApiVersionReader( IEnumerable headerNames ) => - HeaderNames.AddRange( headerNames ?? throw new ArgumentNullException( nameof( headerNames ) ) ); + HeaderNames.AddRange( headerNames ?? throw new System.ArgumentNullException( nameof( headerNames ) ) ); /// /// Initializes a new instance of the class. @@ -31,10 +31,7 @@ public HeaderApiVersionReader( IEnumerable headerNames ) => /// An array of other HTTP header names to read the API version from. public HeaderApiVersionReader( string headerName, params string[] otherHeaderNames ) { - if ( string.IsNullOrEmpty( headerName ) ) - { - throw new ArgumentNullException( headerName ); - } + ArgumentException.ThrowIfNullOrEmpty( headerName ); HeaderNames.Add( headerName ); @@ -65,10 +62,7 @@ public HeaderApiVersionReader( string headerName, params string[] otherHeaderNam /// The context used to add API version parameter descriptions. public virtual void AddParameters( IApiVersionParameterDescriptionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var count = HeaderNames.Count; #if NETFRAMEWORK diff --git a/src/Common/src/Common/LowestImplementedApiVersionSelector.cs b/src/Common/src/Common/LowestImplementedApiVersionSelector.cs index ff39576a..49d58355 100644 --- a/src/Common/src/Common/LowestImplementedApiVersionSelector.cs +++ b/src/Common/src/Common/LowestImplementedApiVersionSelector.cs @@ -34,10 +34,7 @@ public class LowestImplementedApiVersionSelector : IApiVersionSelector /// This method always returns the default API version. public virtual ApiVersion SelectVersion( HttpRequest request, ApiVersionModel model ) { - if ( model == null ) - { - throw new ArgumentNullException( nameof( model ) ); - } + ArgumentNullException.ThrowIfNull( model ); return model.ImplementedApiVersions.Count switch { diff --git a/src/Common/src/Common/MediaTypeApiVersionReader.cs b/src/Common/src/Common/MediaTypeApiVersionReader.cs index ecd7dacd..4f01f4f0 100644 --- a/src/Common/src/Common/MediaTypeApiVersionReader.cs +++ b/src/Common/src/Common/MediaTypeApiVersionReader.cs @@ -28,11 +28,7 @@ public partial class MediaTypeApiVersionReader : IApiVersionReader /// The name of the media type parameter to read the API version from. public MediaTypeApiVersionReader( string parameterName ) { - if ( string.IsNullOrEmpty( parameterName ) ) - { - throw new ArgumentNullException( parameterName ); - } - + ArgumentException.ThrowIfNullOrEmpty( parameterName ); ParameterName = parameterName; } @@ -53,10 +49,7 @@ public MediaTypeApiVersionReader( string parameterName ) /// quality parameter. protected virtual string? ReadAcceptHeader( ICollection accept ) { - if ( accept == null ) - { - throw new ArgumentNullException( nameof( accept ) ); - } + ArgumentNullException.ThrowIfNull( accept ); var count = accept.Count; @@ -103,10 +96,7 @@ public MediaTypeApiVersionReader( string parameterName ) /// The API version read or null. protected virtual string? ReadContentTypeHeader( MediaTypeHeaderValue contentType ) { - if ( contentType == null ) - { - throw new ArgumentNullException( nameof( contentType ) ); - } + ArgumentNullException.ThrowIfNull( contentType ); #if NETFRAMEWORK var parameters = contentType.Parameters.ToArray(); var count = parameters.Length; @@ -137,11 +127,7 @@ public MediaTypeApiVersionReader( string parameterName ) /// The context used to add API version parameter descriptions. public virtual void AddParameters( IApiVersionParameterDescriptionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } - + ArgumentNullException.ThrowIfNull( context ); context.AddParameter( ParameterName, MediaTypeParameter ); } diff --git a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs index d1d34217..aebee932 100644 --- a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs +++ b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs @@ -146,10 +146,7 @@ public virtual IApiVersionReader Build() => #endif protected void AddReader( Func, IReadOnlyList> reader ) { - if ( reader is null ) - { - throw new ArgumentNullException( nameof( reader ) ); - } + ArgumentNullException.ThrowIfNull( reader ); readers ??= new(); readers.Add( reader ); diff --git a/src/Common/src/Common/MediaTypeApiVersionReaderBuilderExtensions.cs b/src/Common/src/Common/MediaTypeApiVersionReaderBuilderExtensions.cs index c91073b0..82155280 100644 --- a/src/Common/src/Common/MediaTypeApiVersionReaderBuilderExtensions.cs +++ b/src/Common/src/Common/MediaTypeApiVersionReaderBuilderExtensions.cs @@ -17,10 +17,7 @@ public static class MediaTypeApiVersionReaderBuilderExtensions /// is null. public static T SelectFirstOrDefault( this T builder ) where T : MediaTypeApiVersionReaderBuilder { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); builder.Select( static ( request, versions ) => versions.Count == 0 ? versions : new[] { versions[0] } ); return builder; @@ -36,10 +33,7 @@ public static T SelectFirstOrDefault( this T builder ) where T : MediaTypeApi /// is null. public static T SelectLastOrDefault( this T builder ) where T : MediaTypeApiVersionReaderBuilder { - if ( builder == null ) - { - throw new ArgumentNullException( nameof( builder ) ); - } + ArgumentNullException.ThrowIfNull( builder ); builder.Select( static ( request, versions ) => versions.Count == 0 ? versions : new[] { versions[versions.Count - 1] } ); return builder; diff --git a/src/Common/src/Common/QueryStringApiVersionReader.cs b/src/Common/src/Common/QueryStringApiVersionReader.cs index a32bde4c..942e0016 100644 --- a/src/Common/src/Common/QueryStringApiVersionReader.cs +++ b/src/Common/src/Common/QueryStringApiVersionReader.cs @@ -28,7 +28,9 @@ public partial class QueryStringApiVersionReader : IApiVersionReader /// This constructor adds the "api-version" query string parameter if no other query parameter names are specified. public QueryStringApiVersionReader( IEnumerable parameterNames ) { - ParameterNames.AddRange( parameterNames ?? throw new ArgumentNullException( nameof( parameterNames ) ) ); + ArgumentNullException.ThrowIfNull( parameterNames ); + + ParameterNames.AddRange( parameterNames ); if ( ParameterNames.Count == 0 ) { @@ -43,10 +45,7 @@ public QueryStringApiVersionReader( IEnumerable parameterNames ) /// An array of query string parameter names to read the API version from. public QueryStringApiVersionReader( string parameterName, params string[] otherParameterNames ) { - if ( string.IsNullOrEmpty( parameterName ) ) - { - throw new ArgumentNullException( parameterName ); - } + ArgumentException.ThrowIfNullOrEmpty( parameterName ); ParameterNames.Add( parameterName ); @@ -77,10 +76,7 @@ public QueryStringApiVersionReader( string parameterName, params string[] otherP /// The context used to add API version parameter descriptions. public virtual void AddParameters( IApiVersionParameterDescriptionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); var count = ParameterNames.Count; #if NETFRAMEWORK diff --git a/src/Common/src/Common/SunsetPolicyBuilder.cs b/src/Common/src/Common/SunsetPolicyBuilder.cs index 13d4aaf2..e0d207d7 100644 --- a/src/Common/src/Common/SunsetPolicyBuilder.cs +++ b/src/Common/src/Common/SunsetPolicyBuilder.cs @@ -24,7 +24,7 @@ public SunsetPolicyBuilder( string? name, ApiVersion? apiVersion ) if ( string.IsNullOrEmpty( name ) && apiVersion == null ) { var message = string.Format( CultureInfo.CurrentCulture, CommonSR.InvalidPolicyKey, nameof( name ), nameof( apiVersion ) ); - throw new ArgumentException( message ); + throw new System.ArgumentException( message ); } Name = name; @@ -39,7 +39,7 @@ public SunsetPolicyBuilder( string? name, ApiVersion? apiVersion ) /// public virtual void Per( SunsetPolicy policy ) => - sunsetPolicy = policy ?? throw new ArgumentNullException( nameof( policy ) ); + sunsetPolicy = policy ?? throw new System.ArgumentNullException( nameof( policy ) ); /// public virtual ISunsetPolicyBuilder Effective( DateTimeOffset sunsetDate ) diff --git a/src/Common/src/Common/UrlSegmentApiVersionReader.cs b/src/Common/src/Common/UrlSegmentApiVersionReader.cs index 93d09853..234a41b1 100644 --- a/src/Common/src/Common/UrlSegmentApiVersionReader.cs +++ b/src/Common/src/Common/UrlSegmentApiVersionReader.cs @@ -22,11 +22,7 @@ public UrlSegmentApiVersionReader() { } /// The context used to add API version parameter descriptions. public virtual void AddParameters( IApiVersionParameterDescriptionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } - + ArgumentNullException.ThrowIfNull( context ); context.AddParameter( name: string.Empty, Path ); } } \ No newline at end of file diff --git a/src/Common/test/Common.OData.Tests/TestModelConfiguration.cs b/src/Common/test/Common.OData.Tests/TestModelConfiguration.cs index f176b7fc..b4e48c09 100644 --- a/src/Common/test/Common.OData.Tests/TestModelConfiguration.cs +++ b/src/Common/test/Common.OData.Tests/TestModelConfiguration.cs @@ -13,10 +13,14 @@ public class TestModelConfiguration : IModelConfiguration { public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { - if ( builder == null ) +#if NETFRAMEWORK + if ( builder is null ) { throw new ArgumentNullException( nameof( builder ) ); } +#else + ArgumentNullException.ThrowIfNull( builder ); +#endif var tests = builder.EntitySet( "Tests" ).EntityType; var neutralTests = builder.EntitySet( "NeutralTests" ).EntityType; From c0b91ca8471415cfaec3986c43943f163b16f57d Mon Sep 17 00:00:00 2001 From: Chris Martinez Date: Mon, 4 Dec 2023 20:01:43 -0800 Subject: [PATCH 070/131] Use collection initializers and spreads everywhere --- .../AmbiguousApiVersionException.cs | 6 +- .../ApiVersionConventionBuilderBase.cs | 16 +-- .../LinkHeaderValue.cs | 8 +- .../NamespaceParser.cs | 2 +- ...ApiVersionParameterSourceExtensionsTest.cs | 3 +- .../ApiExplorer/AdHocEdmScope.cs | 4 +- .../ApiExplorer/ODataApiExplorer.cs | 2 +- .../Routing/ODataRouteBuilderContext.cs | 2 +- .../OData/EdmModelSelector.cs | 4 +- .../OData/VersionedODataModelBuilder.cs | 4 +- .../ODataValidationSettingsConventionTest.cs | 5 +- .../Description/ApiDescriptionGroup.cs | 2 +- .../Description/VersionedApiDescription.cs | 2 +- .../Controllers/ActionSelectorCacheItem.cs | 16 +-- .../ApiVersionControllerSelector.cs | 2 +- .../Dispatcher/ControllerSelectionContext.cs | 2 +- .../HeaderApiVersionReader.cs | 2 +- .../MediaTypeApiVersionReader.cs | 10 +- .../QueryStringApiVersionReader.cs | 2 +- .../HttpRouteDataExtensions.cs | 2 +- .../System.Web.Http/HttpRouteExtensions.cs | 2 +- ...iVersionParameterDescriptionContextTest.cs | 8 +- .../ActionApiVersionConventionBuilderTTest.cs | 5 +- .../ActionApiVersionConventionBuilderTest.cs | 5 +- ...trollerApiVersionConventionBuilderTTest.cs | 4 +- ...ntrollerApiVersionConventionBuilderTest.cs | 4 +- .../HttpRouteCollectionExtensionsTest.cs | 4 +- .../FilteredControllerTypes.cs | 2 +- .../PartialODataDescriptionProvider.cs | 8 +- .../VersionedMetadataController.cs | 7 +- .../OData/Batch/ODataBatchPathMapping.cs | 2 +- .../OData/ModelConfigurationFeature.cs | 2 +- .../OData/ODataApplicationModelProvider.cs | 14 +-- .../Routing/DefaultMetadataMatcherPolicy.cs | 4 +- .../ODataValidationSettingsConventionTest.cs | 4 +- .../DefaultMetadataMatcherPolicyTest.cs | 2 +- .../ApiVersionMetadataCollationCollection.cs | 4 +- .../ApiVersionMetadataCollationContext.cs | 2 +- .../ApiVersioningFeature.cs | 6 +- .../Builder/EndpointBuilderFinalizer.cs | 24 ++-- .../Asp.Versioning.Http/ErrorObjectWriter.cs | 4 +- .../HeaderApiVersionReader.cs | 2 +- .../MediaTypeApiVersionReader.cs | 10 +- .../QueryStringApiVersionReader.cs | 2 +- .../Routing/AmbiguousApiVersionEndpoint.cs | 2 +- .../Routing/ApiVersionMatcherPolicy.cs | 10 +- .../Routing/ClientErrorEndpointBuilder.cs | 2 +- .../Routing/EdgeBuilder.cs | 2 +- .../VersionedApiDescriptionProvider.cs | 26 +++-- .../ActionDescriptorExtensions.cs | 4 +- .../Asp.Versioning.Mvc/ApiVersionCollator.cs | 2 +- .../ActionApiVersionConventionBuilderBase.cs | 4 +- .../IApiVersioningBuilderExtensions.cs | 4 +- .../TestEndpointDataSource.cs | 104 +++++++++--------- .../ApiVersionModelBinderTest.cs | 3 +- .../HttpResponseMessageExtensions.cs | 2 +- .../net#.0/ILoggerExtensions.cs | 2 +- .../ActionApiVersionConventionBuilderBase.cs | 2 +- ...onApiVersionConventionBuilderCollection.cs | 2 +- ...piVersionConventionBuilderCollection{T}.cs | 2 +- .../ApiVersionConventionBuilder.cs | 4 +- .../ImplicitModelBoundSettingsConvention.cs | 9 +- ...QueryOptionsConventionBuilderCollection.cs | 2 +- ...ryOptionsConventionBuilderCollection{T}.cs | 2 +- .../Conventions/ODataAttributeVisitor.cs | 3 +- .../ODataQueryOptionDescriptionContext.cs | 10 +- .../ODataQueryOptionsConventionBuilder.cs | 6 +- .../OData/TypeExtensions.cs | 8 +- .../OData/VersionedODataModelBuilder.cs | 4 +- src/Common/src/Common/ApiVersionReader.cs | 2 +- .../src/Common/ApiVersioningPolicyBuilder.cs | 2 +- .../MediaTypeApiVersionReaderBuilder.cs | 15 ++- ...iaTypeApiVersionReaderBuilderExtensions.cs | 6 +- src/Common/src/Common/SunsetLinkBuilder.cs | 2 +- 74 files changed, 235 insertions(+), 240 deletions(-) diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/AmbiguousApiVersionException.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/AmbiguousApiVersionException.cs index 24577d7a..4a780ad5 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/AmbiguousApiVersionException.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/AmbiguousApiVersionException.cs @@ -12,14 +12,14 @@ public partial class AmbiguousApiVersionException : Exception /// /// Initializes a new instance of the class. /// - public AmbiguousApiVersionException() => apiVersions = Array.Empty(); + public AmbiguousApiVersionException() => apiVersions = []; /// /// Initializes a new instance of the class. /// /// The associated error message. public AmbiguousApiVersionException( string message ) - : base( message ) => apiVersions = Array.Empty(); + : base( message ) => apiVersions = []; /// /// Initializes a new instance of the class. @@ -27,7 +27,7 @@ public AmbiguousApiVersionException( string message ) /// The associated error message. /// The inner exception that caused the current exception, if any. public AmbiguousApiVersionException( string message, Exception innerException ) - : base( message, innerException ) => apiVersions = Array.Empty(); + : base( message, innerException ) => apiVersions = []; /// /// Initializes a new instance of the class. diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Conventions/ApiVersionConventionBuilderBase.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/Conventions/ApiVersionConventionBuilderBase.cs index c3a185e1..4ebe62bd 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/Conventions/ApiVersionConventionBuilderBase.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Conventions/ApiVersionConventionBuilderBase.cs @@ -39,25 +39,25 @@ protected ApiVersionConventionBuilderBase() { } /// Gets the collection of API versions supported by the current controller. /// /// A collection of supported API versions. - protected ICollection SupportedVersions => supported ??= new(); + protected ICollection SupportedVersions => supported ??= []; /// /// Gets the collection of API versions deprecated by the current controller. /// /// A collection of deprecated API versions. - protected ICollection DeprecatedVersions => deprecated ??= new(); + protected ICollection DeprecatedVersions => deprecated ??= []; /// /// Gets the collection of API versions advertised by the current controller. /// /// A collection of advertised API versions. - protected ICollection AdvertisedVersions => advertised ??= new(); + protected ICollection AdvertisedVersions => advertised ??= []; /// /// Gets the collection of API versions advertised and deprecated by the current controller. /// /// A collection of advertised and deprecated API versions. - protected ICollection DeprecatedAdvertisedVersions => deprecatedAdvertised ??= new(); + protected ICollection DeprecatedAdvertisedVersions => deprecatedAdvertised ??= []; /// /// Merges API version information from the specified attributes with the current conventions. @@ -99,19 +99,19 @@ protected virtual void MergeAttributesWithConventions( IReadOnlyList att switch ( provider.Options ) { case None: - target = newSupported ??= new(); + target = newSupported ??= []; source = provider.Versions; break; case Deprecated: - target = newDeprecated ??= new(); + target = newDeprecated ??= []; source = provider.Versions; break; case Advertised: - target = newAdvertised ??= new(); + target = newAdvertised ??= []; source = provider.Versions; break; case DeprecatedAdvertised: - target = newDeprecatedAdvertised ??= new(); + target = newDeprecatedAdvertised ??= []; source = provider.Versions; break; default: diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs index 1fe6dbfb..44e0e095 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs @@ -85,7 +85,7 @@ public StringSegment Language #endif if ( languages is null ) { - languages = new() { value }; + languages = [value]; } else if ( languages.Count == 0 ) { @@ -105,7 +105,7 @@ public StringSegment Language /// This is only a hint; for example, it does not override the Content-Language header field of /// a HTTP response obtained by actually following the link. A single link may indicate that multiple /// languages are available from the indicated resource. - public IList Languages => languages ??= new(); + public IList Languages => languages ??= []; /// /// Gets or sets the link media. @@ -206,11 +206,11 @@ public static bool TryParse( type = attribute.Value; break; case "hreflang": - languages ??= new(); + languages ??= []; languages.Add( attribute.Value ); break; default: - extensions ??= new(); + extensions ??= []; extensions.Add( attribute ); break; } diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/NamespaceParser.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/NamespaceParser.cs index 86de27bd..a4246749 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/NamespaceParser.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/NamespaceParser.cs @@ -92,7 +92,7 @@ public IReadOnlyList Parse( Type type ) } else if ( versions is null ) { - versions = new() { version, result }; + versions = [version, result]; } else { diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/IApiVersionParameterSourceExtensionsTest.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/IApiVersionParameterSourceExtensionsTest.cs index b1186e85..486977b0 100644 --- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/IApiVersionParameterSourceExtensionsTest.cs +++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/IApiVersionParameterSourceExtensionsTest.cs @@ -146,6 +146,7 @@ public void get_parameter_names_should_return_matching_names() { // arrange var source = new Mock(); + var expected = new[] { "api-version", "ver" }; source.Setup( s => s.AddParameters( It.IsAny() ) ) .Callback( ( IApiVersionParameterDescriptionContext context ) => @@ -160,6 +161,6 @@ public void get_parameter_names_should_return_matching_names() var names = source.Object.GetParameterNames( Query ); // assert - names.Should().BeEquivalentTo( new[] { "api-version", "ver" } ); + names.Should().BeEquivalentTo( expected ); } } \ No newline at end of file diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/AdHocEdmScope.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/AdHocEdmScope.cs index 2bcf1549..f6744216 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/AdHocEdmScope.cs +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/AdHocEdmScope.cs @@ -64,7 +64,7 @@ private static IReadOnlyList FilterResults( continue; } - results ??= new(); + results ??= []; results.Add( apiDescription ); for ( var j = 0; j < conventions.Count; j++ ) @@ -73,7 +73,7 @@ private static IReadOnlyList FilterResults( } } - return results?.ToArray() ?? Array.Empty(); + return results?.ToArray() ?? []; } private static void ApplyAdHocEdm( diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs index e1163def..bb3eed90 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/ApiExplorer/ODataApiExplorer.cs @@ -181,7 +181,7 @@ protected override Collection ExploreRouteControllers( return apiDescriptions; } - apiDescriptions = new(); + apiDescriptions = []; var modelSelector = Configuration.GetODataRootContainer( route ).GetRequiredService(); var edmModel = modelSelector.SelectModel( apiVersion ); diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Routing/ODataRouteBuilderContext.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Routing/ODataRouteBuilderContext.cs index 23fe0a48..3ff17b36 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Routing/ODataRouteBuilderContext.cs +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Routing/ODataRouteBuilderContext.cs @@ -355,7 +355,7 @@ private static bool IsNavigationPropertyLink( IEdmEntitySet? entitySet, IEdmSing { if ( propertyNames is null ) { - propertyNames = new(); + propertyNames = []; } else { diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs index f8f7e17b..ed7497b9 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/EdmModelSelector.cs @@ -53,8 +53,8 @@ public EdmModelSelector( IEnumerable models, IApiVersionSelector apiV break; default: - versions = new(); - collection = new(); + versions = []; + collection = []; foreach ( var model in models ) { diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/VersionedODataModelBuilder.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/VersionedODataModelBuilder.cs index 7f493939..f08a3c18 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/VersionedODataModelBuilder.cs +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData/OData/VersionedODataModelBuilder.cs @@ -78,7 +78,7 @@ protected virtual IReadOnlyList GetApiVersions() if ( versions.Count > 0 && supported == null ) { - supported = new(); + supported = []; } for ( var j = 0; j < versions.Count; j++ ) @@ -90,7 +90,7 @@ protected virtual IReadOnlyList GetApiVersions() if ( versions.Count > 0 && deprecated == null ) { - deprecated = new(); + deprecated = []; } for ( var j = 0; j < versions.Count; j++ ) diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs index b31d7496..9d3960fd 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs @@ -10,7 +10,6 @@ namespace Asp.Versioning.Conventions; using Microsoft.AspNet.OData.Extensions; using Microsoft.AspNet.OData.Query; using Microsoft.OData.Edm; -using System.Collections.ObjectModel; using System.Net.Http; using System.Reflection; using System.Web.Http; @@ -565,8 +564,8 @@ public static IEnumerable EnableQueryAttributeData var controller = new Mock() { CallBase = true }; var action = new Mock() { CallBase = true }; - controller.Setup( m => m.GetCustomAttributes( It.IsAny() ) ).Returns( new Collection() ); - action.Setup( m => m.GetCustomAttributes( It.IsAny() ) ).Returns( new Collection() ); + controller.Setup( m => m.GetCustomAttributes( It.IsAny() ) ).Returns( [] ); + action.Setup( m => m.GetCustomAttributes( It.IsAny() ) ).Returns( [] ); var actionDescriptor = action.Object; var responseType = singleResult ? typeof( object ) : typeof( IEnumerable ); diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiDescriptionGroup.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiDescriptionGroup.cs index 596bd634..469c44f5 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiDescriptionGroup.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiDescriptionGroup.cs @@ -56,5 +56,5 @@ public ApiDescriptionGroup( ApiVersion apiVersion, string name ) /// /// A collection of /// versioned API descriptions. - public virtual Collection ApiDescriptions { get; } = new(); + public virtual Collection ApiDescriptions { get; } = []; } \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/VersionedApiDescription.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/VersionedApiDescription.cs index e5ab4a68..da07e7d1 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/VersionedApiDescription.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/VersionedApiDescription.cs @@ -69,7 +69,7 @@ public ApiVersion ApiVersion /// /// A collection of arbitrary metadata properties /// associated with the API description. - public IDictionary Properties => properties ??= new(); + public IDictionary Properties => properties ??= []; private static Action CreateSetResponseDescriptionMutator() { diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs index bdcfe0de..a56c19c1 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs @@ -26,7 +26,7 @@ internal sealed class ActionSelectorCacheItem private static readonly HttpMethod[] cacheListMethodKinds = new[] { HttpMethod.Get, HttpMethod.Put, HttpMethod.Post }; private readonly HttpControllerDescriptor controllerDescriptor; private readonly CandidateAction[] combinedCandidateActions; - private readonly Dictionary actionParameterNames = new(); + private readonly Dictionary actionParameterNames = []; private readonly ILookup combinedActionNameMapping; private StandardActionSelectionCache? standardActions; @@ -76,7 +76,7 @@ private void InitializeStandardActions() if ( controllerDescriptor.IsAttributeRouted() ) { - selectionCache.StandardCandidateActions = Array.Empty(); + selectionCache.StandardCandidateActions = []; } else { @@ -94,7 +94,7 @@ private void InitializeStandardActions() } } - selectionCache.StandardCandidateActions = standardCandidateActions.ToArray(); + selectionCache.StandardCandidateActions = [.. standardCandidateActions]; } selectionCache.StandardActionNameMapping = @@ -118,11 +118,7 @@ private void InitializeStandardActions() HttpControllerContext controllerContext, Func, HttpActionDescriptor?> selector ) { - if ( selector == null ) - { - throw new ArgumentNullException( nameof( selector ) ); - } - + ArgumentNullException.ThrowIfNull( selector ); InitializeStandardActions(); var firstAttempt = FindAction( controllerContext, selector, ignoreSubRoutes: false ); @@ -412,7 +408,7 @@ private List FindActionMatchRequiredRouteAndQueryPara } private List FindActionMatchMostRouteAndQueryParameters( List candidatesFound ) => - candidatesFound.Count < 2 ? candidatesFound : candidatesFound.GroupBy( c => actionParameterNames[c.ActionDescriptor].Length ).OrderByDescending( g => g.Key ).First().ToList(); + candidatesFound.Count < 2 ? candidatesFound : [.. candidatesFound.GroupBy( c => actionParameterNames[c.ActionDescriptor].Length ).OrderByDescending( g => g.Key ).First()]; private static CandidateActionWithParams[] GetCandidateActionsWithBindings( HttpControllerContext controllerContext, CandidateAction[] candidatesFound ) { @@ -485,7 +481,7 @@ private static CandidateAction[] FindActionsForMethod( HttpMethod method, Candid { var listCandidates = new List(); FindActionsForMethod( method, candidates, listCandidates ); - return listCandidates.ToArray(); + return [.. listCandidates]; } private static void FindActionsForMethod( HttpMethod method, CandidateAction[] candidates, List listCandidates ) diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs index 80a76858..c889b994 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ApiVersionControllerSelector.cs @@ -258,7 +258,7 @@ private static HttpControllerDescriptor[] ApplyCollatedModels( CollateControllerModels( controllerModels, visitedControllers, CollateActionModels( actionModels, visitedActions ) ); ApplyCollatedModelsToActions( configuration, visitedActions ); - return controllers.ToArray(); + return [.. controllers]; } private static void CollateControllerVersions( diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ControllerSelectionContext.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ControllerSelectionContext.cs index 5c8003c1..729390ce 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ControllerSelectionContext.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Dispatcher/ControllerSelectionContext.cs @@ -70,7 +70,7 @@ internal ApiVersion? RequestedVersion foreach ( var action in actions ) { - candidates ??= new(); + candidates ??= []; candidates.Add( new( action ) ); } } diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/HeaderApiVersionReader.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/HeaderApiVersionReader.cs index a3294be2..58b12d19 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/HeaderApiVersionReader.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/HeaderApiVersionReader.cs @@ -65,7 +65,7 @@ public virtual IReadOnlyList Read( HttpRequestMessage request ) if ( versions == null ) { - return version == null ? Array.Empty() : new[] { version }; + return version == null ? [] : [version]; } return versions.ToArray(); diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReader.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReader.cs index 30bd1303..d3a96174 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReader.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReader.cs @@ -18,18 +18,18 @@ public virtual IReadOnlyList Read( HttpRequestMessage request ) if ( accept is null || ReadAcceptHeader( accept ) is not string otherVersion ) { - return version is null ? Array.Empty() : new[] { version }; + return version is null ? [] : [version]; } var comparer = StringComparer.OrdinalIgnoreCase; if ( version is null || comparer.Equals( version, otherVersion ) ) { - return new[] { otherVersion }; + return [otherVersion]; } - return comparer.Compare( version, otherVersion ) <= 0 ? - new[] { version, otherVersion } : - new[] { otherVersion, version }; + return comparer.Compare( version, otherVersion ) <= 0 + ? [version, otherVersion] + : [otherVersion, version]; } } \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/QueryStringApiVersionReader.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/QueryStringApiVersionReader.cs index a4d4b148..8b88062b 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/QueryStringApiVersionReader.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/QueryStringApiVersionReader.cs @@ -59,7 +59,7 @@ public virtual IReadOnlyList Read( HttpRequestMessage request ) if ( versions == null ) { - return version == null ? Array.Empty() : new[] { version }; + return version == null ? [] : [version]; } return versions.ToArray(); diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteDataExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteDataExtensions.cs index 20d6990e..314c9bc9 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteDataExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteDataExtensions.cs @@ -33,6 +33,6 @@ internal static class HttpRouteDataExtensions } } - return list.ToArray(); + return [.. list]; } } \ No newline at end of file diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteExtensions.cs index 29dbeb03..9971270b 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteExtensions.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/System.Web.Http/HttpRouteExtensions.cs @@ -49,6 +49,6 @@ internal static class HttpRouteExtensions candidates.Add( new CandidateAction( directRouteActions[i], order, precedence ) ); } - return candidates.ToArray(); + return [.. candidates]; } } \ No newline at end of file diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiVersionParameterDescriptionContextTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiVersionParameterDescriptionContextTest.cs index da8e4bf6..d2fb7be2 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiVersionParameterDescriptionContextTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiVersionParameterDescriptionContextTest.cs @@ -4,13 +4,11 @@ namespace Asp.Versioning.Description; using Asp.Versioning.ApiExplorer; using Asp.Versioning.Routing; -using System.Collections.ObjectModel; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Description; -using System.Web.Http.Filters; using System.Web.Http.Routing; using static Asp.Versioning.ApiVersionParameterLocation; using static System.Web.Http.Description.ApiParameterSource; @@ -299,9 +297,9 @@ private static HttpActionDescriptor NewActionDescriptor() var action = new Mock() { CallBase = true }.Object; var controller = new Mock() { CallBase = true }; - controller.Setup( c => c.GetCustomAttributes( It.IsAny() ) ).Returns( new Collection() ); - controller.Setup( c => c.GetCustomAttributes( It.IsAny() ) ).Returns( new Collection() ); - controller.Setup( c => c.GetFilters() ).Returns( new Collection() ); + controller.Setup( c => c.GetCustomAttributes( It.IsAny() ) ).Returns( [] ); + controller.Setup( c => c.GetCustomAttributes( It.IsAny() ) ).Returns( [] ); + controller.Setup( c => c.GetFilters() ).Returns( [] ); action.ControllerDescriptor = controller.Object; return action; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs index d53ccda7..f99b03cf 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTTest.cs @@ -2,7 +2,6 @@ namespace Asp.Versioning.Conventions; -using System.Collections.ObjectModel; using System.Web.Http; using System.Web.Http.Controllers; using static Asp.Versioning.ApiVersionMapping; @@ -17,7 +16,7 @@ public void apply_to_should_assign_empty_model_without_api_versions_from_mapped_ var actionBuilder = new ActionApiVersionConventionBuilder( controllerBuilder ); var actionDescriptor = new Mock() { CallBase = true }; - actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( new Collection() ); + actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( [] ); actionDescriptor.Object.ControllerDescriptor = new(); // act @@ -43,7 +42,7 @@ public void apply_to_should_assign_model_with_declared_api_versions_from_mapped_ var actionBuilder = new ActionApiVersionConventionBuilder( controllerBuilder ); var actionDescriptor = new Mock() { CallBase = true }; - actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( new Collection() ); + actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( [] ); actionDescriptor.Object.ControllerDescriptor = new(); actionBuilder.MapToApiVersion( new ApiVersion( 2, 0 ) ); diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs index 12a5f6ee..e7626764 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ActionApiVersionConventionBuilderTest.cs @@ -2,7 +2,6 @@ namespace Asp.Versioning.Conventions; -using System.Collections.ObjectModel; using System.Web.Http; using System.Web.Http.Controllers; using static Asp.Versioning.ApiVersionMapping; @@ -17,7 +16,7 @@ public void apply_to_should_assign_empty_model_without_api_versions_from_mapped_ var actionBuilder = new ActionApiVersionConventionBuilder( controllerBuilder ); var actionDescriptor = new Mock() { CallBase = true }; - actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( new Collection() ); + actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( [] ); actionDescriptor.Object.ControllerDescriptor = new(); // act @@ -43,7 +42,7 @@ public void apply_to_should_assign_model_with_declared_api_versions_from_mapped_ var actionBuilder = new ActionApiVersionConventionBuilder( controllerBuilder ); var actionDescriptor = new Mock() { CallBase = true }; - actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( new Collection() ); + actionDescriptor.Setup( ad => ad.GetCustomAttributes() ).Returns( [] ); actionDescriptor.Object.ControllerDescriptor = new(); actionBuilder.MapToApiVersion( new ApiVersion( 2, 0 ) ); diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs index e2fc1c6c..82164eb5 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs @@ -19,7 +19,7 @@ public void apply_to_should_assign_conventions_to_controller() var controllerDescriptor = mock.Object; var controllerBuilder = default( IControllerConventionBuilder ); - mock.Setup( cd => cd.GetCustomAttributes() ).Returns( new Collection() ); + mock.Setup( cd => cd.GetCustomAttributes() ).Returns( [] ); controllerDescriptor.Configuration = configuration; controllerDescriptor.ControllerType = typeof( UndecoratedController ); configuration.AddApiVersioning( o => controllerBuilder = o.Conventions.Controller() ); @@ -54,7 +54,7 @@ public void apply_to_should_assign_empty_conventions_to_api_version_neutral_cont var controllerDescriptor = mock.Object; var controllerBuilder = default( IControllerConventionBuilder ); - mock.Setup( cd => cd.GetCustomAttributes() ).Returns( new Collection() ); + mock.Setup( cd => cd.GetCustomAttributes() ).Returns( [] ); controllerDescriptor.Configuration = configuration; controllerDescriptor.ControllerType = typeof( UndecoratedController ); configuration.AddApiVersioning( o => controllerBuilder = o.Conventions.Controller() ); diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs index 6990e951..eedc943c 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs @@ -20,7 +20,7 @@ public void apply_to_should_assign_conventions_to_controller() var controllerDescriptor = mock.Object; var controllerBuilder = default( IControllerConventionBuilder ); - mock.Setup( cd => cd.GetCustomAttributes() ).Returns( new Collection() ); + mock.Setup( cd => cd.GetCustomAttributes() ).Returns( [] ); controllerDescriptor.Configuration = configuration; controllerDescriptor.ControllerType = typeof( UndecoratedController ); configuration.AddApiVersioning( o => controllerBuilder = o.Conventions.Controller( typeof( UndecoratedController ) ) ); @@ -55,7 +55,7 @@ public void apply_to_should_assign_empty_conventions_to_api_version_neutral_cont var controllerDescriptor = mock.Object; var controllerBuilder = default( IControllerConventionBuilder ); - mock.Setup( cd => cd.GetCustomAttributes() ).Returns( new Collection() ); + mock.Setup( cd => cd.GetCustomAttributes() ).Returns( [] ); controllerDescriptor.Configuration = configuration; controllerDescriptor.ControllerType = typeof( UndecoratedController ); configuration.AddApiVersioning( o => controllerBuilder = o.Conventions.Controller( typeof( UndecoratedController ) ) ); diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpRouteCollectionExtensionsTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpRouteCollectionExtensionsTest.cs index 8d270992..eb271b38 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpRouteCollectionExtensionsTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/System.Web.Http/HttpRouteCollectionExtensionsTest.cs @@ -56,7 +56,7 @@ namespace System.Web.Http.WebHost.Routing internal sealed class HostedHttpRouteCollection : HttpRouteCollection { #pragma warning disable SA1309 // Field names should not begin with underscore - private readonly RouteCollection _routeCollection = new(); + private readonly RouteCollection _routeCollection = []; #pragma warning restore SA1309 // Field names should not begin with underscore public override string VirtualPathRoot => throw NotUsedInUnitTest(); @@ -131,7 +131,7 @@ namespace System.Web.Http.WebHost.Routing internal sealed class HttpWebRoute : Route { public HttpWebRoute( IHttpRoute httpRoute ) - : base( httpRoute.RouteTemplate, new(), new(), new(), Mock.Of() ) => HttpRoute = httpRoute; + : base( httpRoute.RouteTemplate, [], [], [], Mock.Of() ) => HttpRoute = httpRoute; public IHttpRoute HttpRoute { get; } } diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/FilteredControllerTypes.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/FilteredControllerTypes.cs index 6509a017..f84e913c 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/FilteredControllerTypes.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/FilteredControllerTypes.cs @@ -8,7 +8,7 @@ namespace Asp.Versioning; internal sealed class FilteredControllerTypes : ControllerFeatureProvider, ICollection { - private readonly HashSet controllerTypes = new(); + private readonly HashSet controllerTypes = []; protected override bool IsController( TypeInfo typeInfo ) => base.IsController( typeInfo ) && controllerTypes.Contains( typeInfo ); diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs index bd419e18..fc1ff1b6 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs @@ -91,7 +91,7 @@ public virtual void OnProvidersExecuting( ApiDescriptionProviderContext context var results = FilterResults( context.Results, Conventions ); - if ( results.Count == 0 ) + if ( results.Length == 0 ) { return; } @@ -106,7 +106,7 @@ public virtual void OnProvidersExecuting( ApiDescriptionProviderContext context odata.AddRouteComponents( model ); - for ( var j = 0; j < results.Count; j++ ) + for ( var j = 0; j < results.Length; j++ ) { var result = results[j]; var metadata = result.ActionDescriptor.GetApiVersionMetadata(); @@ -158,7 +158,7 @@ private static int ODataOrder() => private static void MarkAsAdHoc( ODataModelBuilder builder, IEdmModel model ) => model.SetAnnotationValue( model, AdHocAnnotation.Instance ); - private static IReadOnlyList FilterResults( + private static ApiDescription[] FilterResults( IList results, IReadOnlyList conventions ) { @@ -193,7 +193,7 @@ private static IReadOnlyList FilterResults( } } - return filtered?.ToArray() ?? Array.Empty(); + return filtered?.ToArray() ?? []; } private sealed class StubModelMetadataProvider : IModelMetadataProvider diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Controllers/VersionedMetadataController.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Controllers/VersionedMetadataController.cs index e29e2ede..1f4b0266 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Controllers/VersionedMetadataController.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Controllers/VersionedMetadataController.cs @@ -4,7 +4,6 @@ namespace Asp.Versioning.Controllers; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OData.Routing.Controllers; -using Microsoft.Extensions.Primitives; using static Microsoft.OData.ODataConstants; using static Microsoft.OData.ODataUtils; using static Microsoft.OData.ODataVersion; @@ -17,6 +16,8 @@ namespace Asp.Versioning.Controllers; [ControllerName( "OData" )] public class VersionedMetadataController : MetadataController { + private static readonly string[] values = ["GET", "OPTIONS"]; + /// /// Handles a request for the HTTP OPTIONS method. /// @@ -55,8 +56,8 @@ public virtual IActionResult GetOptions() { var headers = Response.Headers; - headers.Add( "Allow", new StringValues( new[] { "GET", "OPTIONS" } ) ); - headers.Add( ODataVersionHeader, ODataVersionToString( V4 ) ); + headers.Allow = new( values ); + headers[ODataVersionHeader] = ODataVersionToString( V4 ); return Ok(); } diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs index dede3b74..c3d8948a 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs @@ -26,7 +26,7 @@ public void Add( string prefixName, string routeTemplate, ODataBatchHandler hand Debug.Assert( count < mappings.Length, "The capacity has been exceeded." ); var template = TemplateParser.Parse( routeTemplate.TrimStart( '/' ) ); - var matcher = new TemplateMatcher( template, new() ); + var matcher = new TemplateMatcher( template, [] ); handler.PrefixName = prefixName; mappings[count++] = (matcher, handler, version); diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeature.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeature.cs index 95a2cdc7..5ab6b579 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeature.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ModelConfigurationFeature.cs @@ -21,5 +21,5 @@ public class ModelConfigurationFeature /// Gets the collection of model configurations in an application. /// /// The collection of model configurations in an application. - public ICollection ModelConfigurations => modelConfigurations ??= new(); + public ICollection ModelConfigurations => modelConfigurations ??= []; } \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs index 8b1dd45e..402da34d 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs @@ -89,7 +89,7 @@ private static if ( controller.ControllerType.IsMetadataController() ) { - metadataControllers ??= new(); + metadataControllers ??= []; metadataControllers.Add( controller ); continue; } @@ -116,7 +116,7 @@ private static if ( supported == null && versions.Count > 0 ) { - supported = new(); + supported = []; } for ( var k = 0; k < versions.Count; k++ ) @@ -128,7 +128,7 @@ private static if ( deprecated == null && versions.Count > 0 ) { - deprecated = new(); + deprecated = []; } for ( var k = 0; k < versions.Count; k++ ) @@ -220,7 +220,7 @@ private void ApplyMetadataControllerConventions( builder.ApplyTo( metadataController ); } - private IReadOnlyList MergeApiVersions( + private ApiVersion[] MergeApiVersions( SortedSet? supported, SortedSet? deprecated ) { @@ -228,14 +228,14 @@ private IReadOnlyList MergeApiVersions( { if ( supported == null ) { - return new[] { Options.DefaultApiVersion }; + return [Options.DefaultApiVersion]; } - return supported.ToArray(); + return [.. supported]; } else if ( supported == null ) { - return deprecated.ToArray(); + return [.. deprecated]; } return supported.Union( deprecated ).ToArray(); diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs index 14b3aeca..a4b7bfd3 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Routing/DefaultMetadataMatcherPolicy.cs @@ -79,7 +79,7 @@ public IReadOnlyList GetEdges( IReadOnlyList endpoints continue; } - edges ??= new(); + edges ??= []; edges.Add( endpoint ); var model = endpoint.Metadata.GetMetadata()!.Map( Explicit | Implicit ); @@ -121,7 +121,7 @@ public IReadOnlyList GetEdges( IReadOnlyList endpoints return Array.Empty(); } - var state = (lowestApiVersion, routePatterns?.ToArray() ?? Array.Empty()); + var state = (lowestApiVersion, routePatterns?.ToArray() ?? []); return new PolicyNodeEdge[] { new( state, edges ) }; } diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs index 1193606f..3386690f 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs @@ -634,7 +634,7 @@ public static IEnumerable EnableQueryAttributeData MethodInfo = typeof( ControllerBase ).GetRuntimeMethod( nameof( ControllerBase.Ok ), Type.EmptyTypes ), EndpointMetadata = new object[] { - new ODataRoutingMetadata( string.Empty, model, new() ), + new ODataRoutingMetadata( string.Empty, model, [] ), }, }, HttpMethod = method, @@ -665,7 +665,7 @@ private static ApiDescription NewApiDescription( Type controllerType, Type respo MethodInfo = controllerType.GetRuntimeMethods().Single( m => m.Name == "Get" ), EndpointMetadata = new object[] { - new ODataRoutingMetadata( string.Empty, model, new() ), + new ODataRoutingMetadata( string.Empty, model, [] ), }, }, HttpMethod = "GET", diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/DefaultMetadataMatcherPolicyTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/DefaultMetadataMatcherPolicyTest.cs index 2e3273c3..0d2bd095 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/DefaultMetadataMatcherPolicyTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/DefaultMetadataMatcherPolicyTest.cs @@ -17,7 +17,7 @@ public void applies_to_endpoints_should_return_true_for_service_document() var paramSource = Mock.Of(); var options = Options.Create( new ApiVersioningOptions() ); var policy = new DefaultMetadataMatcherPolicy( paramSource, options ); - var metadata = new ODataRoutingMetadata( string.Empty, EdmCoreModel.Instance, new ODataPathTemplate() ); + var metadata = new ODataRoutingMetadata( string.Empty, EdmCoreModel.Instance, [] ); var items = new object[] { metadata }; var endpoints = new Endpoint[] { new( Limbo, new( items ), default ) }; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs index 62b9a374..85427ba8 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationCollection.cs @@ -17,8 +17,8 @@ public class ApiVersionMetadataCollationCollection : IList, /// public ApiVersionMetadataCollationCollection() { - items = new(); - groups = new(); + items = []; + groups = []; } /// diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationContext.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationContext.cs index dc1dfa25..313c2f26 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationContext.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/ApiVersionMetadataCollationContext.cs @@ -11,5 +11,5 @@ public class ApiVersionMetadataCollationContext /// Gets the read-only list of collation results. /// /// The read-only list of collation results. - public ApiVersionMetadataCollationCollection Results { get; } = new(); + public ApiVersionMetadataCollationCollection Results { get; } = []; } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs index 6b863530..485fa64c 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs @@ -63,7 +63,7 @@ public string? RawRequestedApiVersion } set { - rawApiVersions = string.IsNullOrEmpty( value ) ? default : new[] { value }; + rawApiVersions = string.IsNullOrEmpty( value ) ? default : [value]; } } @@ -105,7 +105,7 @@ public ApiVersion? RequestedApiVersion if ( apiVersion is not null && ( rawApiVersions is null || rawApiVersions.Count == 0 ) ) { - rawApiVersions = new[] { apiVersion.ToString() }; + rawApiVersions = [apiVersion.ToString()]; } } } @@ -116,6 +116,6 @@ private static AmbiguousApiVersionException NewAmbiguousApiVersionException( IRe string.Format( CultureInfo.CurrentCulture, CommonSR.MultipleDifferentApiVersionsRequested, - string.Join( ", ", values.ToArray(), 0, values.Count ) ), + string.Join( ", ", [.. values], 0, values.Count ) ), values ); } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs index 5b8d029e..f83dd0aa 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs @@ -163,11 +163,11 @@ private static bool TryGetApiVersions( IList metadata, out ApiVersionBuc var versions = provider.Versions; var target = provider.Options switch { - None => supported ??= new(), - Mapped => mapped ??= new(), - Deprecated => deprecated ??= new(), - Advertised => advertised ??= new(), - Advertised | Deprecated => deprecatedAdvertised ??= new(), + None => supported ??= [], + Mapped => mapped ??= [], + Deprecated => deprecated ??= [], + Advertised => advertised ??= [], + Advertised | Deprecated => deprecatedAdvertised ??= [], _ => default, }; @@ -183,11 +183,11 @@ private static bool TryGetApiVersions( IList metadata, out ApiVersionBuc } buckets = new( - mapped?.ToArray() ?? Array.Empty(), - supported?.ToArray() ?? Array.Empty(), - deprecated?.ToArray() ?? Array.Empty(), - advertised?.ToArray() ?? Array.Empty(), - deprecatedAdvertised?.ToArray() ?? Array.Empty() ); + mapped?.ToArray() ?? [], + supported?.ToArray() ?? [], + deprecated?.ToArray() ?? [], + advertised?.ToArray() ?? [], + deprecatedAdvertised?.ToArray() ?? [] ); return true; } @@ -224,7 +224,7 @@ private static ApiVersionMetadata Build( IList metadata, ApiVersionSet v } else { - emptyVersions = Array.Empty(); + emptyVersions = []; endpointModel = new( declaredVersions: emptyVersions, inheritedSupported, @@ -248,7 +248,7 @@ private static ApiVersionMetadata Build( IList metadata, ApiVersionSet v } else { - emptyVersions = Array.Empty(); + emptyVersions = []; endpointModel = new( declaredVersions: mapped, supportedVersions: inheritedSupported, diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs index 1b16255a..fed4ed71 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs @@ -78,7 +78,7 @@ protected readonly struct ErrorDetail { private readonly ProblemDetails problemDetails; private readonly InnerError? innerError; - private readonly Dictionary extensions = new(); + private readonly Dictionary extensions = []; internal ErrorDetail( ProblemDetails problemDetails ) { @@ -157,7 +157,7 @@ public string? Target protected readonly struct InnerError { private readonly ProblemDetails problemDetails; - private readonly Dictionary extensions = new(); + private readonly Dictionary extensions = []; internal InnerError( ProblemDetails problemDetails ) => this.problemDetails = problemDetails; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/HeaderApiVersionReader.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/HeaderApiVersionReader.cs index f6602402..96b90c63 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/HeaderApiVersionReader.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/HeaderApiVersionReader.cs @@ -70,7 +70,7 @@ public virtual IReadOnlyList Read( HttpRequest request ) if ( versions == null ) { - return version == null ? Array.Empty() : new[] { version }; + return version == null ? [] : [version]; } return versions.ToArray(); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReader.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReader.cs index 48cdd92c..9a040c6f 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReader.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReader.cs @@ -22,18 +22,18 @@ public virtual IReadOnlyList Read( HttpRequest request ) if ( accept is null || ReadAcceptHeader( accept ) is not string otherVersion ) { - return version is null ? Array.Empty() : new[] { version }; + return version is null ? [] : [version]; } var comparer = StringComparer.OrdinalIgnoreCase; if ( version is null || comparer.Equals( version, otherVersion ) ) { - return new[] { otherVersion }; + return [otherVersion]; } - return comparer.Compare( version, otherVersion ) <= 0 ? - new[] { version, otherVersion } : - new[] { otherVersion, version }; + return comparer.Compare( version, otherVersion ) <= 0 + ? [version, otherVersion] + : [otherVersion, version]; } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/QueryStringApiVersionReader.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/QueryStringApiVersionReader.cs index 6d167d33..144f5b34 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/QueryStringApiVersionReader.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/QueryStringApiVersionReader.cs @@ -66,7 +66,7 @@ public virtual IReadOnlyList Read( HttpRequest request ) if ( versions == null ) { - return version == null ? Array.Empty() : new[] { version }; + return version == null ? [] : [version]; } return versions.ToArray(); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs index 4cde72f6..e4d6b94d 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs @@ -19,7 +19,7 @@ private static Task OnExecute( HttpContext context, ILogger logger ) { var apiVersions = context.ApiVersioningFeature().RawRequestedApiVersions; - logger.ApiVersionAmbiguous( apiVersions.ToArray() ); + logger.ApiVersionAmbiguous( [.. apiVersions] ); context.Response.StatusCode = StatusCodes.Status400BadRequest; if ( !context.TryGetProblemDetailsService( out var problemDetails ) ) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs index e2eaaaff..285a65c1 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs @@ -151,7 +151,7 @@ public PolicyJumpTable BuildJumpTable( int exitDestination, IReadOnlyList(), + routePatterns ?? [], apiVersionParser, source, Options ); @@ -192,7 +192,7 @@ public IReadOnlyList GetEdges( IReadOnlyList endpoints if ( model.IsApiVersionNeutral ) { builder.Add( endpoint, ApiVersion.Neutral, metadata ); - neutralEndpoints ??= new(); + neutralEndpoints ??= []; neutralEndpoints.Add( (endpoint, metadata) ); } else @@ -309,7 +309,7 @@ private static void Collate( if ( versions.Count > 0 ) { - supported ??= new(); + supported ??= []; for ( var j = 0; j < versions.Count; j++ ) { @@ -324,7 +324,7 @@ private static void Collate( return; } - deprecated ??= new(); + deprecated ??= []; for ( var j = 0; j < versions.Count; j++ ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ClientErrorEndpointBuilder.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ClientErrorEndpointBuilder.cs index 8077e8f9..cad20584 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ClientErrorEndpointBuilder.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ClientErrorEndpointBuilder.cs @@ -57,7 +57,7 @@ private string[] GetDisplayNames() { if ( candidates.Count == 0 ) { - return Array.Empty(); + return []; } ref readonly var candidate = ref candidates[0]; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/EdgeBuilder.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/EdgeBuilder.cs index 010210f7..8fb60798 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/EdgeBuilder.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/EdgeBuilder.cs @@ -86,7 +86,7 @@ private void Add( ref EdgeKey key, RouteEndpoint endpoint ) if ( !edges.TryGetValue( key, out var endpoints ) ) { - edges.Add( key, endpoints = new() ); + edges.Add( key, endpoints = [] ); } endpoints.Add( endpoint ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs index 776251d8..d914fab7 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs @@ -128,27 +128,29 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context ) var groupResults = new List( capacity: results.Count ); var unversioned = default( Dictionary ); var formatGroupName = Options.FormatGroupName; + var versions = FlattenApiVersions( results ); - foreach ( var version in FlattenApiVersions( results ) ) + for ( var i = 0; i < versions.Length; i++ ) { + var version = versions[i]; var formattedVersion = version.ToString( Options.GroupNameFormat, CurrentCulture ); - for ( var i = 0; i < results.Count; i++ ) + for ( var j = 0; j < results.Count; j++ ) { - if ( unversioned != null && unversioned.ContainsKey( i ) ) + if ( unversioned != null && unversioned.ContainsKey( j ) ) { continue; } - var result = results[i]; + var result = results[j]; var action = result.ActionDescriptor; if ( !ShouldExploreAction( action, version ) ) { if ( IsUnversioned( action ) ) { - unversioned ??= new(); - unversioned.Add( i, result ); + unversioned ??= []; + unversioned.Add( j, result ); } continue; @@ -181,9 +183,9 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context ) results.Clear(); - for ( var i = 0; i < groupResults.Count; i++ ) + for ( var j = 0; j < groupResults.Count; j++ ) { - results.Add( groupResults[i] ); + results.Add( groupResults[j] ); } if ( unversioned == null ) @@ -289,7 +291,7 @@ private static void AddOrUpdateResult( results.Add( result ); } - private IEnumerable FlattenApiVersions( IList descriptions ) + private ApiVersion[] FlattenApiVersions( IList descriptions ) { var versions = default( SortedSet ); @@ -301,7 +303,7 @@ private IEnumerable FlattenApiVersions( IList descri if ( versions is null && declaredVersions.Count > 0 ) { - versions = new(); + versions = []; } for ( var j = 0; j < declaredVersions.Count; j++ ) @@ -312,10 +314,10 @@ private IEnumerable FlattenApiVersions( IList descri if ( versions is null ) { - return new[] { Options.DefaultApiVersion }; + return [Options.DefaultApiVersion]; } - return versions; + return [.. versions]; } private sealed class SimpleConstraintResolver : IInlineConstraintResolver diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Abstractions/ActionDescriptorExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Abstractions/ActionDescriptorExtensions.cs index c5afef16..9ca5df02 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Abstractions/ActionDescriptorExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Abstractions/ActionDescriptorExtensions.cs @@ -43,7 +43,7 @@ internal static void AddOrReplaceApiVersionMetadata( this ActionDescriptor actio if ( endpointMetadata == null ) { - action.EndpointMetadata = new List() { value }; + action.EndpointMetadata = [value]; return; } @@ -65,7 +65,7 @@ internal static void AddOrReplaceApiVersionMetadata( this ActionDescriptor actio if ( endpointMetadata.IsReadOnly ) { - action.EndpointMetadata = new List() { value }; + action.EndpointMetadata = [value]; } else { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionCollator.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionCollator.cs index 88c2c521..e621198a 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionCollator.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiVersionCollator.cs @@ -114,7 +114,7 @@ private IEnumerable> GroupActionsByController( I if ( !groups.TryGetValue( key, out var values ) ) { - groups.Add( key, values = new() ); + groups.Add( key, values = [] ); } values.Add( action ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs index fb9933b1..82770380 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs @@ -48,7 +48,7 @@ public virtual void ApplyTo( ActionModel item ) } else { - emptyVersions = Array.Empty(); + emptyVersions = []; endpointModel = new( declaredVersions: emptyVersions, inheritedSupported, @@ -68,7 +68,7 @@ public virtual void ApplyTo( ActionModel item ) } else { - emptyVersions = Array.Empty(); + emptyVersions = []; endpointModel = new( declaredVersions: mapped, supportedVersions: inheritedSupported, diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs index 894648c4..e2a8fd68 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -109,8 +109,8 @@ IUrlHelperFactory NewFactory( IServiceProvider serviceProvider ) if ( source.VersionsByUrl() ) { - var factory = ActivatorUtilities.CreateFactory( typeof( ApiVersionUrlHelperFactory ), new[] { typeof( IUrlHelperFactory ) } ); - instance = factory( serviceProvider, new[] { decorated } ); + var factory = ActivatorUtilities.CreateFactory( typeof( ApiVersionUrlHelperFactory ), [typeof( IUrlHelperFactory )] ); + instance = factory( serviceProvider, [decorated] ); } return (IUrlHelperFactory) instance; diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestEndpointDataSource.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestEndpointDataSource.cs index a10a4b81..b0cd40b3 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestEndpointDataSource.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestEndpointDataSource.cs @@ -10,11 +10,11 @@ namespace Asp.Versioning.ApiExplorer; internal sealed class TestEndpointDataSource : EndpointDataSource { - public override IReadOnlyList Endpoints { get; } = CreateEndpoints(); + public override List Endpoints { get; } = CreateEndpoints(); public override IChangeToken GetChangeToken() => NullChangeToken.Singleton; - private static IReadOnlyList CreateEndpoints() + private static List CreateEndpoints() { var endpoints = new List(); @@ -24,154 +24,154 @@ private static IReadOnlyList CreateEndpoints() return endpoints; } - private static void AddOrderEndpoints( ICollection endpoints ) + private static void AddOrderEndpoints( List endpoints ) { // api version 0.9 and 1.0 endpoints.Add( NewEndpoint( "GET-orders/{id}", "orders/{id}", - declared: new ApiVersion[] { new( 0, 9 ), new( 1, 0 ) }, - supported: new ApiVersion[] { new( 1, 0 ) }, - deprecated: new ApiVersion[] { new( 0, 9 ) } ) ); + declared: [new( 0, 9 ), new( 1, 0 )], + supported: [new( 1, 0 )], + deprecated: [new( 0, 9 )] ) ); endpoints.Add( NewEndpoint( "POST-orders", "orders", - declared: new ApiVersion[] { new( 1, 0 ) }, - supported: new ApiVersion[] { new( 1, 0 ) } ) ); + declared: [new( 1, 0 )], + supported: [new( 1, 0 )] ) ); // api version 2.0 endpoints.Add( NewEndpoint( "GET-orders", "orders", - declared: new ApiVersion[] { new( 2, 0 ) }, - supported: new ApiVersion[] { new( 2, 0 ) } ) ); + declared: [new( 2, 0 )], + supported: [new( 2, 0 )] ) ); endpoints.Add( NewEndpoint( "GET-orders/{id}", "orders/{id}", - declared: new ApiVersion[] { new( 2, 0 ) }, - supported: new ApiVersion[] { new( 2, 0 ) } ) ); + declared: [new( 2, 0 )], + supported: [new( 2, 0 )] ) ); endpoints.Add( NewEndpoint( "POST-orders", "orders", - declared: new ApiVersion[] { new( 2, 0 ) }, - supported: new ApiVersion[] { new( 2, 0 ) } ) ); + declared: [new( 2, 0 )], + supported: [new( 2, 0 )] ) ); // api version 3.0 endpoints.Add( NewEndpoint( "GET-orders", "orders", - declared: new ApiVersion[] { new( 3, 0 ) }, - supported: new ApiVersion[] { new( 3, 0 ) }, - advertised: new ApiVersion[] { new( 4, 0 ) } ) ); + declared: [new( 3, 0 )], + supported: [new( 3, 0 )], + advertised: [new( 4, 0 )] ) ); endpoints.Add( NewEndpoint( "GET-orders/{id}", "orders/{id}", - declared: new ApiVersion[] { new( 3, 0 ) }, - supported: new ApiVersion[] { new( 3, 0 ) }, - advertised: new ApiVersion[] { new( 4, 0 ) } ) ); + declared: [new( 3, 0 )], + supported: [new( 3, 0 )], + advertised: [new( 4, 0 )] ) ); endpoints.Add( NewEndpoint( "POST-orders", "orders", - declared: new ApiVersion[] { new( 3, 0 ) }, - supported: new ApiVersion[] { new( 3, 0 ) }, - advertised: new ApiVersion[] { new( 4, 0 ) } ) ); + declared: [new( 3, 0 )], + supported: [new( 3, 0 )], + advertised: [new( 4, 0 )] ) ); endpoints.Add( NewEndpoint( "DELETE-orders/{id}", "orders/{id}", - declared: new ApiVersion[] { new( 3, 0 ) }, - supported: new ApiVersion[] { new( 3, 0 ) }, - advertised: new ApiVersion[] { new( 4, 0 ) } ) ); + declared: [new( 3, 0 )], + supported: [new( 3, 0 )], + advertised: [new( 4, 0 )] ) ); } - private static void AddPeopleEndpoints( ICollection endpoints ) + private static void AddPeopleEndpoints( List endpoints ) { // api version 0.9 and 1.0 endpoints.Add( NewEndpoint( "GET-people/{id}", "people/{id}", - declared: new ApiVersion[] { new( 0, 9 ), new( 1, 0 ) }, - supported: new ApiVersion[] { new( 1, 0 ) }, - deprecated: new ApiVersion[] { new( 0, 9 ) } ) ); + declared: [new( 0, 9 ), new( 1, 0 )], + supported: [new( 1, 0 )], + deprecated: [new( 0, 9 )] ) ); endpoints.Add( NewEndpoint( "POST-people", "people", - declared: new ApiVersion[] { new( 1, 0 ) }, - supported: new ApiVersion[] { new( 1, 0 ) } ) ); + declared: [new( 1, 0 )], + supported: [new( 1, 0 )] ) ); // api version 2.0 endpoints.Add( NewEndpoint( "GET-people", "people", - declared: new ApiVersion[] { new( 2, 0 ) }, - supported: new ApiVersion[] { new( 2, 0 ) } ) ); + declared: [new( 2, 0 )], + supported: [new( 2, 0 )] ) ); endpoints.Add( NewEndpoint( "GET-people/{id}", "people/{id}", - declared: new ApiVersion[] { new( 2, 0 ) }, - supported: new ApiVersion[] { new( 2, 0 ) } ) ); + declared: [new( 2, 0 )], + supported: [new( 2, 0 )] ) ); endpoints.Add( NewEndpoint( "POST-people", "people", - declared: new ApiVersion[] { new( 2, 0 ) }, - supported: new ApiVersion[] { new( 2, 0 ) } ) ); + declared: [new( 2, 0 )], + supported: [new( 2, 0 )] ) ); // api version 3.0 endpoints.Add( NewEndpoint( "GET-people", "people", - declared: new ApiVersion[] { new( 3, 0 ) }, - supported: new ApiVersion[] { new( 3, 0 ) }, - advertised: new ApiVersion[] { new( 4, 0 ) } ) ); + declared: [new( 3, 0 )], + supported: [new( 3, 0 )], + advertised: [new( 4, 0 )] ) ); endpoints.Add( NewEndpoint( "GET-people/{id}", "people/{id}", - declared: new ApiVersion[] { new( 3, 0 ) }, - supported: new ApiVersion[] { new( 3, 0 ) }, - advertised: new ApiVersion[] { new( 4, 0 ) } ) ); + declared: [new( 3, 0 )], + supported: [new( 3, 0 )], + advertised: [new( 4, 0 )] ) ); endpoints.Add( NewEndpoint( "POST-people", "people", - declared: new ApiVersion[] { new( 3, 0 ) }, - supported: new ApiVersion[] { new( 3, 0 ) }, - advertised: new ApiVersion[] { new( 4, 0 ) } ) ); + declared: [new( 3, 0 )], + supported: [new( 3, 0 )], + advertised: [new( 4, 0 )] ) ); } private static Endpoint NewEndpoint( string displayName, string pattern, - IEnumerable declared, - IEnumerable supported, - IEnumerable deprecated = null, - IEnumerable advertised = null, - IEnumerable advertisedDeprecated = null ) + ApiVersion[] declared, + ApiVersion[] supported, + ApiVersion[] deprecated = null, + ApiVersion[] advertised = null, + ApiVersion[] advertisedDeprecated = null ) { var metadata = new ApiVersionMetadata( ApiVersionModel.Empty, diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApiVersionModelBinderTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApiVersionModelBinderTest.cs index 76326328..a636c419 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApiVersionModelBinderTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApiVersionModelBinderTest.cs @@ -5,7 +5,6 @@ namespace Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; public class ApiVersionModelBinderTest { @@ -59,7 +58,7 @@ private static ModelBindingContext NewModelBindingContext( ApiVersion apiVersion bindingContext.SetupGet( bc => bc.HttpContext ).Returns( httpContext ); bindingContext.SetupProperty( bc => bc.Result ); - bindingContext.SetupProperty( bc => bc.ValidationState, new ValidationStateDictionary() ); + bindingContext.SetupProperty( bc => bc.ValidationState, [] ); return bindingContext.Object; } diff --git a/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpResponseMessageExtensions.cs b/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpResponseMessageExtensions.cs index dd9f90fd..0a430a32 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpResponseMessageExtensions.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/System.Net.Http/HttpResponseMessageExtensions.cs @@ -99,7 +99,7 @@ public static IReadOnlyDictionary GetOpenApiDocumentUrls( } var key = GetApiVersionExtension( link, ref parser ); - urls ??= new(); + urls ??= []; urls[key] = link.LinkTarget; } diff --git a/src/Client/src/Asp.Versioning.Http.Client/net#.0/ILoggerExtensions.cs b/src/Client/src/Asp.Versioning.Http.Client/net#.0/ILoggerExtensions.cs index 6a575ca7..a7535807 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/net#.0/ILoggerExtensions.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/net#.0/ILoggerExtensions.cs @@ -74,7 +74,7 @@ private static string[] FormatLinks( SunsetPolicy sunsetPolicy ) { if ( !sunsetPolicy.HasLinks ) { - return Array.Empty(); + return []; } // (<Language>[,<Language>]): <Url> diff --git a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs index 0c77a225..99aa8402 100644 --- a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs +++ b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderBase.cs @@ -29,7 +29,7 @@ public abstract partial class ActionApiVersionConventionBuilderBase : ApiVersion /// Gets the collection of API versions mapped to the current action. /// </summary> /// <value>A <see cref="ICollection{T}">collection</see> of mapped <see cref="ApiVersion">API versions</see>.</value> - protected ICollection<ApiVersion> MappedVersions => mapped ??= new(); + protected ICollection<ApiVersion> MappedVersions => mapped ??= []; /// <summary> /// Gets the controller naming convention associated with the builder. diff --git a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection.cs b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection.cs index 7b3c91e4..f2673f0d 100644 --- a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection.cs +++ b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection.cs @@ -36,7 +36,7 @@ protected internal virtual ActionApiVersionConventionBuilder GetOrAdd( MethodInf if ( actionBuilderMappings is null ) { mapping = new( actionMethod, new( controllerBuilder ) ); - actionBuilderMappings = new() { mapping }; + actionBuilderMappings = [mapping]; return mapping.Builder; } diff --git a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection{T}.cs b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection{T}.cs index a94bc224..ff19f2b7 100644 --- a/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection{T}.cs +++ b/src/Common/src/Common.Mvc/Conventions/ActionApiVersionConventionBuilderCollection{T}.cs @@ -45,7 +45,7 @@ protected internal virtual ActionApiVersionConventionBuilder<T> GetOrAdd( Method if ( actionBuilderMappings is null ) { mapping = new( actionMethod, new( controllerBuilder ) ); - actionBuilderMappings = new() { mapping }; + actionBuilderMappings = [mapping]; return mapping.Builder; } diff --git a/src/Common/src/Common.Mvc/Conventions/ApiVersionConventionBuilder.cs b/src/Common/src/Common.Mvc/Conventions/ApiVersionConventionBuilder.cs index ded967a0..477d6666 100644 --- a/src/Common/src/Common.Mvc/Conventions/ApiVersionConventionBuilder.cs +++ b/src/Common/src/Common.Mvc/Conventions/ApiVersionConventionBuilder.cs @@ -40,13 +40,13 @@ public partial class ApiVersionConventionBuilder : IApiVersionConventionBuilder /// Gets a collection of controller convention builders. /// </summary> /// <value>A <see cref="IDictionary{TKey, TValue}">collection</see> of <see cref="IControllerConventionBuilder">controller convention builders</see>.</value> - protected IDictionary<Type, IControllerConventionBuilder> ControllerConventionBuilders => controllerConventionBuilders ??= new(); + protected IDictionary<Type, IControllerConventionBuilder> ControllerConventionBuilders => controllerConventionBuilders ??= []; /// <summary> /// Gets a collection of controller conventions. /// </summary> /// <value>A <see cref="IList{T}">list</see> of <see cref="IControllerConvention">controller conventions</see>.</value> - protected IList<IControllerConvention> ControllerConventions => controllerConventions ??= new(); + protected IList<IControllerConvention> ControllerConventions => controllerConventions ??= []; /// <inheritdoc /> public virtual int Count => diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs index fa2f7a07..a9c96f59 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ImplicitModelBoundSettingsConvention.cs @@ -17,7 +17,7 @@ namespace Asp.Versioning.Conventions; /// </summary> public sealed partial class ImplicitModelBoundSettingsConvention : IModelConfiguration, IODataQueryOptionsConvention { - private readonly HashSet<Type> types = new(); + private readonly HashSet<Type> types = []; /// <inheritdoc /> public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string? routePrefix ) @@ -67,7 +67,7 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string? rou #if NETFRAMEWORK var array = new StructuralTypeConfiguration[count]; - types = new(); + types = []; #else var pool = ArrayPool<StructuralTypeConfiguration>.Shared; var array = pool.Rent( count ); @@ -96,10 +96,7 @@ public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string? rou return default; } - types = new HashSet<Type>() - { - structuralTypes.Current.ClrType, - }; + types = [structuralTypes.Current.ClrType]; while ( structuralTypes.MoveNext() ) { diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection.cs index 539051ea..c9a26f99 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection.cs @@ -33,7 +33,7 @@ protected internal virtual ODataActionQueryOptionsConventionBuilder GetOrAdd( Me if ( actionBuilderMappings == null ) { var builder = new ODataActionQueryOptionsConventionBuilder( controllerBuilder ); - actionBuilderMappings = new() { new( actionMethod, builder ) }; + actionBuilderMappings = [new( actionMethod, builder )]; return builder; } diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection{T}.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection{T}.cs index 718b854e..11addf0d 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection{T}.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderCollection{T}.cs @@ -44,7 +44,7 @@ protected internal virtual ODataActionQueryOptionsConventionBuilder<T> GetOrAdd( if ( actionBuilderMappings == null ) { var builder = new ODataActionQueryOptionsConventionBuilder<T>( controllerBuilder ); - actionBuilderMappings = new() { new( actionMethod, builder ) }; + actionBuilderMappings = [new( actionMethod, builder )]; return builder; } diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs index 4afba308..7cb50224 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs @@ -24,6 +24,7 @@ namespace Asp.Versioning.Conventions; internal sealed partial class ODataAttributeVisitor { + private static readonly char[] Comma = [',']; private readonly ODataQueryOptionDescriptionContext context; internal ODataAttributeVisitor( @@ -124,7 +125,7 @@ private void VisitEnableQuery( IReadOnlyList<EnableQueryAttribute> attributes ) continue; } - var properties = attribute.AllowedOrderByProperties.Split( new[] { ',' }, RemoveEmptyEntries ); + var properties = attribute.AllowedOrderByProperties.Split( Comma, RemoveEmptyEntries ); var allowedOrderByProperties = context.AllowedOrderByProperties; var comparer = StringComparer.OrdinalIgnoreCase; diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs index 72f8c712..5486f49f 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionDescriptionContext.cs @@ -53,7 +53,7 @@ protected internal ODataQueryOptionDescriptionContext( AllowedArithmeticOperators = validationSettings.AllowedArithmeticOperators; AllowedFunctions = validationSettings.AllowedFunctions; AllowedLogicalOperators = validationSettings.AllowedLogicalOperators; - allowedOrderByProperties = validationSettings.AllowedOrderByProperties.ToList(); + allowedOrderByProperties = [.. validationSettings.AllowedOrderByProperties]; MaxOrderByNodeCount = validationSettings.MaxOrderByNodeCount; MaxAnyAllExpressionDepth = validationSettings.MaxAnyAllExpressionDepth; MaxNodeCount = validationSettings.MaxNodeCount; @@ -90,25 +90,25 @@ protected internal ODataQueryOptionDescriptionContext( /// Gets the names of properties that can be selected. /// </summary> /// <value>A <see cref="IList{T}">list</see> of selectable property names.</value> - public IList<string> AllowedSelectProperties => allowedSelectProperties ??= new(); + public IList<string> AllowedSelectProperties => allowedSelectProperties ??= []; /// <summary> /// Gets the names of properties that can be expanded. /// </summary> /// <value>A <see cref="IList{T}">list</see> of expandable property names.</value> - public IList<string> AllowedExpandProperties => allowedExpandProperties ??= new(); + public IList<string> AllowedExpandProperties => allowedExpandProperties ??= []; /// <summary> /// Gets the names of properties that can be filtered. /// </summary> /// <value>A <see cref="IList{T}">list</see> of filterable property names.</value> - public IList<string> AllowedFilterProperties => allowedFilterProperties ??= new(); + public IList<string> AllowedFilterProperties => allowedFilterProperties ??= []; /// <summary> /// Gets the names of properties that can be sorted. /// </summary> /// <value>A <see cref="IList{T}">list</see> of sortable property names.</value> - public IList<string> AllowedOrderByProperties => allowedOrderByProperties ??= new(); + public IList<string> AllowedOrderByProperties => allowedOrderByProperties ??= []; /// <summary> /// Gets or sets the maximum number of expressions that can be present in the $orderby query option. diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs index cf275e86..b41a4ff9 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs @@ -43,13 +43,13 @@ public IODataQueryOptionDescriptionProvider DescriptionProvider /// </summary> /// <value>A <see cref="IDictionary{TKey, TValue}">collection</see> of /// <see cref="IODataQueryOptionsConventionBuilder">controller convention builders</see>.</value> - protected IDictionary<Type, IODataQueryOptionsConventionBuilder> ConventionBuilders => conventionBuilders ??= new(); + protected IDictionary<Type, IODataQueryOptionsConventionBuilder> ConventionBuilders => conventionBuilders ??= []; /// <summary> /// Gets a collection of OData query option conventions. /// </summary> /// <value>A <see cref="IList{T}">list</see> of <see cref="IODataQueryOptionsConvention">OData query option conventions</see>.</value> - protected IList<IODataQueryOptionsConvention> Conventions => conventions ??= new(); + protected IList<IODataQueryOptionsConvention> Conventions => conventions ??= []; /// <summary> /// Gets or creates the convention builder for the specified controller. @@ -144,7 +144,7 @@ public virtual void ApplyTo( IEnumerable<ApiDescription> apiDescriptions, ODataQ } convention = builder.Build( queryOptionSettings ); - controllerConventions ??= new(); + controllerConventions ??= []; controllerConventions.Add( controller, convention ); } diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs b/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs index 654b4033..a32cd16a 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/TypeExtensions.cs @@ -114,10 +114,10 @@ internal static IEnumerable<CustomAttributeBuilder> DeclaredAttributes( this Mem yield return new CustomAttributeBuilder( ctor, ctorArgs, - namedProperties.ToArray(), - propertyValues.ToArray(), - namedFields.ToArray(), - fieldValues.ToArray() ); + [.. namedProperties], + [.. propertyValues], + [.. namedFields], + [.. fieldValues] ); } } diff --git a/src/Common/src/Common.OData/OData/VersionedODataModelBuilder.cs b/src/Common/src/Common.OData/OData/VersionedODataModelBuilder.cs index f1fa3c43..b5fc95ea 100644 --- a/src/Common/src/Common.OData/OData/VersionedODataModelBuilder.cs +++ b/src/Common/src/Common.OData/OData/VersionedODataModelBuilder.cs @@ -34,7 +34,7 @@ public partial class VersionedODataModelBuilder /// Gets the list of model configurations associated with the builder. /// </summary> /// <value>A <see cref="IList{T}">list</see> of model configurations associated with the builder.</value> - public IList<IModelConfiguration> ModelConfigurations => modelConfigurations ??= new(); + public IList<IModelConfiguration> ModelConfigurations => modelConfigurations ??= []; /// <summary> /// Gets or sets the action that is invoked after the <see cref="IEdmModel">EDM model</see> has been created. @@ -97,7 +97,7 @@ private IReadOnlyList<IModelConfiguration> GetMergedConfigurations() private void BuildModelPerApiVersion( IReadOnlyList<ApiVersion> apiVersions, IReadOnlyList<IModelConfiguration> configurations, - ICollection<IEdmModel> models, + List<IEdmModel> models, string? routePrefix ) { for ( var i = 0; i < apiVersions.Count; i++ ) diff --git a/src/Common/src/Common/ApiVersionReader.cs b/src/Common/src/Common/ApiVersionReader.cs index 7762c6f7..90c34294 100644 --- a/src/Common/src/Common/ApiVersionReader.cs +++ b/src/Common/src/Common/ApiVersionReader.cs @@ -115,7 +115,7 @@ public IReadOnlyList<string> Read( HttpRequest request ) if ( versions == null ) { - return version == null ? Array.Empty<string>() : new[] { version }; + return version == null ? [] : [version]; } return versions.ToArray(); diff --git a/src/Common/src/Common/ApiVersioningPolicyBuilder.cs b/src/Common/src/Common/ApiVersioningPolicyBuilder.cs index 908a0342..6e571d21 100644 --- a/src/Common/src/Common/ApiVersioningPolicyBuilder.cs +++ b/src/Common/src/Common/ApiVersioningPolicyBuilder.cs @@ -33,7 +33,7 @@ public virtual ISunsetPolicyBuilder Sunset( string? name, ApiVersion? apiVersion var key = new PolicyKey( name, apiVersion ); - sunsetPolicies ??= new(); + sunsetPolicies ??= []; if ( !sunsetPolicies.TryGetValue( key, out var builder ) ) { diff --git a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs index aebee932..f6416e41 100644 --- a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs +++ b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs @@ -130,11 +130,16 @@ public virtual MediaTypeApiVersionReaderBuilder Select( Func<HttpRequest, IReadO #endif public virtual IApiVersionReader Build() => new BuiltMediaTypeApiVersionReader( - parameters?.ToArray() ?? Array.Empty<string>(), - included ?? EmptyCollection(), - excluded ?? EmptyCollection(), + parameters?.ToArray() ?? [], +#if NET45 + included ?? [], + excluded ?? [], +#else + included ?? new( capacity: 0 ), + excluded ?? new( capacity: 0 ), +#endif select ?? DefaultSelector, - readers ?? EmptyList() ); + readers?.ToArray() ?? [] ); /// <summary> /// Adds a function used to read the an API version from one or more media types. @@ -148,7 +153,7 @@ protected void AddReader( Func<IReadOnlyList<MediaTypeWithQualityHeaderValue>, I { ArgumentNullException.ThrowIfNull( reader ); - readers ??= new(); + readers ??= []; readers.Add( reader ); } diff --git a/src/Common/src/Common/MediaTypeApiVersionReaderBuilderExtensions.cs b/src/Common/src/Common/MediaTypeApiVersionReaderBuilderExtensions.cs index 82155280..c1e99a54 100644 --- a/src/Common/src/Common/MediaTypeApiVersionReaderBuilderExtensions.cs +++ b/src/Common/src/Common/MediaTypeApiVersionReaderBuilderExtensions.cs @@ -18,8 +18,7 @@ public static class MediaTypeApiVersionReaderBuilderExtensions public static T SelectFirstOrDefault<T>( this T builder ) where T : MediaTypeApiVersionReaderBuilder { ArgumentNullException.ThrowIfNull( builder ); - - builder.Select( static ( request, versions ) => versions.Count == 0 ? versions : new[] { versions[0] } ); + builder.Select( static ( request, versions ) => versions.Count == 0 ? versions : [versions[0]] ); return builder; } @@ -34,8 +33,7 @@ public static T SelectFirstOrDefault<T>( this T builder ) where T : MediaTypeApi public static T SelectLastOrDefault<T>( this T builder ) where T : MediaTypeApiVersionReaderBuilder { ArgumentNullException.ThrowIfNull( builder ); - - builder.Select( static ( request, versions ) => versions.Count == 0 ? versions : new[] { versions[versions.Count - 1] } ); + builder.Select( static ( request, versions ) => versions.Count == 0 ? versions : [versions[versions.Count - 1]] ); return builder; } } \ No newline at end of file diff --git a/src/Common/src/Common/SunsetLinkBuilder.cs b/src/Common/src/Common/SunsetLinkBuilder.cs index 28985ed9..03f72175 100644 --- a/src/Common/src/Common/SunsetLinkBuilder.cs +++ b/src/Common/src/Common/SunsetLinkBuilder.cs @@ -26,7 +26,7 @@ public ILinkBuilder Language( string value ) } else if ( languages == null ) { - languages = new() { language, value }; + languages = [language, value]; } else { From e3913cc6f6e7e22afaa72ca2b59cdfbc010b28c9 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:03:29 -0800 Subject: [PATCH 071/131] Add [Obsolete] for deprecated APIs --- .../AmbiguousApiVersionException.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/AmbiguousApiVersionException.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/AmbiguousApiVersionException.cs index 7215f3e9..baf164e4 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/AmbiguousApiVersionException.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/AmbiguousApiVersionException.cs @@ -2,6 +2,7 @@ namespace Asp.Versioning; +using System.ComponentModel; using System.Runtime.Serialization; /// <content> @@ -10,11 +11,23 @@ namespace Asp.Versioning; [Serializable] public partial class AmbiguousApiVersionException : Exception { + private const string LegacyFormatterImplMessage = "This API supports obsolete formatter-based serialization. It should not be called or extended by application code."; +#if NET + private const string LegacyFormatterImplDiagId = "SYSLIB0051"; + private const string SharedUrlFormat = "https://aka.ms/dotnet-warnings/{0}"; +#endif + /// <summary> /// Initializes a new instance of the <see cref="AmbiguousApiVersionException"/> class. /// </summary> /// <param name="info">The <see cref="SerializationInfo">serialization info</see> the exception is being deserialized with.</param> /// <param name="context">The <see cref="StreamingContext">streaming context</see> the exception is being deserialized from.</param> +#if NET + [Obsolete( LegacyFormatterImplMessage, DiagnosticId = LegacyFormatterImplDiagId, UrlFormat = SharedUrlFormat )] +#else + [Obsolete( LegacyFormatterImplMessage )] +#endif + [EditorBrowsable( EditorBrowsableState.Never )] protected AmbiguousApiVersionException( SerializationInfo info, StreamingContext context ) : base( info, context ) => apiVersions = (string[]) info.GetValue( nameof( apiVersions ), typeof( string[] ) )!; @@ -23,6 +36,10 @@ protected AmbiguousApiVersionException( SerializationInfo info, StreamingContext /// </summary> /// <param name="info">The <see cref="SerializationInfo">serialization info</see> the exception is being serialized with.</param> /// <param name="context">The <see cref="StreamingContext">streaming context</see> the exception is being serialized in.</param> +#if NET + [Obsolete( LegacyFormatterImplMessage, DiagnosticId = LegacyFormatterImplDiagId, UrlFormat = SharedUrlFormat )] +#endif + [EditorBrowsable( EditorBrowsableState.Never )] public override void GetObjectData( SerializationInfo info, StreamingContext context ) { base.GetObjectData( info, context ); From 8f86407f206e064586c46bc9d0daae988c2e0433 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:19:00 -0800 Subject: [PATCH 072/131] Use CompositeFormat where possible --- .../ApiVersionParser.cs | 4 +-- .../src/Asp.Versioning.Abstractions/Format.cs | 18 +++++++++++ .../Format.cs | 22 +++++++++++++ .../IApiVersioningBuilderExtensions.cs | 8 ++++- .../OData/src/Asp.Versioning.OData/Format.cs | 10 ++++++ .../ApiVersioningFeature.cs | 2 +- .../Builder/EndpointBuilderFinalizer.cs | 2 +- .../IEndpointConventionBuilderExtensions.cs | 3 +- .../WebApi/src/Asp.Versioning.Http/Format.cs | 16 ++++++++++ .../MediaTypeApiVersionReaderBuilder.cs | 2 +- .../Routing/AmbiguousApiVersionEndpoint.cs | 2 +- .../Routing/EndpointProblem.cs | 2 +- .../Routing/MalformedApiVersionEndpoint.cs | 2 +- .../ValidateApiVersioningOptions.cs | 2 +- .../src/Asp.Versioning.Mvc/MvcFormat.cs | 14 +++++++++ .../Conventions/ActionMethodResolver.cs | 4 +-- .../ApiVersionConventionBuilder.cs | 2 +- .../Conventions/ExpressionExtensions.cs | 2 +- .../VersionByNamespaceConvention.cs | 2 +- ...aultODataQueryOptionDescriptionProvider.cs | 31 +++++++++++-------- ...QueryOptionsConventionBuilderExtensions.cs | 6 ++-- ...ControllerQueryOptionsConventionBuilder.cs | 2 +- .../ODataQueryOptionsConventionBuilder.cs | 4 +-- .../src/Common/ApiVersioningPolicyBuilder.cs | 2 +- src/Common/src/Common/SunsetPolicyBuilder.cs | 2 +- 25 files changed, 129 insertions(+), 37 deletions(-) create mode 100644 src/Abstractions/src/Asp.Versioning.Abstractions/Format.cs create mode 100644 src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Format.cs create mode 100644 src/AspNetCore/OData/src/Asp.Versioning.OData/Format.cs create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Http/Format.cs create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/MvcFormat.cs diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs index c52194d3..97ec10bb 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionParser.cs @@ -336,11 +336,11 @@ public virtual bool TryParse( Text text, [MaybeNullWhen( false )] out ApiVersion [MethodImpl( MethodImplOptions.AggressiveInlining )] private static FormatException InvalidGroupVersion( string value ) => - new( string.Format( CultureInfo.CurrentCulture, SR.ApiVersionBadGroupVersion, value ) ); + new( string.Format( CultureInfo.CurrentCulture, Format.ApiVersionBadGroupVersion, value ) ); [MethodImpl( MethodImplOptions.AggressiveInlining )] private static FormatException InvalidStatus( string value ) => - new( string.Format( CultureInfo.CurrentCulture, SR.ApiVersionBadStatus, value ) ); + new( string.Format( CultureInfo.CurrentCulture, Format.ApiVersionBadStatus, value ) ); private static bool IsDateLike( Text value ) { diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Format.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/Format.cs new file mode 100644 index 00000000..dbea4469 --- /dev/null +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Format.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning; + +#if NET +using System.Text; +#endif + +internal static class Format +{ +#if NETSTANDARD + internal static readonly string ApiVersionBadStatus = SR.ApiVersionBadStatus; + internal static readonly string ApiVersionBadGroupVersion = SR.ApiVersionBadGroupVersion; +#else + internal static readonly CompositeFormat ApiVersionBadStatus = CompositeFormat.Parse( SR.ApiVersionBadStatus ); + internal static readonly CompositeFormat ApiVersionBadGroupVersion = CompositeFormat.Parse( SR.ApiVersionBadGroupVersion ); +#endif +} \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Format.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Format.cs new file mode 100644 index 00000000..11126284 --- /dev/null +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Format.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning; + +using System.Text; + +internal static class Format +{ + internal static readonly CompositeFormat UnsupportedQueryOption = CompositeFormat.Parse( ODataExpSR.UnsupportedQueryOption ); + internal static readonly CompositeFormat MaxExpressionDesc = CompositeFormat.Parse( ODataExpSR.MaxExpressionDesc ); + internal static readonly CompositeFormat AllowedPropertiesDesc = CompositeFormat.Parse( ODataExpSR.AllowedPropertiesDesc ); + internal static readonly CompositeFormat MaxDepthDesc = CompositeFormat.Parse( ODataExpSR.MaxDepthDesc ); + internal static readonly CompositeFormat MaxValueDesc = CompositeFormat.Parse( ODataExpSR.MaxValueDesc ); + internal static readonly CompositeFormat AllowedLogicalOperatorsDesc = CompositeFormat.Parse( ODataExpSR.AllowedLogicalOperatorsDesc ); + internal static readonly CompositeFormat AllowedArithmeticOperatorsDesc = CompositeFormat.Parse( ODataExpSR.AllowedArithmeticOperatorsDesc ); + internal static readonly CompositeFormat AmbiguousActionMethod = CompositeFormat.Parse( ODataExpSR.AmbiguousActionMethod ); + internal static readonly CompositeFormat InvalidActionMethodExpression = CompositeFormat.Parse( ODataExpSR.InvalidActionMethodExpression ); + internal static readonly CompositeFormat ActionMethodNotFound = CompositeFormat.Parse( ODataExpSR.ActionMethodNotFound ); + internal static readonly CompositeFormat AllowedFunctionsDesc = CompositeFormat.Parse( ODataExpSR.AllowedFunctionsDesc ); + internal static readonly CompositeFormat RequiredInterfaceNotImplemented = CompositeFormat.Parse( ODataExpSR.RequiredInterfaceNotImplemented ); + internal static readonly CompositeFormat ConventionStyleMismatch = CompositeFormat.Parse( ODataExpSR.ConventionStyleMismatch ); +} \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs index aba443f5..28f86621 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -102,7 +102,13 @@ private static void TryRemoveODataService( this IServiceCollection services, Typ } } - var message = string.Format( CultureInfo.CurrentCulture, SR.UnableToFindServices, nameof( IMvcBuilder ), "AddOData", "ConfigureServices(...)" ); + var message = string.Format( + CultureInfo.CurrentCulture, + Format.UnableToFindServices, + nameof( IMvcBuilder ), + "AddOData", + "ConfigureServices(...)" ); + throw new InvalidOperationException( message ); } diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Format.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/Format.cs new file mode 100644 index 00000000..c4202f8c --- /dev/null +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Format.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning; + +using System.Text; + +internal static class Format +{ + internal static readonly CompositeFormat UnableToFindServices = CompositeFormat.Parse( SR.UnableToFindServices ); +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs index 485fa64c..8437429a 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiVersioningFeature.cs @@ -115,7 +115,7 @@ private static AmbiguousApiVersionException NewAmbiguousApiVersionException( IRe new( string.Format( CultureInfo.CurrentCulture, - CommonSR.MultipleDifferentApiVersionsRequested, + Format.MultipleDifferentApiVersionsRequested, string.Join( ", ", [.. values], 0, values.Count ) ), values ); } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs index f83dd0aa..250083cd 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs @@ -267,7 +267,7 @@ private static RequestDelegate EnsureRequestDelegate( RequestDelegate? current, throw new InvalidOperationException( string.Format( CultureInfo.CurrentCulture, - SR.UnsetRequestDelegate, + Format.UnsetRequestDelegate, nameof( RequestDelegate ), nameof( RouteEndpoint ) ) ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs index 154a8f39..15525d66 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs @@ -7,6 +7,7 @@ namespace Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using System.Collections; using System.Globalization; +using System.Runtime.Serialization; using static Asp.Versioning.ApiVersionProviderOptions; /// <summary> @@ -439,7 +440,7 @@ private static void AddMetadata( EndpointBuilder builder, object item ) throw new InvalidOperationException( string.Format( CultureInfo.CurrentCulture, - SR.NoVersionSet, + Format.NoVersionSet, builder.DisplayName, nameof( IEndpointRouteBuilderExtensions.NewVersionedApi ), nameof( IEndpointRouteBuilderExtensions.WithApiVersionSet ) ) ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Format.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Format.cs new file mode 100644 index 00000000..060138aa --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Format.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning; + +using System.Text; + +internal static class Format +{ + internal static readonly CompositeFormat MultipleDifferentApiVersionsRequested = CompositeFormat.Parse( CommonSR.MultipleDifferentApiVersionsRequested ); + internal static readonly CompositeFormat NoVersionSet = CompositeFormat.Parse( SR.NoVersionSet ); + internal static readonly CompositeFormat InvalidMediaTypeTemplate = CompositeFormat.Parse( CommonSR.InvalidMediaTypeTemplate ); + internal static readonly CompositeFormat UnsetRequestDelegate = CompositeFormat.Parse( SR.UnsetRequestDelegate ); + internal static readonly CompositeFormat VersionedResourceNotSupported = CompositeFormat.Parse( SR.VersionedResourceNotSupported ); + internal static readonly CompositeFormat InvalidDefaultApiVersion = CompositeFormat.Parse( SR.InvalidDefaultApiVersion ); + internal static readonly CompositeFormat InvalidPolicyKey = CompositeFormat.Parse( CommonSR.InvalidPolicyKey ); +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs index b7055397..f86c7e50 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs @@ -34,7 +34,7 @@ public partial class MediaTypeApiVersionReaderBuilder if ( string.IsNullOrEmpty( parameterName ) && routePattern.Parameters.Count > 1 ) { - var message = string.Format( CultureInfo.CurrentCulture, CommonSR.InvalidMediaTypeTemplate, template ); + var message = string.Format( CultureInfo.CurrentCulture, Format.InvalidMediaTypeTemplate, template ); throw new ArgumentException( message, nameof( template ) ); } diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs index e4d6b94d..348ea1fa 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs @@ -29,7 +29,7 @@ private static Task OnExecute( HttpContext context, ILogger logger ) var detail = string.Format( CultureInfo.CurrentCulture, - CommonSR.MultipleDifferentApiVersionsRequested, + Format.MultipleDifferentApiVersionsRequested, string.Join( ", ", apiVersions ) ); return problemDetails.WriteAsync( diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/EndpointProblem.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/EndpointProblem.cs index bb64582f..250a9e43 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/EndpointProblem.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/EndpointProblem.cs @@ -52,7 +52,7 @@ internal static Task UnsupportedApiVersion( { var detail = string.Format( CultureInfo.CurrentCulture, - SR.VersionedResourceNotSupported, + Format.VersionedResourceNotSupported, new Uri( context.Request.GetDisplayUrl() ).SafeFullPath(), context.ApiVersioningFeature().RawRequestedApiVersion ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/MalformedApiVersionEndpoint.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/MalformedApiVersionEndpoint.cs index 568de2d2..4e4bf28d 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/MalformedApiVersionEndpoint.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/MalformedApiVersionEndpoint.cs @@ -29,7 +29,7 @@ private static Task OnExecute( HttpContext context, ILogger logger ) var detail = string.Format( CultureInfo.CurrentCulture, - SR.VersionedResourceNotSupported, + Format.VersionedResourceNotSupported, new Uri( context.Request.GetDisplayUrl() ).SafeFullPath(), requestedVersion ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ValidateApiVersioningOptions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ValidateApiVersioningOptions.cs index c07373fa..2abe453b 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ValidateApiVersioningOptions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ValidateApiVersioningOptions.cs @@ -27,7 +27,7 @@ public ValidateOptionsResult Validate( string? name, ApiVersioningOptions option { var message = string.Format( CultureInfo.CurrentCulture, - SR.InvalidDefaultApiVersion, + Format.InvalidDefaultApiVersion, nameof( ApiVersion ), nameof( ApiVersion.Neutral ), nameof( ApiVersioningOptions ), diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/MvcFormat.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/MvcFormat.cs new file mode 100644 index 00000000..97287926 --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/MvcFormat.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning; + +using System.Text; + +internal static class MvcFormat +{ + internal static readonly CompositeFormat ActionMethodNotFound = CompositeFormat.Parse( MvcSR.ActionMethodNotFound ); + internal static readonly CompositeFormat AmbiguousActionMethod = CompositeFormat.Parse( MvcSR.AmbiguousActionMethod ); + internal static readonly CompositeFormat MultipleApiVersionsInferredFromNamespaces = CompositeFormat.Parse( MvcSR.MultipleApiVersionsInferredFromNamespaces ); + internal static readonly CompositeFormat InvalidActionMethodExpression = CompositeFormat.Parse( MvcSR.InvalidActionMethodExpression ); + internal static readonly CompositeFormat ConventionStyleMismatch = CompositeFormat.Parse( MvcSR.ConventionStyleMismatch ); +} \ No newline at end of file diff --git a/src/Common/src/Common.Mvc/Conventions/ActionMethodResolver.cs b/src/Common/src/Common.Mvc/Conventions/ActionMethodResolver.cs index 367f4e8d..3c0fa420 100644 --- a/src/Common/src/Common.Mvc/Conventions/ActionMethodResolver.cs +++ b/src/Common/src/Common.Mvc/Conventions/ActionMethodResolver.cs @@ -20,7 +20,7 @@ internal static MethodInfo Resolve( Type controllerType, string methodName, Type switch ( methods.Length ) { case 0: - throw new MissingMethodException( string.Format( CultureInfo.CurrentCulture, MvcSR.ActionMethodNotFound, methodName ) ); + throw new MissingMethodException( string.Format( CultureInfo.CurrentCulture, MvcFormat.ActionMethodNotFound, methodName ) ); case 1: return methods[0]; } @@ -33,7 +33,7 @@ internal static MethodInfo Resolve( Type controllerType, string methodName, Type return methods[0]; } - throw new AmbiguousMatchException( string.Format( CultureInfo.CurrentCulture, MvcSR.AmbiguousActionMethod, methodName ) ); + throw new AmbiguousMatchException( string.Format( CultureInfo.CurrentCulture, MvcFormat.AmbiguousActionMethod, methodName ) ); } private static bool IsAction( MethodInfo method ) => diff --git a/src/Common/src/Common.Mvc/Conventions/ApiVersionConventionBuilder.cs b/src/Common/src/Common.Mvc/Conventions/ApiVersionConventionBuilder.cs index 477d6666..6a18e4f8 100644 --- a/src/Common/src/Common.Mvc/Conventions/ApiVersionConventionBuilder.cs +++ b/src/Common/src/Common.Mvc/Conventions/ApiVersionConventionBuilder.cs @@ -77,7 +77,7 @@ public virtual IControllerConventionBuilder<TController> Controller<TController> // this should only ever happen if a subclass overrides Controller(Type) and adds a // IControllerConventionBuilder that is not covariant with IControllerConventionBuilder<TController> - var message = string.Format( CultureInfo.CurrentCulture, MvcSR.ConventionStyleMismatch, key.Name ); + var message = string.Format( CultureInfo.CurrentCulture, MvcFormat.ConventionStyleMismatch, key.Name ); throw new InvalidOperationException( message ); } diff --git a/src/Common/src/Common.Mvc/Conventions/ExpressionExtensions.cs b/src/Common/src/Common.Mvc/Conventions/ExpressionExtensions.cs index c3211e2a..5f10baa1 100644 --- a/src/Common/src/Common.Mvc/Conventions/ExpressionExtensions.cs +++ b/src/Common/src/Common.Mvc/Conventions/ExpressionExtensions.cs @@ -15,7 +15,7 @@ internal static MethodInfo ExtractMethod<TDelegate>( this Expression<TDelegate> return methodCall.Method; } - var message = string.Format( CultureInfo.CurrentCulture, MvcSR.InvalidActionMethodExpression, expression ); + var message = string.Format( CultureInfo.CurrentCulture, MvcFormat.InvalidActionMethodExpression, expression ); throw new InvalidOperationException( message ); } } \ No newline at end of file diff --git a/src/Common/src/Common.Mvc/Conventions/VersionByNamespaceConvention.cs b/src/Common/src/Common.Mvc/Conventions/VersionByNamespaceConvention.cs index 64f42f90..28a96884 100644 --- a/src/Common/src/Common.Mvc/Conventions/VersionByNamespaceConvention.cs +++ b/src/Common/src/Common.Mvc/Conventions/VersionByNamespaceConvention.cs @@ -48,7 +48,7 @@ public virtual bool Apply( IControllerConventionBuilder builder, ControllerModel case 1: break; default: - var message = string.Format( CultureInfo.CurrentCulture, MvcSR.MultipleApiVersionsInferredFromNamespaces, type.Namespace ); + var message = string.Format( CultureInfo.CurrentCulture, MvcFormat.MultipleApiVersionsInferredFromNamespaces, type.Namespace ); throw new InvalidOperationException( message ); } diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs index f4d65d65..d13b6d75 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/DefaultODataQueryOptionDescriptionProvider.cs @@ -18,6 +18,11 @@ namespace Asp.Versioning.Conventions; using static Microsoft.AspNetCore.OData.Query.AllowedQueryOptions; #endif using static System.Globalization.CultureInfo; +#if NETFRAMEWORK +using Fmt = Asp.Versioning.ODataExpSR; +#else +using Fmt = Asp.Versioning.Format; +#endif /// <summary> /// Represents the default <see cref="IODataQueryOptionDescriptionProvider">OData query option description provider.</see>. @@ -51,7 +56,7 @@ public virtual string Describe( AllowedQueryOptions queryOption, ODataQueryOptio _ => throw new System.ArgumentException( string.Format( CurrentCulture, - ODataExpSR.UnsupportedQueryOption, + Fmt.UnsupportedQueryOption, #pragma warning disable IDE0079 #pragma warning disable CA1308 // Normalize strings to uppercase (proper casing is lowercase) queryOption.ToString().ToLowerInvariant() ), @@ -77,7 +82,7 @@ protected virtual string DescribeFilter( ODataQueryOptionDescriptionContext cont if ( context.MaxNodeCount > 1 ) { description.Append( Space ) - .AppendFormat( CurrentCulture, ODataExpSR.MaxExpressionDesc, context.MaxNodeCount ); + .AppendFormat( CurrentCulture, Fmt.MaxExpressionDesc, context.MaxNodeCount ); } AppendAllowedOptions( description, context ); @@ -87,7 +92,7 @@ protected virtual string DescribeFilter( ODataQueryOptionDescriptionContext cont description.Append( Space ) .AppendFormat( CurrentCulture, - ODataExpSR.AllowedPropertiesDesc, + Fmt.AllowedPropertiesDesc, string.Join( ", ", context.AllowedFilterProperties ) ); } @@ -116,7 +121,7 @@ protected virtual string DescribeExpand( ODataQueryOptionDescriptionContext cont if ( hasMaxExpansionDepth ) { description.Append( Space ) - .AppendFormat( CurrentCulture, ODataExpSR.MaxDepthDesc, context.MaxExpansionDepth ); + .AppendFormat( CurrentCulture, Fmt.MaxDepthDesc, context.MaxExpansionDepth ); } if ( context.AllowedExpandProperties.Count > 0 ) @@ -124,7 +129,7 @@ protected virtual string DescribeExpand( ODataQueryOptionDescriptionContext cont description.Append( Space ) .AppendFormat( CurrentCulture, - ODataExpSR.AllowedPropertiesDesc, + Fmt.AllowedPropertiesDesc, string.Join( ", ", context.AllowedExpandProperties ) ); } @@ -150,7 +155,7 @@ protected virtual string DescribeSelect( ODataQueryOptionDescriptionContext cont .Append( Space ) .AppendFormat( CurrentCulture, - ODataExpSR.AllowedPropertiesDesc, + Fmt.AllowedPropertiesDesc, string.Join( ", ", context.AllowedSelectProperties ) ) .ToString(); } @@ -177,7 +182,7 @@ protected virtual string DescribeOrderBy( ODataQueryOptionDescriptionContext con if ( hasMaxOrderByNodeCount ) { description.Append( Space ) - .AppendFormat( CurrentCulture, ODataExpSR.MaxExpressionDesc, context.MaxOrderByNodeCount ); + .AppendFormat( CurrentCulture, Fmt.MaxExpressionDesc, context.MaxOrderByNodeCount ); } if ( context.AllowedOrderByProperties.Count > 0 ) @@ -185,7 +190,7 @@ protected virtual string DescribeOrderBy( ODataQueryOptionDescriptionContext con description.Append( Space ) .AppendFormat( CurrentCulture, - ODataExpSR.AllowedPropertiesDesc, + Fmt.AllowedPropertiesDesc, string.Join( ", ", context.AllowedOrderByProperties ) ); } @@ -209,7 +214,7 @@ protected virtual string DescribeTop( ODataQueryOptionDescriptionContext context return GetOrCreateBuilder() .Append( ODataExpSR.TopQueryOptionDesc ) .Append( Space ) - .AppendFormat( CurrentCulture, ODataExpSR.MaxValueDesc, context.MaxTop ) + .AppendFormat( CurrentCulture, Fmt.MaxValueDesc, context.MaxTop ) .ToString(); } @@ -230,7 +235,7 @@ protected virtual string DescribeSkip( ODataQueryOptionDescriptionContext contex return GetOrCreateBuilder() .Append( ODataExpSR.SkipQueryOptionDesc ) .Append( Space ) - .AppendFormat( CurrentCulture, ODataExpSR.MaxValueDesc, context.MaxSkip ) + .AppendFormat( CurrentCulture, Fmt.MaxValueDesc, context.MaxSkip ) .ToString(); } @@ -249,7 +254,7 @@ private static void AppendAllowedOptions( StringBuilder description, ODataQueryO description.Append( Space ) .AppendFormat( CurrentCulture, - ODataExpSR.AllowedLogicalOperatorsDesc, + Fmt.AllowedLogicalOperatorsDesc, string.Join( ", ", EnumerateLogicalOperators( context.AllowedLogicalOperators ) ) ); @@ -261,7 +266,7 @@ private static void AppendAllowedOptions( StringBuilder description, ODataQueryO description.Append( Space ) .AppendFormat( CurrentCulture, - ODataExpSR.AllowedArithmeticOperatorsDesc, + Fmt.AllowedArithmeticOperatorsDesc, string.Join( ", ", EnumerateArithmeticOperators( context.AllowedArithmeticOperators ) ) ); @@ -275,7 +280,7 @@ private static void AppendAllowedOptions( StringBuilder description, ODataQueryO description.Append( Space ) .AppendFormat( CurrentCulture, - ODataExpSR.AllowedFunctionsDesc, + Fmt.AllowedFunctionsDesc, context.AllowedFunctions.ToString().ToLowerInvariant() ); #pragma warning restore CA1308 // Normalize strings to uppercase #pragma warning restore IDE0079 diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderExtensions.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderExtensions.cs index fb997141..bcd941a0 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderExtensions.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilderExtensions.cs @@ -204,7 +204,7 @@ public static ODataActionQueryOptionsConventionBuilder Action( switch ( methods.Length ) { case 0: - message = string.Format( CultureInfo.CurrentCulture, ODataExpSR.ActionMethodNotFound, methodName ); + message = string.Format( CultureInfo.CurrentCulture, Format.ActionMethodNotFound, methodName ); throw new MissingMethodException( message ); case 1: return builder.Action( methods[0] ); @@ -218,7 +218,7 @@ public static ODataActionQueryOptionsConventionBuilder Action( return builder.Action( methods[0] ); } - message = string.Format( CultureInfo.CurrentCulture, ODataExpSR.AmbiguousActionMethod, methodName ); + message = string.Format( CultureInfo.CurrentCulture, Format.AmbiguousActionMethod, methodName ); throw new AmbiguousMatchException( message ); } @@ -263,7 +263,7 @@ private static MethodInfo ExtractMethod<TDelegate>( this Expression<TDelegate> e return methodCall.Method; } - var message = string.Format( CultureInfo.CurrentCulture, ODataExpSR.InvalidActionMethodExpression, expression ); + var message = string.Format( CultureInfo.CurrentCulture, Format.InvalidActionMethodExpression, expression ); throw new InvalidOperationException( message ); } } \ No newline at end of file diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionsConventionBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionsConventionBuilder.cs index d400bc20..a22d953b 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionsConventionBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionsConventionBuilder.cs @@ -27,7 +27,7 @@ public ODataControllerQueryOptionsConventionBuilder( Type controllerType ) if ( !webApiController.IsAssignableFrom( controllerType ) ) { - var message = string.Format( CultureInfo.CurrentCulture, ODataExpSR.RequiredInterfaceNotImplemented, controllerType, webApiController ); + var message = string.Format( CultureInfo.CurrentCulture, Format.RequiredInterfaceNotImplemented, controllerType, webApiController ); throw new System.ArgumentException( message ); } #endif diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs index b41a4ff9..99c6a919 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs @@ -78,7 +78,7 @@ public virtual ODataControllerQueryOptionsConventionBuilder<TController> Control return typedBuilder; } - var message = string.Format( CultureInfo.CurrentCulture, ODataExpSR.ConventionStyleMismatch, key.Name ); + var message = string.Format( CultureInfo.CurrentCulture, Format.ConventionStyleMismatch, key.Name ); throw new InvalidOperationException( message ); } @@ -103,7 +103,7 @@ public virtual ODataControllerQueryOptionsConventionBuilder Controller( Type con return typedBuilder; } - var message = string.Format( CultureInfo.CurrentCulture, ODataExpSR.ConventionStyleMismatch, controllerType.Name ); + var message = string.Format( CultureInfo.CurrentCulture, Format.ConventionStyleMismatch, controllerType.Name ); throw new InvalidOperationException( message ); } diff --git a/src/Common/src/Common/ApiVersioningPolicyBuilder.cs b/src/Common/src/Common/ApiVersioningPolicyBuilder.cs index 6e571d21..2eb50a29 100644 --- a/src/Common/src/Common/ApiVersioningPolicyBuilder.cs +++ b/src/Common/src/Common/ApiVersioningPolicyBuilder.cs @@ -27,7 +27,7 @@ public virtual ISunsetPolicyBuilder Sunset( string? name, ApiVersion? apiVersion { if ( string.IsNullOrEmpty( name ) && apiVersion == null ) { - var message = string.Format( CultureInfo.CurrentCulture, CommonSR.InvalidPolicyKey, nameof( name ), nameof( apiVersion ) ); + var message = string.Format( CultureInfo.CurrentCulture, Format.InvalidPolicyKey, nameof( name ), nameof( apiVersion ) ); throw new System.ArgumentException( message ); } diff --git a/src/Common/src/Common/SunsetPolicyBuilder.cs b/src/Common/src/Common/SunsetPolicyBuilder.cs index e0d207d7..a6450b49 100644 --- a/src/Common/src/Common/SunsetPolicyBuilder.cs +++ b/src/Common/src/Common/SunsetPolicyBuilder.cs @@ -23,7 +23,7 @@ public SunsetPolicyBuilder( string? name, ApiVersion? apiVersion ) { if ( string.IsNullOrEmpty( name ) && apiVersion == null ) { - var message = string.Format( CultureInfo.CurrentCulture, CommonSR.InvalidPolicyKey, nameof( name ), nameof( apiVersion ) ); + var message = string.Format( CultureInfo.CurrentCulture, Format.InvalidPolicyKey, nameof( name ), nameof( apiVersion ) ); throw new System.ArgumentException( message ); } From 024c491b0b3808f92455e563d191719fffa96a04 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:23:07 -0800 Subject: [PATCH 073/131] Simplify internal types with default constructors --- .../IApiVersioningBuilderExtensions.cs | 7 +-- .../IApiVersioningBuilderExtensions.cs | 6 +-- .../Builder/VersionedEndpointRouteBuilder.cs | 44 ++++--------------- .../Routing/ApiVersionMatcherPolicy.cs | 30 ++++--------- .../DefaultApiVersionDescriptionProvider.cs | 16 +++---- .../GroupedApiVersionDescriptionProvider.cs | 16 +++---- .../VersionedApiDescriptionProvider.cs | 6 +-- .../ApiVersionWriter.cs | 7 +-- 8 files changed, 32 insertions(+), 100 deletions(-) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs index 159717b1..9304fe5d 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -60,12 +60,9 @@ private static void AddApiExplorerServices( IApiVersioningBuilder builder ) #pragma warning disable IDE0079 #pragma warning disable CA1812 - private sealed class ODataApiExplorerOptionsAdapter : IOptionsFactory<ApiExplorerOptions> + private sealed class ODataApiExplorerOptionsAdapter( IOptionsFactory<ODataApiExplorerOptions> factory ) + : IOptionsFactory<ApiExplorerOptions> { - private readonly IOptionsFactory<ODataApiExplorerOptions> factory; - - public ODataApiExplorerOptionsAdapter( IOptionsFactory<ODataApiExplorerOptions> factory ) => this.factory = factory; - public ApiExplorerOptions Create( string name ) => factory.Create( name ); } } \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs index 28f86621..8d90b695 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -161,12 +161,8 @@ IHttpContextFactory NewFactory( IServiceProvider serviceProvider ) return Describe( typeof( IHttpContextFactory ), NewFactory, lifetime ); } - private sealed class HttpContextFactoryDecorator : IHttpContextFactory + private sealed class HttpContextFactoryDecorator( IHttpContextFactory decorated ) : IHttpContextFactory { - private readonly IHttpContextFactory decorated; - - public HttpContextFactoryDecorator( IHttpContextFactory decorated ) => this.decorated = decorated; - public HttpContext Create( IFeatureCollection featureCollection ) { // features do not support cloning or DI, which is precisely why ASP.NET Core no longer supports diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/VersionedEndpointRouteBuilder.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/VersionedEndpointRouteBuilder.cs index c1773a6c..36af4a92 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/VersionedEndpointRouteBuilder.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/VersionedEndpointRouteBuilder.cs @@ -59,20 +59,12 @@ public virtual IApplicationBuilder CreateApplicationBuilder() => public virtual void Add( Action<EndpointBuilder> convention ) => conventionBuilder.Add( convention ); - private sealed class ServiceProviderDecorator : IServiceProvider + private sealed class ServiceProviderDecorator( + IServiceProvider decorated, + ApiVersionSetBuilder versionSetBuilder ) : IServiceProvider { - private readonly IServiceProvider decorated; - private readonly ApiVersionSetBuilder versionSetBuilder; private ApiVersionSet? versionSet; - internal ServiceProviderDecorator( - IServiceProvider decorated, - ApiVersionSetBuilder versionSetBuilder ) - { - this.decorated = decorated; - this.versionSetBuilder = versionSetBuilder; - } - public object? GetService( Type serviceType ) { if ( typeof( ApiVersionSetBuilder ).Equals( serviceType ) ) @@ -89,19 +81,10 @@ internal ServiceProviderDecorator( } } - private sealed class EndpointDataSourceDecorator : EndpointDataSource - { - private readonly EndpointDataSource decorated; - private readonly ApiVersionSetBuilder versionSetBuilder; - - internal EndpointDataSourceDecorator( + private sealed class EndpointDataSourceDecorator( EndpointDataSource decorated, - ApiVersionSetBuilder versionSetBuilder ) - { - this.decorated = decorated; - this.versionSetBuilder = versionSetBuilder; - } - + ApiVersionSetBuilder versionSetBuilder ) : EndpointDataSource + { public override IReadOnlyList<Endpoint> Endpoints => decorated.Endpoints; public override IChangeToken GetChangeToken() => decorated.GetChangeToken(); @@ -175,19 +158,10 @@ private void CollateGroupApiVersions() } } - private sealed class EndpointDataSourceCollectionAdapter : ICollection<EndpointDataSource> - { - private readonly ICollection<EndpointDataSource> adapted; - private readonly ApiVersionSetBuilder versionSetBuilder; - - internal EndpointDataSourceCollectionAdapter( + private sealed class EndpointDataSourceCollectionAdapter( ICollection<EndpointDataSource> adapted, - ApiVersionSetBuilder versionSetBuilder ) - { - this.adapted = adapted; - this.versionSetBuilder = versionSetBuilder; - } - + ApiVersionSetBuilder versionSetBuilder ) : ICollection<EndpointDataSource> + { public int Count => adapted.Count; public bool IsReadOnly => adapted.IsReadOnly; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs index 285a65c1..dcae033d 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs @@ -479,18 +479,11 @@ private ApiVersion TrySelectApiVersion( HttpContext httpContext, CandidateSet ca bool INodeBuilderPolicy.AppliesToEndpoints( IReadOnlyList<Endpoint> endpoints ) => !ContainsDynamicEndpoints( endpoints ) && AppliesToEndpoints( endpoints ); - private readonly struct Match + private readonly struct Match( int index, int score, bool isExplicit ) { - internal readonly int Index; - internal readonly int Score; - internal readonly bool IsExplicit; - - internal Match( int index, int score, bool isExplicit ) - { - Index = index; - Score = score; - IsExplicit = isExplicit; - } + internal readonly int Index = index; + internal readonly int Score = score; + internal readonly bool IsExplicit = isExplicit; internal int CompareTo( in Match other ) { @@ -499,22 +492,15 @@ internal int CompareTo( in Match other ) } } - private sealed class ApiVersionCollator + private sealed class ApiVersionCollator( + IEnumerable<IApiVersionMetadataCollationProvider> providers, + IOptions<ApiVersioningOptions> options ) { - private readonly IApiVersionMetadataCollationProvider[] providers; - private readonly IOptions<ApiVersioningOptions> options; + private readonly IApiVersionMetadataCollationProvider[] providers = providers.ToArray(); private readonly object syncRoot = new(); private IReadOnlyList<ApiVersion>? items; private int version; - internal ApiVersionCollator( - IEnumerable<IApiVersionMetadataCollationProvider> providers, - IOptions<ApiVersioningOptions> options ) - { - this.providers = providers.ToArray(); - this.options = options; - } - public IReadOnlyList<ApiVersion> Items { get diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs index 3b01026b..07f65bfd 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs @@ -125,22 +125,16 @@ private void AppendDescriptions( ICollection<ApiVersionDescription> descriptions } } - private sealed class ApiVersionDescriptionCollection + private sealed class ApiVersionDescriptionCollection( + DefaultApiVersionDescriptionProvider provider, + IEnumerable<IApiVersionMetadataCollationProvider> collators ) { private readonly object syncRoot = new(); - private readonly DefaultApiVersionDescriptionProvider provider; - private readonly IApiVersionMetadataCollationProvider[] collators; + private readonly DefaultApiVersionDescriptionProvider provider = provider; + private readonly IApiVersionMetadataCollationProvider[] collators = collators.ToArray(); private IReadOnlyList<ApiVersionDescription>? items; private int version; - public ApiVersionDescriptionCollection( - DefaultApiVersionDescriptionProvider provider, - IEnumerable<IApiVersionMetadataCollationProvider> collators ) - { - this.provider = provider; - this.collators = collators.ToArray(); - } - public IReadOnlyList<ApiVersionDescription> Items { get diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs index 7af19256..ca31264b 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs @@ -143,22 +143,16 @@ private void AppendDescriptions( } } - private sealed class ApiVersionDescriptionCollection + private sealed class ApiVersionDescriptionCollection( + GroupedApiVersionDescriptionProvider provider, + IEnumerable<IApiVersionMetadataCollationProvider> collators ) { private readonly object syncRoot = new(); - private readonly GroupedApiVersionDescriptionProvider provider; - private readonly IApiVersionMetadataCollationProvider[] collators; + private readonly GroupedApiVersionDescriptionProvider provider = provider; + private readonly IApiVersionMetadataCollationProvider[] collators = collators.ToArray(); private IReadOnlyList<ApiVersionDescription>? items; private int version; - public ApiVersionDescriptionCollection( - GroupedApiVersionDescriptionProvider provider, - IEnumerable<IApiVersionMetadataCollationProvider> collators ) - { - this.provider = provider; - this.collators = collators.ToArray(); - } - public IReadOnlyList<ApiVersionDescription> Items { get diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs index d914fab7..e1f22177 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/VersionedApiDescriptionProvider.cs @@ -320,12 +320,8 @@ private ApiVersion[] FlattenApiVersions( IList<ApiDescription> descriptions ) return [.. versions]; } - private sealed class SimpleConstraintResolver : IInlineConstraintResolver + private sealed class SimpleConstraintResolver( IOptions<ApiExplorerOptions> options ) : IInlineConstraintResolver { - private readonly IOptions<ApiExplorerOptions> options; - - internal SimpleConstraintResolver( IOptions<ApiExplorerOptions> options ) => this.options = options; - public IRouteConstraint? ResolveConstraint( string inlineConstraint ) { if ( options.Value.RouteConstraintName == inlineConstraint ) diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionWriter.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionWriter.cs index 25e03fc9..c56f2b97 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionWriter.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionWriter.cs @@ -55,13 +55,8 @@ public static IApiVersionWriter Combine( IEnumerable<IApiVersionWriter> apiVersi return new CombinedApiVersionWriter( writers ); } - private sealed class CombinedApiVersionWriter : IApiVersionWriter + private sealed class CombinedApiVersionWriter( IApiVersionWriter[] apiVersionWriters ) : IApiVersionWriter { - private readonly IApiVersionWriter[] apiVersionWriters; - - public CombinedApiVersionWriter( IApiVersionWriter[] apiVersionWriters ) => - this.apiVersionWriters = apiVersionWriters; - public void Write( HttpRequestMessage request, ApiVersion apiVersion ) { for ( var i = 0; i < apiVersionWriters.Length; i++ ) From 1dd82ad48d4ae5c50b53de8793801e8aa12e3692 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:31:29 -0800 Subject: [PATCH 074/131] Prefer concrete types for better performance --- .../ApiExplorer/ODataApiDescriptionProvider.cs | 4 ++-- .../ODataQueryOptionsConventionBuilder.cs | 5 +++-- .../OData/Batch/ODataBatchPathMapping.cs | 2 +- .../ODataControllerSpecificationTest.cs | 6 +++--- .../DefaultApiVersionDescriptionProvider.cs | 4 ++-- .../Routing/ApiVersionRouteConstraintTest.cs | 4 ++-- .../TestActionDescriptorCollectionProvider.cs | 4 ++-- .../ApiBehaviorSpecificationTest.cs | 4 ++-- .../ReportApiVersionsAttributeTest.cs | 2 +- .../IHttpClientBuilderExtensions.cs | 7 ++----- .../ODataControllerQueryOptionConvention.cs | 4 ++-- .../OData/DefaultModelTypeBuilder.cs | 14 +++++++------- .../Conventions/ApiVersionConventionBuilderTest.cs | 6 +++--- .../ControllerApiVersionConventionBuilderTTest.cs | 6 +++--- .../ControllerApiVersionConventionBuilderTest.cs | 6 +++--- 15 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs index 06ad7201..5736e8f4 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs @@ -198,7 +198,7 @@ private static bool IsNavigationPropertyLink( ODataPathTemplate template ) => private static bool TryMatchModelVersion( ApiDescription description, - IReadOnlyList<IODataRoutingMetadata> items, + IODataRoutingMetadata[] items, [NotNullWhen( true )] out IODataRoutingMetadata? metadata ) { if ( description.GetApiVersion() is not ApiVersion apiVersion ) @@ -215,7 +215,7 @@ private static bool TryMatchModelVersion( return false; } - for ( var i = 0; i < items.Count; i++ ) + for ( var i = 0; i < items.Length; i++ ) { var item = items[i]; var otherApiVersion = item.Model.GetApiVersion(); diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs index 6986e816..3c876e07 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs @@ -4,6 +4,7 @@ namespace Asp.Versioning.Conventions; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Controllers; +using System.Reflection; /// <content> /// Provides additional implementation specific to Microsoft ASP.NET Core. @@ -11,13 +12,13 @@ namespace Asp.Versioning.Conventions; [CLSCompliant( false )] public partial class ODataQueryOptionsConventionBuilder { - private static Type GetController( ApiDescription apiDescription ) + private static TypeInfo GetController( ApiDescription apiDescription ) { if ( apiDescription.ActionDescriptor is ControllerActionDescriptor action ) { return action.ControllerTypeInfo; } - return typeof( object ); + return typeof( object ).GetTypeInfo(); } } \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs index c3d8948a..e009a930 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs @@ -99,7 +99,7 @@ private static void MergeRouteData( HttpContext context, RouteValueDictionary ro private ODataBatchHandler? SelectBestCandidate( HttpContext context, ref PathString path, - IReadOnlyDictionary<ApiVersion, int> candidates, + Dictionary<ApiVersion, int> candidates, RouteValueDictionary routeData ) { if ( candidates.Count == 0 ) diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/ApplicationModels/ODataControllerSpecificationTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/ApplicationModels/ODataControllerSpecificationTest.cs index 9176a07e..889d3b34 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/ApplicationModels/ODataControllerSpecificationTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/ApplicationModels/ODataControllerSpecificationTest.cs @@ -35,20 +35,20 @@ public void is_satisfied_by_should_return_expected_value( Type controllerType, b private sealed class NormalODataController : ODataController { [EnableQuery] - public IActionResult Get() => Ok(); + public OkResult Get() => Ok(); } [ODataAttributeRouting] private sealed class CustomODataController : ControllerBase { [EnableQuery] - public IActionResult Get() => Ok(); + public OkResult Get() => Ok(); } [Route( "api/test" )] private sealed class NonODataController : ControllerBase { [HttpGet] - public IActionResult Get() => Ok(); + public OkResult Get() => Ok(); } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs index 07f65bfd..63408cad 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs @@ -69,7 +69,7 @@ protected virtual IReadOnlyList<ApiVersionDescription> Describe( IReadOnlyList<A return descriptions.OrderBy( d => d.ApiVersion ).ToArray(); } - private void BucketizeApiVersions( IReadOnlyList<ApiVersionMetadata> metadata, ISet<ApiVersion> supported, ISet<ApiVersion> deprecated ) + private void BucketizeApiVersions( IReadOnlyList<ApiVersionMetadata> metadata, HashSet<ApiVersion> supported, HashSet<ApiVersion> deprecated ) { var declared = new HashSet<ApiVersion>(); var advertisedSupported = new HashSet<ApiVersion>(); @@ -115,7 +115,7 @@ private void BucketizeApiVersions( IReadOnlyList<ApiVersionMetadata> metadata, I } } - private void AppendDescriptions( ICollection<ApiVersionDescription> descriptions, IEnumerable<ApiVersion> versions, bool deprecated ) + private void AppendDescriptions( List<ApiVersionDescription> descriptions, IEnumerable<ApiVersion> versions, bool deprecated ) { foreach ( var version in versions ) { diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Routing/ApiVersionRouteConstraintTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Routing/ApiVersionRouteConstraintTest.cs index d522f4f4..16084847 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Routing/ApiVersionRouteConstraintTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Routing/ApiVersionRouteConstraintTest.cs @@ -163,11 +163,11 @@ private static HttpContext NewHttpContext() return httpContext.Object; } - private static IRouteBuilder CreateRouteBuilder( IServiceProvider services ) + private static RouteBuilder CreateRouteBuilder( IServiceProvider services ) { var app = new Mock<IApplicationBuilder>(); app.SetupGet( a => a.ApplicationServices ).Returns( services ); - return new RouteBuilder( app.Object ) { DefaultHandler = new PassThroughRouter() }; + return new( app.Object ) { DefaultHandler = new PassThroughRouter() }; } private static IUrlHelper NewUrlHelper( string controller, string action, string version ) diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestActionDescriptorCollectionProvider.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestActionDescriptorCollectionProvider.cs index be8ff384..efe83dea 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestActionDescriptorCollectionProvider.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestActionDescriptorCollectionProvider.cs @@ -41,7 +41,7 @@ private static ActionDescriptorCollection CreateActionDescriptors() return new( actions.ToArray(), 0 ); } - private static void AddOrderActionDescriptors( ICollection<ActionDescriptor> actions ) + private static void AddOrderActionDescriptors( List<ActionDescriptor> actions ) { // api version 0.9 and 1.0 actions.Add( @@ -106,7 +106,7 @@ private static void AddOrderActionDescriptors( ICollection<ActionDescriptor> act advertised: new ApiVersion[] { new( 4, 0 ) } ) ); } - private static void AddPeopleActionDescriptors( ICollection<ActionDescriptor> actions ) + private static void AddPeopleActionDescriptors( List<ActionDescriptor> actions ) { // api version 0.9 and 1.0 actions.Add( diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApplicationModels/ApiBehaviorSpecificationTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApplicationModels/ApiBehaviorSpecificationTest.cs index 418dfa5b..336a7ba1 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApplicationModels/ApiBehaviorSpecificationTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApplicationModels/ApiBehaviorSpecificationTest.cs @@ -33,13 +33,13 @@ public void is_satisfied_by_should_return_expected_result( Type controllerType, private sealed class ApiBehaviorController : ControllerBase { [HttpGet] - public IActionResult Get() => Ok(); + public OkResult Get() => Ok(); } [Route( "/" )] private sealed class NonApiBehaviorController : Controller { [HttpGet] - public IActionResult Index() => View(); + public ViewResult Index() => View(); } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs index a79476ef..894d841f 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs @@ -66,7 +66,7 @@ public async Task on_action_executing_should_not_add_headers_for_versionX2Dneutr private static ActionExecutingContext CreateContext( ApiVersionMetadata metadata, - ICollection<(Func<object, Task> Callback, object State)> onStartResponse ) + List<(Func<object, Task> Callback, object State)> onStartResponse ) { var headers = new HeaderDictionary(); var response = new Mock<HttpResponse>(); diff --git a/src/Client/src/Asp.Versioning.Http.Client/net#.0/DependencyInjection/IHttpClientBuilderExtensions.cs b/src/Client/src/Asp.Versioning.Http.Client/net#.0/DependencyInjection/IHttpClientBuilderExtensions.cs index 62195d91..08f24e42 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/net#.0/DependencyInjection/IHttpClientBuilderExtensions.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/net#.0/DependencyInjection/IHttpClientBuilderExtensions.cs @@ -128,7 +128,7 @@ private static ApiVersionHandler NewApiVersionHandler( return new( writer, apiVersion, notification, parser ); } - private static IApiNotification? BuildFallbackNotification( + private static ApiVersionHandlerLogger<ApiVersionHandler>? BuildFallbackNotification( IServiceProvider serviceProvider, IApiVersionParser? parser ) { @@ -142,9 +142,6 @@ private static ApiVersionHandler NewApiVersionHandler( var enumerable = serviceProvider.GetService<ApiVersionHeaderEnumerable>(); - return new ApiVersionHandlerLogger<ApiVersionHandler>( - logger, - parser ?? ApiVersionParser.Default, - enumerable ?? new() ); + return new( logger, parser ?? ApiVersionParser.Default, enumerable ?? new() ); } } \ No newline at end of file diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionConvention.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionConvention.cs index 32e8a620..40077d14 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionConvention.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionConvention.cs @@ -41,7 +41,7 @@ public void ApplyTo( ApiDescription apiDescription ) convention!.ApplyTo( apiDescription ); } - private static IODataQueryOptionsConvention ImplicitActionConvention( ODataQueryOptionSettings settings ) + private static ODataValidationSettingsConvention ImplicitActionConvention( ODataQueryOptionSettings settings ) { var validationSettings = new ODataValidationSettings() { @@ -51,6 +51,6 @@ private static IODataQueryOptionsConvention ImplicitActionConvention( ODataQuery AllowedQueryOptions = AllowedQueryOptions.None, }; - return new ODataValidationSettingsConvention( validationSettings, settings ); + return new( validationSettings, settings ); } } \ No newline at end of file diff --git a/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs index dc4e8c1d..ce5a1010 100644 --- a/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/OData/DefaultModelTypeBuilder.cs @@ -120,7 +120,7 @@ public Type NewActionParameters( IEdmModel model, IEdmAction action, string cont return type; } - private IDictionary<EdmTypeKey, Type> GenerateTypesForEdmModel( IEdmModel model, ApiVersion apiVersion ) + private Dictionary<EdmTypeKey, Type> GenerateTypesForEdmModel( IEdmModel model, ApiVersion apiVersion ) { ModuleBuilder NewModuleBuilder() => ( modules ??= new() ).GetOrAdd( new( model, apiVersion ), CreateModuleForApiVersion ); @@ -195,8 +195,8 @@ private static Type GenerateTypeIfNeeded( IEdmStructuredType structuredType, Bui private static Tuple<bool, bool> BuildSignatureProperties( Type clrType, - IReadOnlyDictionary<string, IEdmProperty> structuralProperties, - IReadOnlyDictionary<PropertyInfo, IEdmProperty> mappedClrProperties, + Dictionary<string, IEdmProperty> structuralProperties, + Dictionary<PropertyInfo, IEdmProperty> mappedClrProperties, List<ClassProperty> properties, List<PropertyDependency> dependentProperties, BuilderContext context ) @@ -370,7 +370,7 @@ private static TypeBuilder CreateTypeBuilderFromSignature( ModuleBuilder moduleB return typeBuilder; } - private static IDictionary<EdmTypeKey, Type> ResolveDependencies( BuilderContext context ) + private static Dictionary<EdmTypeKey, Type> ResolveDependencies( BuilderContext context ) { var edmTypes = context.EdmTypes; @@ -520,11 +520,11 @@ internal BuilderContext( IEdmModel edmModel, ApiVersion apiVersion, Func<ModuleB internal IEdmModel EdmModel { get; } - internal IDictionary<EdmTypeKey, Type> EdmTypes { get; } = new Dictionary<EdmTypeKey, Type>(); + internal Dictionary<EdmTypeKey, Type> EdmTypes { get; } = []; - internal ISet<EdmTypeKey> VisitedEdmTypes => visitedEdmTypes ??= new(); + internal HashSet<EdmTypeKey> VisitedEdmTypes => visitedEdmTypes ??= []; - internal IList<PropertyDependency> Dependencies => dependencies ??= new(); + internal List<PropertyDependency> Dependencies => dependencies ??= []; internal bool HasDependencies => dependencies != null && dependencies.Count > 0; } diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs index 17bd1849..0a4b5fdd 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs @@ -4,9 +4,9 @@ namespace Asp.Versioning.Conventions { #if NETFRAMEWORK using System.Web.Http; + using System.Web.Http.Results; using ControllerBase = System.Web.Http.ApiController; using ControllerModel = System.Web.Http.Controllers.HttpControllerDescriptor; - using IActionResult = System.Web.Http.IHttpActionResult; #else using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; @@ -126,7 +126,7 @@ private sealed class TestApiVersionConventionBuilder : ApiVersionConventionBuild private sealed class StubController : ControllerBase { - public IActionResult Get() => Ok(); + public OkResult Get() => Ok(); } } @@ -139,7 +139,7 @@ namespace v2 #endif internal sealed class UndecoratedController : ControllerBase { - public IActionResult Get() => Ok(); + public OkResult Get() => Ok(); } } } \ No newline at end of file diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs index 237e600b..47bc0f7a 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs @@ -3,8 +3,8 @@ namespace Asp.Versioning.Conventions; #if NETFRAMEWORK +using System.Web.Http.Results; using ControllerBase = System.Web.Http.ApiController; -using IActionResult = System.Web.Http.IHttpActionResult; #else using Microsoft.AspNetCore.Mvc; #endif @@ -81,7 +81,7 @@ private sealed class TestControllerApiVersionConventionBuilder : ControllerApiVe #endif private sealed class UndecoratedController : ControllerBase { - public IActionResult Get() => Ok(); + public OkResult Get() => Ok(); } #if !NETFRAMEWORK @@ -93,6 +93,6 @@ private sealed class UndecoratedController : ControllerBase [AdvertiseApiVersions( "3.0-Beta", Deprecated = true )] private sealed class DecoratedController : ControllerBase { - public IActionResult Get() => Ok(); + public OkResult Get() => Ok(); } } \ No newline at end of file diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs index 54f3cbdb..7c40b893 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTest.cs @@ -3,8 +3,8 @@ namespace Asp.Versioning.Conventions; #if NETFRAMEWORK +using System.Web.Http.Results; using ControllerBase = System.Web.Http.ApiController; -using IActionResult = System.Web.Http.IHttpActionResult; #else using Microsoft.AspNetCore.Mvc; #endif @@ -86,7 +86,7 @@ internal TestControllerApiVersionConventionBuilder( Type controllerType ) : base #endif private sealed class UndecoratedController : ControllerBase { - public IActionResult Get() => Ok(); + public OkResult Get() => Ok(); } #if !NETFRAMEWORK @@ -98,6 +98,6 @@ private sealed class UndecoratedController : ControllerBase [AdvertiseApiVersions( "3.0-Beta", Deprecated = true )] private sealed class DecoratedController : ControllerBase { - public IActionResult Get() => Ok(); + public OkResult Get() => Ok(); } } \ No newline at end of file From 343e6586dc7ad35efde035dec74706a794aa4102 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:32:08 -0800 Subject: [PATCH 075/131] Refactor with multiple Minimal API configurations --- .../Http/MinimalApiFixture.cs | 72 +++++++------------ 1 file changed, 25 insertions(+), 47 deletions(-) diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs index b568ff84..20789427 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs @@ -1,5 +1,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 // Remove unnecessary suppression +#pragma warning disable ASP0018 // Unused route parameter + namespace Asp.Versioning.Http; using Asp.Versioning.Conventions; @@ -11,24 +14,16 @@ public class MinimalApiFixture : HttpServerFixture { protected override void OnConfigureEndpoints( IEndpointRouteBuilder endpoints ) { + endpoints.MapGet( "api/ping", () => Results.NoContent() ) + .WithApiVersionSet( endpoints.NewApiVersionSet().Build() ) + .IsApiVersionNeutral(); + var values = endpoints.NewApiVersionSet( "Values" ) .HasApiVersion( 1.0 ) .HasApiVersion( 2.0 ) .ReportApiVersions() .Build(); - var helloWorld = endpoints.NewApiVersionSet( "Hello World" ) - .HasApiVersion( 1.0 ) - .HasApiVersion( 2.0 ) - .ReportApiVersions() - .Build(); - - var orders = endpoints.NewApiVersionSet( "Orders" ).Build(); - - endpoints.MapGet( "api/ping", () => Results.NoContent() ) - .WithApiVersionSet( endpoints.NewApiVersionSet().Build() ) - .IsApiVersionNeutral(); - endpoints.MapGet( "api/values", () => "Value 1" ) .WithApiVersionSet( values ) .MapToApiVersion( 1.0 ); @@ -37,44 +32,27 @@ protected override void OnConfigureEndpoints( IEndpointRouteBuilder endpoints ) .WithApiVersionSet( values ) .MapToApiVersion( 2.0 ); - endpoints.MapGet( "api/v{version:apiVersion}/hello", () => "Hello world!" ) - .WithApiVersionSet( helloWorld ) - .MapToApiVersion( 1.0 ); - - endpoints.MapGet( "api/v{version:apiVersion}/hello/{text}", ( string text ) => text ) - .WithApiVersionSet( helloWorld ) - .MapToApiVersion( 1.0 ); - - endpoints.MapGet( "api/v{version:apiVersion}/hello", () => "Hello world! (v2)" ) - .WithApiVersionSet( helloWorld ) - .MapToApiVersion( 2.0 ); - - endpoints.MapGet( "api/v{version:apiVersion}/hello/{text}", ( string text ) => text + " (v2)" ) - .WithApiVersionSet( helloWorld ) - .MapToApiVersion( 2.0 ); - - endpoints.MapPost( "api/v{version:apiVersion}/hello", () => { } ) - .WithApiVersionSet( helloWorld ); - - endpoints.MapGet( "api/order", () => { } ) - .WithApiVersionSet( orders ) - .HasApiVersion( 1.0 ) - .HasApiVersion( 2.0 ); + var orders = endpoints.NewVersionedApi( "Orders" ) + .MapGroup( "api/order" ) + .HasApiVersion( 1.0 ) + .HasApiVersion( 2.0 ); - endpoints.MapGet( "api/order/{id}", ( int id ) => { } ) - .WithApiVersionSet( orders ) - .HasDeprecatedApiVersion( 0.9 ) - .HasApiVersion( 1.0 ) - .HasApiVersion( 2.0 ); + orders.MapGet( "/", () => Results.Ok() ); + orders.MapGet( "/{id}", ( int id ) => Results.Ok() ).HasDeprecatedApiVersion( 0.9 ); + orders.MapPost( "/", () => Results.Created() ); + orders.MapDelete( "/{id}", ( int id ) => Results.NoContent() ).IsApiVersionNeutral(); - endpoints.MapPost( "api/order", () => { } ) - .WithApiVersionSet( orders ) - .HasApiVersion( 1.0 ) - .HasApiVersion( 2.0 ); + var helloWorld = endpoints.NewVersionedApi( "Orders" ) + .MapGroup( "api/v{version:apiVersion}/hello" ) + .HasApiVersion( 1.0 ) + .HasApiVersion( 2.0 ) + .ReportApiVersions(); - endpoints.MapDelete( "api/order/{id}", ( int id ) => { } ) - .WithApiVersionSet( orders ) - .IsApiVersionNeutral(); + helloWorld.MapGet( "/", () => "Hello world!" ).MapToApiVersion( 1.0 ); + helloWorld.MapGet( "/{text}", ( string text ) => text ).MapToApiVersion( 1.0 ); + helloWorld.MapGet( "/", () => "Hello world! (v2)" ).MapToApiVersion( 2.0 ); + helloWorld.MapGet( "/{text}", ( string text ) => text + " (v2)" ).MapToApiVersion( 2.0 ); + helloWorld.MapPost( "/", () => { } ); } protected override void OnAddApiVersioning( ApiVersioningOptions options ) From ea53cebebd4beb60107ebb97ba9f2ab13ce27f3e Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:34:24 -0800 Subject: [PATCH 076/131] Refactor to recommended header usage --- .../DefaultApiVersionReporter.cs | 4 ++-- .../Http/HttpResponseExtensions.cs | 13 ++----------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DefaultApiVersionReporter.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DefaultApiVersionReporter.cs index d495c8f6..94ecb388 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DefaultApiVersionReporter.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DefaultApiVersionReporter.cs @@ -21,7 +21,7 @@ private static void AddApiVersionHeader( IHeaderDictionary headers, string heade if ( versions.Count == 1 ) { - headers.Add( headerName, versions[0].ToString() ); + headers[headerName] = versions[0].ToString(); return; } @@ -55,7 +55,7 @@ private static void AddApiVersionHeader( IHeaderDictionary headers, string heade } } - headers.Add( headerName, headerValue.ToString() ); + headers[headerName] = headerValue.ToString(); pool.Return( array ); } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpResponseExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpResponseExtensions.cs index bd0ffefc..9b8ed20a 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpResponseExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpResponseExtensions.cs @@ -38,7 +38,7 @@ public static void WriteSunsetPolicy( this HttpResponse response, SunsetPolicy s if ( sunsetPolicy.Date.HasValue ) { - headers.Add( Sunset, sunsetPolicy.Date.Value.ToString( "r" ) ); + headers[Sunset] = sunsetPolicy.Date.Value.ToString( "r" ); } AddLinkHeaders( headers, sunsetPolicy.Links ); @@ -53,16 +53,7 @@ private static void AddLinkHeaders( IHeaderDictionary headers, IList<LinkHeaderV values[i] = links[i].ToString(); } - var newValues = new StringValues( values ); - - if ( headers.TryGetValue( Link, out var existingValues ) ) - { - headers[Link] = StringValues.Concat( existingValues, newValues ); - } - else - { - headers.Add( Link, newValues ); - } + headers.Append( Link, values ); } /// <summary> From 073cdca363211c8137b3c86dc92573211cce559b Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:34:47 -0800 Subject: [PATCH 077/131] Use nameof --- .../Asp.Versioning.Mvc/Routing/WithoutApiVersionUrlHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/WithoutApiVersionUrlHelper.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/WithoutApiVersionUrlHelper.cs index 72caa212..1496708b 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/WithoutApiVersionUrlHelper.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Routing/WithoutApiVersionUrlHelper.cs @@ -28,7 +28,7 @@ internal sealed class WithoutApiVersionUrlHelper : IUrlHelper return decorated.Action( actionContext ); } - [return: NotNullIfNotNull( "contentPath" )] + [return: NotNullIfNotNull( nameof( contentPath ) )] public string? Content( string? contentPath ) { if ( Feature is IApiVersioningFeature feature ) From e0cf03f58a35fbfa562e723abdf4d23cce197a27 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:35:10 -0800 Subject: [PATCH 078/131] Correct casing --- .../src/Asp.Versioning.Http.Client/ApiVersionWriter.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionWriter.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionWriter.cs index c56f2b97..09beac30 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionWriter.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionWriter.cs @@ -11,27 +11,27 @@ public static class ApiVersionWriter /// Returns a new API version writer that is a combination of the specified set. /// </summary> /// <param name="apiVersionWriter">The primary <see cref="IApiVersionWriter">API version writer</see>.</param> - /// <param name="otherApiVersionwriters">An array of the other + /// <param name="otherApiVersionWriters">An array of the other /// <see cref="IApiVersionWriter">API version writers</see> to combine.</param> /// <returns>A new, combined <see cref="IApiVersionWriter">API version writer</see>.</returns> public static IApiVersionWriter Combine( IApiVersionWriter apiVersionWriter, - params IApiVersionWriter[] otherApiVersionwriters ) + params IApiVersionWriter[] otherApiVersionWriters ) { ArgumentNullException.ThrowIfNull( apiVersionWriter ); int count; IApiVersionWriter[] apiVersionWriters; - if ( otherApiVersionwriters is null || ( count = otherApiVersionwriters.Length ) == 0 ) + if ( otherApiVersionWriters is null || ( count = otherApiVersionWriters.Length ) == 0 ) { - apiVersionWriters = new[] { apiVersionWriter }; + apiVersionWriters = [apiVersionWriter]; } else { apiVersionWriters = new IApiVersionWriter[count + 1]; apiVersionWriters[0] = apiVersionWriter; - System.Array.Copy( otherApiVersionwriters, 0, apiVersionWriters, 1, count ); + System.Array.Copy( otherApiVersionWriters, 0, apiVersionWriters, 1, count ); } return new CombinedApiVersionWriter( apiVersionWriters ); From 7b053403a66bbb4e1967d64093da4270ae572180 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:35:55 -0800 Subject: [PATCH 079/131] Avoid unnecessary initialization --- .../IApiVersioningBuilderExtensions.cs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs index e2a8fd68..7796ca96 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -14,6 +14,7 @@ namespace Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; +using System.Runtime.CompilerServices; using static Microsoft.Extensions.DependencyInjection.ServiceDescriptor; /// <summary> @@ -83,19 +84,25 @@ private static object CreateInstance( this IServiceProvider services, ServiceDes return ActivatorUtilities.GetServiceOrCreateInstance( services, descriptor.ImplementationType! ); } - private static ServiceDescriptor WithUrlHelperFactoryDecorator( IServiceCollection services ) + [SkipLocalsInit] + private static DecoratedServiceDescriptor WithUrlHelperFactoryDecorator( IServiceCollection services ) { var descriptor = services.FirstOrDefault( sd => sd.ServiceType == typeof( IUrlHelperFactory ) ); - if ( descriptor is DecoratedServiceDescriptor ) + if ( descriptor is DecoratedServiceDescriptor sd ) { - return descriptor; + return sd; } - var lifetime = ServiceLifetime.Singleton; - Func<IServiceProvider, object> instantiate = sp => new UrlHelperFactory(); + ServiceLifetime lifetime; + Func<IServiceProvider, object> instantiate; - if ( descriptor != null ) + if ( descriptor == null ) + { + lifetime = ServiceLifetime.Singleton; + instantiate = static sp => new UrlHelperFactory(); + } + else { lifetime = descriptor.Lifetime; instantiate = sp => sp.CreateInstance( descriptor ); @@ -116,7 +123,7 @@ IUrlHelperFactory NewFactory( IServiceProvider serviceProvider ) return (IUrlHelperFactory) instance; } - return new DecoratedServiceDescriptor( typeof( IUrlHelperFactory ), NewFactory, lifetime ); + return new( typeof( IUrlHelperFactory ), NewFactory, lifetime ); } private sealed class DecoratedServiceDescriptor : ServiceDescriptor From f92814a3c58fcf975f6c8b228a2fa970f469d75c Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:36:20 -0800 Subject: [PATCH 080/131] Improve readability with type aliases --- .../ApiVersionDescriptionProviderFactory.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs index 4643fe39..05b1cba1 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs @@ -1,21 +1,26 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -namespace Microsoft.AspNetCore.Builder; +#pragma warning disable SA1135 // Using directives should be qualified +#pragma warning disable SA1200 // Using directives should be placed correctly using Asp.Versioning; using Asp.Versioning.ApiExplorer; -using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Options; +namespace Microsoft.AspNetCore.Builder; + +using Microsoft.AspNetCore.Routing; +using Activator = Func<IEnumerable<IApiVersionMetadataCollationProvider>, ISunsetPolicyManager, IOptions<ApiExplorerOptions>, IApiVersionDescriptionProvider>; + internal sealed class ApiVersionDescriptionProviderFactory : IApiVersionDescriptionProviderFactory { private readonly ISunsetPolicyManager sunsetPolicyManager; private readonly IApiVersionMetadataCollationProvider[] providers; private readonly IOptions<ApiExplorerOptions> options; - private readonly Func<IEnumerable<IApiVersionMetadataCollationProvider>, ISunsetPolicyManager, IOptions<ApiExplorerOptions>, IApiVersionDescriptionProvider> activator; + private readonly Activator activator; public ApiVersionDescriptionProviderFactory( - Func<IEnumerable<IApiVersionMetadataCollationProvider>, ISunsetPolicyManager, IOptions<ApiExplorerOptions>, IApiVersionDescriptionProvider> activator, + Activator activator, ISunsetPolicyManager sunsetPolicyManager, IEnumerable<IApiVersionMetadataCollationProvider> providers, IOptions<ApiExplorerOptions> options ) From 45808a7c9fb4d2f1f313551e4699c071c55c8e55 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:37:55 -0800 Subject: [PATCH 081/131] Refactor internals with concrete types and type aliases --- .../MediaTypeApiVersionReaderBuilder.cs | 11 +- .../MediaTypeApiVersionReaderBuilder.cs | 9 +- .../MediaTypeApiVersionReaderBuilder.cs | 117 +++++++++--------- 3 files changed, 64 insertions(+), 73 deletions(-) diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs index 29e19c6b..413f20e0 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/MediaTypeApiVersionReaderBuilder.cs @@ -29,8 +29,8 @@ public partial class MediaTypeApiVersionReaderBuilder var parser = new RouteParser(); var parsedRoute = parser.Parse( template ); var segments = from content in parsedRoute.PathSegments.OfType<IPathContentSegment>() - from segment in content.Subsegments.OfType<IPathParameterSubsegment>() - select segment; + from segment in content.Subsegments.OfType<IPathParameterSubsegment>() + select segment; if ( segments.Count() > 1 ) { @@ -108,11 +108,6 @@ private static IReadOnlyList<string> ReadMediaTypePattern( } } - if ( version is null ) - { - return Array.Empty<string>(); - } - - return versions is null ? new[] { version } : versions.ToArray(); + return ToArray( ref version, versions ); } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs index f86c7e50..92bcbd12 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/MediaTypeApiVersionReaderBuilder.cs @@ -46,7 +46,7 @@ public partial class MediaTypeApiVersionReaderBuilder return this; } - private static IReadOnlyList<string> ReadMediaTypePattern( + private static string[] ReadMediaTypePattern( IReadOnlyList<MediaTypeHeaderValue> mediaTypes, TemplateMatcher matcher, string? parameterName ) @@ -103,11 +103,6 @@ private static IReadOnlyList<string> ReadMediaTypePattern( } } - if ( version is null ) - { - return Array.Empty<string>(); - } - - return versions is null ? new[] { version } : versions.ToArray(); + return ToArray( ref version, versions ); } } \ No newline at end of file diff --git a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs index f6416e41..64e9d573 100644 --- a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs +++ b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs @@ -2,28 +2,36 @@ #pragma warning disable IDE0079 #pragma warning disable SA1121 - -namespace Asp.Versioning; +#pragma warning disable SA1135 +#pragma warning disable SA1200 #if NETFRAMEWORK using System.Net.Http.Headers; +using HttpRequest = System.Net.Http.HttpRequestMessage; +using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeWithQualityHeaderValue; #else using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; +#endif + +namespace Asp.Versioning; + +#if !NETFRAMEWORK using System.Buffers; #endif using System.Runtime.CompilerServices; using System.Text.RegularExpressions; #if NETFRAMEWORK -using HttpRequest = System.Net.Http.HttpRequestMessage; using Str = System.String; using StrComparer = System.StringComparer; #else -using MediaTypeWithQualityHeaderValue = Microsoft.Net.Http.Headers.MediaTypeHeaderValue; using Str = Microsoft.Extensions.Primitives.StringSegment; using StrComparer = Microsoft.Extensions.Primitives.StringSegmentComparer; #endif using static Asp.Versioning.ApiVersionParameterLocation; using static System.StringComparison; +using ReaderCallback = Func<IReadOnlyList<MediaTypeHeaderValue>, IReadOnlyList<string>>; +using SelectorCallback = Func<HttpRequest, IReadOnlyList<string>, IReadOnlyList<string>>; /// <summary> /// Represents a builder for an API version reader that reads the value from a media type HTTP header in the request. @@ -33,8 +41,8 @@ public partial class MediaTypeApiVersionReaderBuilder private HashSet<string>? parameters; private HashSet<Str>? included; private HashSet<Str>? excluded; - private Func<HttpRequest, IReadOnlyList<string>, IReadOnlyList<string>>? select; - private List<Func<IReadOnlyList<MediaTypeWithQualityHeaderValue>, IReadOnlyList<string>>>? readers; + private SelectorCallback? select; + private List<ReaderCallback>? readers; /// <summary> /// Adds the name of a media type parameter to be read. @@ -114,7 +122,7 @@ public virtual MediaTypeApiVersionReaderBuilder Match( [StringSyntax( StringSynt [CLSCompliant( false )] #endif #pragma warning disable CA1716 // Identifiers should not match keywords - public virtual MediaTypeApiVersionReaderBuilder Select( Func<HttpRequest, IReadOnlyList<string>, IReadOnlyList<string>> selector ) + public virtual MediaTypeApiVersionReaderBuilder Select( SelectorCallback selector ) #pragma warning restore CA1716 // Identifiers should not match keywords { select = selector; @@ -149,7 +157,7 @@ public virtual IApiVersionReader Build() => #if !NETFRAMEWORK [CLSCompliant( false )] #endif - protected void AddReader( Func<IReadOnlyList<MediaTypeWithQualityHeaderValue>, IReadOnlyList<string>> reader ) + protected void AddReader( ReaderCallback reader ) { ArgumentNullException.ThrowIfNull( reader ); @@ -158,23 +166,29 @@ protected void AddReader( Func<IReadOnlyList<MediaTypeWithQualityHeaderValue>, I } [MethodImpl( MethodImplOptions.AggressiveInlining )] - private static ICollection<Str> EmptyCollection() => Array.Empty<Str>(); + private static IReadOnlyList<string> DefaultSelector( HttpRequest request, IReadOnlyList<string> versions ) => versions; [MethodImpl( MethodImplOptions.AggressiveInlining )] - private static IReadOnlyList<Func<IReadOnlyList<MediaTypeWithQualityHeaderValue>, IReadOnlyList<string>>> EmptyList() => - Array.Empty<Func<IReadOnlyList<MediaTypeWithQualityHeaderValue>, IReadOnlyList<string>>>(); + private static string[] ToArray( ref string? version, List<string>? versions ) + { + if ( version is null ) + { + return []; + } - private static IReadOnlyList<string> DefaultSelector( HttpRequest request, IReadOnlyList<string> versions ) => versions; + return versions is null ? [version] : [.. versions]; + } - private static IReadOnlyList<string> ReadMediaType( - IReadOnlyList<MediaTypeWithQualityHeaderValue> mediaTypes, + private static string[] ReadMediaType( + IReadOnlyList<MediaTypeHeaderValue> mediaTypes, string pattern ) { var version = default( string ); var versions = default( List<string> ); var regex = default( Regex ); + var count = mediaTypes.Count; - for ( var i = 0; i < mediaTypes.Count; i++ ) + for ( var i = 0; i < count; i++ ) { var mediaType = mediaTypes[i].MediaType; @@ -203,7 +217,7 @@ private static IReadOnlyList<string> ReadMediaType( } else if ( versions == null ) { - versions = new( capacity: mediaTypes.Count - i + 1 ) + versions = new( capacity: count - i + 1 ) { version, value, @@ -218,22 +232,18 @@ private static IReadOnlyList<string> ReadMediaType( } } - if ( version is null ) - { - return Array.Empty<string>(); - } - - return versions is null ? new[] { version } : versions.ToArray(); + return ToArray( ref version, versions ); } - private static IReadOnlyList<string> ReadMediaTypeParameter( - IReadOnlyList<MediaTypeWithQualityHeaderValue> mediaTypes, + private static string[] ReadMediaTypeParameter( + IReadOnlyList<MediaTypeHeaderValue> mediaTypes, string parameterName ) { var version = default( string ); var versions = default( List<string> ); + var count = mediaTypes.Count; - for ( var i = 0; i < mediaTypes.Count; i++ ) + for ( var i = 0; i < count; i++ ) { var mediaType = mediaTypes[i]; @@ -256,7 +266,7 @@ private static IReadOnlyList<string> ReadMediaTypeParameter( } else if ( versions == null ) { - versions = new( capacity: mediaTypes.Count - i + 1 ) + versions = new( capacity: count - i + 1 ) { version, value, @@ -269,28 +279,23 @@ private static IReadOnlyList<string> ReadMediaTypeParameter( } } - if ( version is null ) - { - return Array.Empty<string>(); - } - - return versions is null ? new[] { version } : versions.ToArray(); + return ToArray( ref version, versions ); } private sealed class BuiltMediaTypeApiVersionReader : IApiVersionReader { - private readonly IReadOnlyList<string> parameters; - private readonly ICollection<Str> included; - private readonly ICollection<Str> excluded; - private readonly Func<HttpRequest, IReadOnlyList<string>, IReadOnlyList<string>> selector; - private readonly IReadOnlyList<Func<IReadOnlyList<MediaTypeWithQualityHeaderValue>, IReadOnlyList<string>>> readers; + private readonly string[] parameters; + private readonly HashSet<Str> included; + private readonly HashSet<Str> excluded; + private readonly SelectorCallback selector; + private readonly ReaderCallback[] readers; internal BuiltMediaTypeApiVersionReader( - IReadOnlyList<string> parameters, - ICollection<Str> included, - ICollection<Str> excluded, - Func<HttpRequest, IReadOnlyList<string>, IReadOnlyList<string>> selector, - IReadOnlyList<Func<IReadOnlyList<MediaTypeWithQualityHeaderValue>, IReadOnlyList<string>>> readers ) + string[] parameters, + HashSet<Str> included, + HashSet<Str> excluded, + SelectorCallback selector, + ReaderCallback[] readers ) { this.parameters = parameters; this.included = included; @@ -301,18 +306,15 @@ internal BuiltMediaTypeApiVersionReader( public void AddParameters( IApiVersionParameterDescriptionContext context ) { - if ( context == null ) - { - throw new ArgumentNullException( nameof( context ) ); - } + ArgumentNullException.ThrowIfNull( context ); - if ( parameters.Count == 0 ) + if ( parameters.Length == 0 ) { context.AddParameter( name: string.Empty, MediaTypeParameter ); } else { - for ( var i = 0; i < parameters.Count; i++ ) + for ( var i = 0; i < parameters.Length; i++ ) { context.AddParameter( parameters[i], MediaTypeParameter ); } @@ -321,7 +323,7 @@ public void AddParameters( IApiVersionParameterDescriptionContext context ) public IReadOnlyList<string> Read( HttpRequest request ) { - if ( readers.Count == 0 ) + if ( readers.Length == 0 ) { return Array.Empty<string>(); } @@ -329,22 +331,21 @@ public IReadOnlyList<string> Read( HttpRequest request ) #if NETFRAMEWORK var headers = request.Headers; var contentType = request.Content?.Headers.ContentType; - var accept = headers.Accept; #else var headers = request.GetTypedHeaders(); var contentType = headers.ContentType; - var accept = headers.Accept; #endif + var accept = headers.Accept; var version = default( string ); var versions = default( SortedSet<string> ); - var mediaTypes = default( List<MediaTypeWithQualityHeaderValue> ); + var mediaTypes = default( List<MediaTypeHeaderValue> ); if ( contentType != null ) { #if NETFRAMEWORK - mediaTypes = new() { MediaTypeWithQualityHeaderValue.Parse( contentType.ToString() ) }; + mediaTypes = [MediaTypeHeaderValue.Parse( contentType.ToString() )]; #else - mediaTypes = new() { contentType }; + mediaTypes = [contentType]; #endif } @@ -376,13 +377,13 @@ public IReadOnlyList<string> Read( HttpRequest request ) if ( versions == null ) { - return version == null ? Array.Empty<string>() : new[] { version }; + return version == null ? Array.Empty<string>() : [version]; } return selector( request, versions.ToArray() ); } - private void Filter( IList<MediaTypeWithQualityHeaderValue> mediaTypes ) + private void Filter( List<MediaTypeHeaderValue> mediaTypes ) { if ( excluded.Count > 0 ) { @@ -412,11 +413,11 @@ private void Filter( IList<MediaTypeWithQualityHeaderValue> mediaTypes ) } private void Read( - List<MediaTypeWithQualityHeaderValue> mediaTypes, + IReadOnlyList<MediaTypeHeaderValue> mediaTypes, ref string? version, ref SortedSet<string>? versions ) { - for ( var i = 0; i < readers.Count; i++ ) + for ( var i = 0; i < readers.Length; i++ ) { var result = readers[i]( mediaTypes ); From eb9c0f9a9e80cd9b18b0fffa22a4806ca52406a7 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:38:20 -0800 Subject: [PATCH 082/131] Fix spelling --- .../MediaTypeApiVersionBuilderTest.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/MediaTypeApiVersionBuilderTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/MediaTypeApiVersionBuilderTest.cs index fc842c09..7050bf6a 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/MediaTypeApiVersionBuilderTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/MediaTypeApiVersionBuilderTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable ASP0015 // Suggest using IHeaderDictionary properties + namespace Asp.Versioning; using Microsoft.AspNetCore.Http; @@ -260,7 +262,7 @@ public void read_should_assume_version_from_single_parameter_in_media_type_templ [InlineData( "application/vnd-v{v}+json", "v", "application/vnd-v2.1+json", "2.1" )] [InlineData( "application/vnd-v{ver}+json", "ver", "application/vnd-v2022-11-01+json", "2022-11-01" )] [InlineData( "application/vnd-{version}+xml", "version", "application/vnd-1.1-beta+xml", "1.1-beta" )] - public void read_should_retreive_version_from_media_type_template( + public void read_should_retrieve_version_from_media_type_template( string template, string parameterName, string mediaType, From 05a3007e31fc442afe7f81093b629b3c2cbd48b0 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 20:40:41 -0800 Subject: [PATCH 083/131] Bump version numbers and update release notes --- .../Asp.Versioning.Abstractions.csproj | 9 ++++----- .../Asp.Versioning.WebApi.OData.Tests.csproj | 10 +++++----- .../Asp.Versioning.OData.ApiExplorer.csproj | 4 ++-- .../Asp.Versioning.OData/Asp.Versioning.OData.csproj | 4 ++-- .../src/Asp.Versioning.Http/Asp.Versioning.Http.csproj | 4 ++-- .../Asp.Versioning.Mvc.ApiExplorer.csproj | 4 ++-- .../src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj | 4 ++-- .../WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt | 2 +- .../Asp.Versioning.Http.Client.csproj | 7 ++++--- .../src/Asp.Versioning.Http.Client/ReleaseNotes.txt | 2 +- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj b/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj index 1ed9b56d..8757f5da 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> - + <PropertyGroup> - <VersionPrefix>7.0.0</VersionPrefix> - <AssemblyVersion>7.0.0.0</AssemblyVersion> + <VersionPrefix>8.0.0</VersionPrefix> + <AssemblyVersion>8.0.0.0</AssemblyVersion> <TargetFrameworks>$(DefaultTargetFramework);netstandard1.0;netstandard2.0</TargetFrameworks> <AssemblyTitle>API Versioning Abstractions</AssemblyTitle> <Description>The abstractions library for API versioning.</Description> @@ -40,11 +40,10 @@ <ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETStandard' "> <Using Include="Backport.ArgumentNullException" Alias="ArgumentNullException" /> - <Compile Include="$(BackportDir)HashCode.cs" Visible="false" /> </ItemGroup> <ItemGroup Condition=" '$(TargetFramework)' != 'netstandard1.0' "> <PackageReference Include="Microsoft.Extensions.Primitives" Version="$(DotNetReleasePackageVersion)" /> </ItemGroup> - + </Project> \ No newline at end of file diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Asp.Versioning.WebApi.OData.Tests.csproj b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Asp.Versioning.WebApi.OData.Tests.csproj index 592ea6dc..30285b43 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Asp.Versioning.WebApi.OData.Tests.csproj +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Asp.Versioning.WebApi.OData.Tests.csproj @@ -1,9 +1,9 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> - <PropertyGroup> - <TargetFrameworks>net452;net472</TargetFrameworks> - <RootNamespace>Asp.Versioning</RootNamespace> - </PropertyGroup> + <PropertyGroup> + <TargetFrameworks>net452;net472</TargetFrameworks> + <RootNamespace>Asp.Versioning</RootNamespace> + </PropertyGroup> <ItemGroup> <ProjectReference Include="..\..\src\Asp.Versioning.WebApi.OData\Asp.Versioning.WebApi.OData.csproj" /> diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj index 4cc610d7..d132353a 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>7.1.0</VersionPrefix> - <AssemblyVersion>7.1.0.0</AssemblyVersion> + <VersionPrefix>8.0.0</VersionPrefix> + <AssemblyVersion>8.0.0.0</AssemblyVersion> <TargetFramework>$(DefaultTargetFramework)</TargetFramework> <RootNamespace>Asp.Versioning</RootNamespace> <AssemblyTitle>ASP.NET Core API Versioning API Explorer for OData v4.0</AssemblyTitle> diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj index ddbe3255..ee194d18 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>7.1.0</VersionPrefix> - <AssemblyVersion>7.1.0.0</AssemblyVersion> + <VersionPrefix>8.0.0</VersionPrefix> + <AssemblyVersion>8.0.0.0</AssemblyVersion> <TargetFramework>$(DefaultTargetFramework)</TargetFramework> <RootNamespace>Asp.Versioning</RootNamespace> <AssemblyTitle>ASP.NET Core API Versioning with OData v4.0</AssemblyTitle> diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj index 5de452a5..9ef9ea44 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>7.1.0</VersionPrefix> - <AssemblyVersion>7.1.0.0</AssemblyVersion> + <VersionPrefix>8.0.0</VersionPrefix> + <AssemblyVersion>8.0.0.0</AssemblyVersion> <TargetFramework>$(DefaultTargetFramework)</TargetFramework> <RootNamespace>Asp.Versioning</RootNamespace> <AssemblyTitle>ASP.NET Core API Versioning</AssemblyTitle> diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj index 4b8932f0..35aa3746 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>7.1.0</VersionPrefix> - <AssemblyVersion>7.1.0.0</AssemblyVersion> + <VersionPrefix>8.0.0</VersionPrefix> + <AssemblyVersion>8.0.0.0</AssemblyVersion> <TargetFramework>$(DefaultTargetFramework)</TargetFramework> <RootNamespace>Asp.Versioning.ApiExplorer</RootNamespace> <AssemblyTitle>ASP.NET Core API Versioning API Explorer</AssemblyTitle> diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj index 495eeee3..0d58ab3f 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>7.1.1</VersionPrefix> - <AssemblyVersion>7.1.0.0</AssemblyVersion> + <VersionPrefix>8.0.0</VersionPrefix> + <AssemblyVersion>8.0.0.0</AssemblyVersion> <TargetFramework>$(DefaultTargetFramework)</TargetFramework> <RootNamespace>Asp.Versioning</RootNamespace> <AssemblyTitle>ASP.NET Core API Versioning</AssemblyTitle> diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt index 525fc7a3..5f282702 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ReleaseNotes.txt @@ -1 +1 @@ -If Controller Attribute is set, use it for the controller name ([#1033](https://github.com/dotnet/aspnet-api-versioning/issues/1033)) \ No newline at end of file + \ No newline at end of file diff --git a/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj b/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj index 4c1ac1db..d47be589 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj +++ b/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>7.0.1</VersionPrefix> - <AssemblyVersion>7.0.0.0</AssemblyVersion> + <VersionPrefix>8.0.0</VersionPrefix> + <AssemblyVersion>8.0.0.0</AssemblyVersion> <TargetFrameworks>$(DefaultTargetFramework);netstandard1.1;netstandard2.0</TargetFrameworks> <RootNamespace>Asp.Versioning.Http</RootNamespace> <AssemblyTitle>API Versioning Client Extensions</AssemblyTitle> @@ -18,7 +18,7 @@ <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' "> <Compile Include="$(BackportDir)Array.cs" Visible="false" /> </ItemGroup> - + <ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETStandard' "> <Compile Include="$(BackportDir)ArgumentException.cs" Visible="false" /> <Compile Include="$(BackportDir)ArgumentNullException.cs" Visible="false" /> @@ -44,4 +44,5 @@ <ItemGroup> <ProjectReference Include="..\..\..\Abstractions\src\Asp.Versioning.Abstractions\Asp.Versioning.Abstractions.csproj" /> </ItemGroup> + </Project> diff --git a/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt b/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt index 1672692d..5f282702 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt +++ b/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt @@ -1 +1 @@ -Publish NuGet symbol package \ No newline at end of file + \ No newline at end of file From 2b81fb7c7468629607d6ec19f37893a1174ed9f7 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 4 Dec 2023 21:08:44 -0800 Subject: [PATCH 084/131] Add missing /unsafe directive --- .../WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj index 0d58ab3f..0871cc58 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj @@ -8,6 +8,7 @@ <AssemblyTitle>ASP.NET Core API Versioning</AssemblyTitle> <Description>A service API versioning library for Microsoft ASP.NET Core MVC.</Description> <PackageTags>Asp;AspNet;AspNetCore;MVC;Versioning</PackageTags> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> <ItemGroup> From 26d94664b146927c65ed6a1745c6ebd6e2c4ccc7 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Tue, 5 Dec 2023 10:18:17 -0800 Subject: [PATCH 085/131] Avoid exceptions with TryWriteAsync vs WriteAsync --- .../Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs | 2 +- .../WebApi/src/Asp.Versioning.Http/Routing/EndpointProblem.cs | 2 +- .../Asp.Versioning.Http/Routing/MalformedApiVersionEndpoint.cs | 2 +- .../Routing/UnspecifiedApiVersionEndpoint.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs index 348ea1fa..80eb5456 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/AmbiguousApiVersionEndpoint.cs @@ -32,7 +32,7 @@ private static Task OnExecute( HttpContext context, ILogger logger ) Format.MultipleDifferentApiVersionsRequested, string.Join( ", ", apiVersions ) ); - return problemDetails.WriteAsync( + return problemDetails.TryWriteAsync( EndpointProblem.New( context, ProblemDetailsDefaults.Ambiguous, diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/EndpointProblem.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/EndpointProblem.cs index 250a9e43..9a8b539b 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/EndpointProblem.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/EndpointProblem.cs @@ -56,7 +56,7 @@ internal static Task UnsupportedApiVersion( new Uri( context.Request.GetDisplayUrl() ).SafeFullPath(), context.ApiVersioningFeature().RawRequestedApiVersion ); - return problemDetails.WriteAsync( New( context, Unsupported, detail ) ).AsTask(); + return problemDetails.TryWriteAsync( New( context, Unsupported, detail ) ).AsTask(); } return Task.CompletedTask; diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/MalformedApiVersionEndpoint.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/MalformedApiVersionEndpoint.cs index 4e4bf28d..c129794a 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/MalformedApiVersionEndpoint.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/MalformedApiVersionEndpoint.cs @@ -33,7 +33,7 @@ private static Task OnExecute( HttpContext context, ILogger logger ) new Uri( context.Request.GetDisplayUrl() ).SafeFullPath(), requestedVersion ); - return problemDetails.WriteAsync( + return problemDetails.TryWriteAsync( EndpointProblem.New( context, ProblemDetailsDefaults.Invalid, diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/UnspecifiedApiVersionEndpoint.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/UnspecifiedApiVersionEndpoint.cs index dc97846f..6597aec9 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/UnspecifiedApiVersionEndpoint.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/UnspecifiedApiVersionEndpoint.cs @@ -28,7 +28,7 @@ private static Task OnExecute( HttpContext context, string[]? candidateEndpoints if ( context.TryGetProblemDetailsService( out var problemDetails ) ) { - return problemDetails.WriteAsync( + return problemDetails.TryWriteAsync( EndpointProblem.New( context, ProblemDetailsDefaults.Unspecified, From 770cf6aae4078a98dfbd06f5b1a1677ae2df78dd Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Tue, 5 Dec 2023 10:18:24 -0800 Subject: [PATCH 086/131] Update comments --- .../DependencyInjection/IServiceCollectionExtensions.cs | 4 ++-- .../src/Asp.Versioning.Http/Rfc7231ProblemDetailsWriter.cs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs index 615b505a..508f34d9 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs @@ -153,8 +153,8 @@ LinkGenerator NewFactory( IServiceProvider serviceProvider ) } } - // TODO: Remove in .NET 8.0 - // REF: https://github.com/dotnet/aspnetcore/issues/45051 + // TODO: Remove in .NET 9.0 or .NET 8.0 patch + // BUG: https://github.com/dotnet/aspnetcore/issues/52577 private static void TryAddProblemDetailsRfc7231Compliance( IServiceCollection services ) { var descriptor = services.FirstOrDefault( IsDefaultProblemDetailsWriter ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Rfc7231ProblemDetailsWriter.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Rfc7231ProblemDetailsWriter.cs index 8c7382be..3b380197 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Rfc7231ProblemDetailsWriter.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Rfc7231ProblemDetailsWriter.cs @@ -36,6 +36,9 @@ public bool CanWrite( ProblemDetailsContext context ) { var acceptHeaderValue = acceptHeader[i]; + // TODO: the logic is inverted in .NET 8. remove when fixed + // BUG: https://github.com/dotnet/aspnetcore/issues/52577 + // REF: https://github.com/dotnet/aspnetcore/blob/release/8.0/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs#L38 if ( acceptHeaderValue.IsSubsetOf( jsonMediaType ) || acceptHeaderValue.IsSubsetOf( problemDetailsJsonMediaType ) ) { From a65e09643b1761beb0cad40638e15ef400833628 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 15:25:14 -0800 Subject: [PATCH 087/131] Support async API version selection. Resolves #1009 --- .../OData/Batch/ODataBatchPathMapping.cs | 111 +++++++++++++----- .../OData/VersionedODataOptions.cs | 53 +++++++++ .../IApiVersionSelector.cs | 26 ++++ .../Routing/ApiVersionMatcherPolicy.cs | 13 +- src/Common/src/Common/ApiVersioningOptions.cs | 2 +- src/Common/src/Common/IApiVersionSelector.cs | 5 +- 6 files changed, 172 insertions(+), 38 deletions(-) create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Http/IApiVersionSelector.cs diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs index e009a930..23b412c6 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs @@ -40,11 +40,56 @@ public bool TryGetHandler( HttpContext context, [NotNullWhen( true )] out ODataB return false; } + var routeData = new RouteValueDictionary(); + var candidates = new Dictionary<ApiVersion, int>( capacity: mappings.Length ); + + batchHandler = SelectExactMatch( context, routeData, candidates ) ?? + SelectBestCandidate( context, candidates, routeData ); + + return batchHandler is not null; + } + + public ValueTask<ODataBatchHandler?> TryGetHandlerAsync( HttpContext context, CancellationToken cancellationToken ) + { + if ( count == 0 ) + { + return ValueTask.FromResult( default( ODataBatchHandler ) ); + } + + var routeData = new RouteValueDictionary(); + var candidates = new Dictionary<ApiVersion, int>( capacity: mappings.Length ); + + if ( SelectExactMatch( context, routeData, candidates ) is { } handler ) + { + return ValueTask.FromResult<ODataBatchHandler?>( handler ); + } + + return SelectBestCandidateAsync( context, candidates, routeData, cancellationToken ); + } + + private static void MergeRouteData( HttpContext context, RouteValueDictionary routeData ) + { + if ( routeData.Count == 0 ) + { + return; + } + + var batchRouteData = context.ODataFeature().BatchRouteData; + + foreach ( var (key, value) in routeData ) + { + batchRouteData.Add( key, value ); + } + } + + private ODataBatchHandler? SelectExactMatch( + HttpContext context, + RouteValueDictionary routeData, + Dictionary<ApiVersion, int> candidates ) + { var path = context.Request.Path; var feature = context.ApiVersioningFeature(); var unspecified = feature.RawRequestedApiVersions.Count == 0; - var routeData = new RouteValueDictionary(); - var candidates = new Dictionary<ApiVersion, int>( capacity: mappings.Length ); for ( var i = 0; i < count; i++ ) { @@ -73,32 +118,39 @@ public bool TryGetHandler( HttpContext context, [NotNullWhen( true )] out ODataB } MergeRouteData( context, routeData ); - batchHandler = handler; - return true; + return handler; } - batchHandler = SelectBestCandidate( context, ref path, candidates, routeData ); - return batchHandler is not null; + return default; } - private static void MergeRouteData( HttpContext context, RouteValueDictionary routeData ) + private ODataBatchHandler? SelectBestCandidate( + HttpContext context, + Dictionary<ApiVersion, int> candidates, + RouteValueDictionary routeData, + ApiVersion version ) { - if ( routeData.Count == 0 ) + if ( version is null || !candidates.TryGetValue( version, out var index ) ) { - return; + return default; } - var batchRouteData = context.ODataFeature().BatchRouteData; + ref readonly var mapping = ref mappings[index]; + var (matcher, handler, _) = mapping; - foreach ( var (key, value) in routeData ) - { - batchRouteData.Add( key, value ); - } + routeData.Clear(); + matcher.TryMatch( context.Request.Path, routeData ); + MergeRouteData( context, routeData ); + + // it's important that the resolved api version be set here to ensure the correct + // ODataOptions are resolved by ODataBatchHandler when executed + context.ApiVersioningFeature().RequestedApiVersion = version; + + return handler; } private ODataBatchHandler? SelectBestCandidate( HttpContext context, - ref PathString path, Dictionary<ApiVersion, int> candidates, RouteValueDictionary routeData ) { @@ -114,22 +166,27 @@ private static void MergeRouteData( HttpContext context, RouteValueDictionary ro var model = new ApiVersionModel( candidates.Keys, Enumerable.Empty<ApiVersion>() ); var version = selector.SelectVersion( context.Request, model ); - if ( version is null || !candidates.TryGetValue( version, out var index ) ) + return SelectBestCandidate( context, candidates, routeData, version ); + } + + private async ValueTask<ODataBatchHandler?> SelectBestCandidateAsync( + HttpContext context, + Dictionary<ApiVersion, int> candidates, + RouteValueDictionary routeData, + CancellationToken cancellationToken ) + { + if ( candidates.Count == 0 ) { return default; } - ref readonly var mapping = ref mappings[index]; - var (matcher, handler, _) = mapping; - - routeData.Clear(); - matcher.TryMatch( path, routeData ); - MergeRouteData( context, routeData ); - - // it's important that the resolved api version be set here to ensure the correct - // ODataOptions are resolved by ODataBatchHandler when executed - context.ApiVersioningFeature().RequestedApiVersion = version; + // ~/$batch is always version-neutral so there is no need to check + // ApiVersioningOptions.AllowDefaultVersionWhenUnspecified. use the + // configured IApiVersionSelector to provide a chance to select the + // most appropriate version. + var model = new ApiVersionModel( candidates.Keys, Enumerable.Empty<ApiVersion>() ); + var version = await selector.SelectVersionAsync( context.Request, model, cancellationToken ).ConfigureAwait( false ); - return handler; + return SelectBestCandidate( context, candidates, routeData, version ); } } \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataOptions.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataOptions.cs index 64df342f..5c08429e 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataOptions.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/VersionedODataOptions.cs @@ -85,6 +85,8 @@ public IReadOnlyDictionary<ApiVersion, ODataOptions> Mapping /// <param name="context">The current <see cref="HttpContext">HTTP context</see>.</param> /// <param name="handler">The retrieved <see cref="ODataBatchHandler">OData batch handler</see> or <c>null</c>.</param> /// <returns>True if the <paramref name="handler"/> was successfully retrieved; otherwise, false.</returns> + /// <remarks>Prefer the asynchronous version of this method + /// <see cref="TryGetBatchHandlerAsync(HttpContext, CancellationToken)"/>.</remarks> public virtual bool TryGetBatchHandler( HttpContext context, [NotNullWhen( true )] out ODataBatchHandler? handler ) { ArgumentNullException.ThrowIfNull( context ); @@ -98,12 +100,33 @@ public virtual bool TryGetBatchHandler( HttpContext context, [NotNullWhen( true return batchMapping.TryGetHandler( context, out handler ); } + /// <summary> + /// Attempts to retrieve the configured batch handler for the current context. + /// </summary> + /// <param name="context">The current <see cref="HttpContext">HTTP context</see>.</param> + /// <param name="cancellationToken">The token that can be used to cancel the operation.</param> + /// <returns>A <see cref="ValueTask{TResult}">task</see> containing the matched <see cref="ODataBatchHandler"/> + /// or <c>null</c> if the no match was found.</returns> + public virtual ValueTask<ODataBatchHandler?> TryGetBatchHandlerAsync( HttpContext context, CancellationToken cancellationToken ) + { + ArgumentNullException.ThrowIfNull( context ); + + if ( batchMapping is null ) + { + return ValueTask.FromResult( default( ODataBatchHandler? ) ); + } + + return batchMapping.TryGetHandlerAsync( context, cancellationToken ); + } + /// <summary> /// Attempts to get the current OData options. /// </summary> /// <param name="context">The current <see cref="HttpContext">HTTP context</see>.</param> /// <param name="options">The resolved <see cref="ODataOptions">OData options</see> or <c>null</c>.</param> /// <returns>True if the current OData were successfully resolved; otherwise, false.</returns> + /// <remarks>Prefer the asynchronous version of this method + /// <see cref="TryGetValueAsync(HttpContext?, CancellationToken)"/>.</remarks> public virtual bool TryGetValue( HttpContext? context, [NotNullWhen( true )] out ODataOptions? options ) { if ( context == null || mapping == null || mapping.Count == 0 ) @@ -129,6 +152,36 @@ public virtual bool TryGetValue( HttpContext? context, [NotNullWhen( true )] out return mapping.TryGetValue( apiVersion, out options ); } + /// <summary> + /// Attempts to get the current OData options. + /// </summary> + /// <param name="context">The current <see cref="HttpContext">HTTP context</see>.</param> + /// <param name="cancellationToken">The token that can be used to cancel the operation.</param> + /// <returns>A <see cref="ValueTask{TResult}">task</see> containing the matched <see cref="ODataOptions"/> + /// or <c>null</c> if the no match was found.</returns> + public virtual async ValueTask<ODataOptions?> TryGetValueAsync( HttpContext? context, CancellationToken cancellationToken ) + { + if ( context == null || mapping == null || mapping.Count == 0 ) + { + return default; + } + + var apiVersion = context.GetRequestedApiVersion(); + + if ( apiVersion == null ) + { + var model = new ApiVersionModel( mapping.Keys, Array.Empty<ApiVersion>() ); + apiVersion = await ApiVersionSelector.SelectVersionAsync( context.Request, model, cancellationToken ).ConfigureAwait( false ); + + if ( apiVersion == null ) + { + return default; + } + } + + return mapping.TryGetValue( apiVersion, out var options ) ? options : default; + } + /// <summary> /// Attempts to resolve the current OData options. /// </summary> diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/IApiVersionSelector.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/IApiVersionSelector.cs new file mode 100644 index 00000000..70f1a38b --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/IApiVersionSelector.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning; + +using Microsoft.AspNetCore.Http; + +/// <content> +/// content> +/// Provides additional implementation specific to ASP.NET Core. +/// </content> +[CLSCompliant( false )] +public partial interface IApiVersionSelector +{ + /// <summary> + /// Selects an API version given the specified HTTP request and API version information. + /// </summary> + /// <param name="request">The current <see cref="HttpRequest">HTTP request</see> to select the version for.</param> + /// <param name="model">The <see cref="ApiVersionModel">model</see> to select the version from.</param> + /// <param name="cancellationToken">The token that can be used to cancel the operation.</param> + /// <returns>A <see cref="ValueTask{TResult}">task</see> containing the selected <see cref="ApiVersion">API version</see>.</returns> + ValueTask<ApiVersion> SelectVersionAsync( + HttpRequest request, + ApiVersionModel model, + CancellationToken cancellationToken ) => + ValueTask.FromResult( SelectVersion( request, model ) ); +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs index dcae033d..918d84b2 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs @@ -77,7 +77,7 @@ public bool AppliesToEndpoints( IReadOnlyList<Endpoint> endpoints ) } /// <inheritdoc /> - public Task ApplyAsync( HttpContext httpContext, CandidateSet candidates ) + public async Task ApplyAsync( HttpContext httpContext, CandidateSet candidates ) { ArgumentNullException.ThrowIfNull( httpContext ); ArgumentNullException.ThrowIfNull( candidates ); @@ -87,7 +87,7 @@ public Task ApplyAsync( HttpContext httpContext, CandidateSet candidates ) if ( apiVersion == null && Options.AssumeDefaultVersionWhenUnspecified ) { - apiVersion = TrySelectApiVersion( httpContext, candidates ); + apiVersion = await TrySelectApiVersionAsync( httpContext, candidates ).ConfigureAwait( false ); feature.RequestedApiVersion = apiVersion; } @@ -98,8 +98,6 @@ public Task ApplyAsync( HttpContext httpContext, CandidateSet candidates ) var builder = new ClientErrorEndpointBuilder( feature, candidates, Options, logger ); httpContext.SetEndpoint( builder.Build() ); } - - return Task.CompletedTask; } /// <inheritdoc /> @@ -453,7 +451,7 @@ private static (bool Matched, bool HasCandidates) MatchApiVersion( CandidateSet return (matched, hasCandidates); } - private ApiVersion TrySelectApiVersion( HttpContext httpContext, CandidateSet candidates ) + private ValueTask<ApiVersion> TrySelectApiVersionAsync( HttpContext httpContext, CandidateSet candidates ) { var models = new List<ApiVersionModel>( capacity: candidates.Count ); @@ -473,7 +471,10 @@ private ApiVersion TrySelectApiVersion( HttpContext httpContext, CandidateSet ca } } - return ApiVersionSelector.SelectVersion( httpContext.Request, models.Aggregate() ); + return ApiVersionSelector.SelectVersionAsync( + httpContext.Request, + models.Aggregate(), + httpContext.RequestAborted ); } bool INodeBuilderPolicy.AppliesToEndpoints( IReadOnlyList<Endpoint> endpoints ) => diff --git a/src/Common/src/Common/ApiVersioningOptions.cs b/src/Common/src/Common/ApiVersioningOptions.cs index b5297ba9..82c1cfce 100644 --- a/src/Common/src/Common/ApiVersioningOptions.cs +++ b/src/Common/src/Common/ApiVersioningOptions.cs @@ -47,7 +47,7 @@ public partial class ApiVersioningOptions /// <value>True if the a default API version should be assumed when a client does not /// provide an API version; otherwise, false. The default value is <c>false</c>.</value> /// <remarks>When a default API version is assumed, the version used is based up the - /// result of the <see cref="IApiVersionSelector.SelectVersion"/> method.</remarks> + /// result from <see cref="IApiVersionSelector"/>.</remarks> public bool AssumeDefaultVersionWhenUnspecified { get; set; } /// <summary> diff --git a/src/Common/src/Common/IApiVersionSelector.cs b/src/Common/src/Common/IApiVersionSelector.cs index 84eca910..3063fe95 100644 --- a/src/Common/src/Common/IApiVersionSelector.cs +++ b/src/Common/src/Common/IApiVersionSelector.cs @@ -11,10 +11,7 @@ namespace Asp.Versioning; /// <summary> /// Defines the behavior of an API version selector. /// </summary> -#if !NETFRAMEWORK -[CLSCompliant( false )] -#endif -public interface IApiVersionSelector +public partial interface IApiVersionSelector { /// <summary> /// Selects an API version given the specified HTTP request and API version information. From 19bc132f101b05e1486fa22a208d55617d7845c2 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 15:25:56 -0800 Subject: [PATCH 088/131] Add extension method to simplify version selection without a HTTP request --- .../ApiVersionParameterDescriptionContext.cs | 7 +----- .../IApiVersionSelectorExtensions.cs | 22 ++++++++++++++++ .../IApiVersionSelectorExtensions.cs | 25 +++++++++++++++++++ .../ApiVersionParameterDescriptionContext.cs | 3 +-- 4 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 src/AspNet/WebApi/src/Asp.Versioning.WebApi/IApiVersionSelectorExtensions.cs create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Http/IApiVersionSelectorExtensions.cs diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiVersionParameterDescriptionContext.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiVersionParameterDescriptionContext.cs index 8e685601..f92c6857 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiVersionParameterDescriptionContext.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi.ApiExplorer/Description/ApiVersionParameterDescriptionContext.cs @@ -255,12 +255,7 @@ private static bool FirstParameterIsOptional( var mapping = ApiVersionMapping.Explicit | ApiVersionMapping.Implicit; var model = apiDescription.ActionDescriptor.GetApiVersionMetadata().Map( mapping ); - ApiVersion defaultApiVersion; - - using ( var request = new HttpRequestMessage() ) - { - defaultApiVersion = options.ApiVersionSelector.SelectVersion( request, model ); - } + var defaultApiVersion = options.ApiVersionSelector.SelectVersion( model ); return apiVersion == defaultApiVersion; } diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/IApiVersionSelectorExtensions.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/IApiVersionSelectorExtensions.cs new file mode 100644 index 00000000..37d63336 --- /dev/null +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/IApiVersionSelectorExtensions.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning; + +/// <summary> +/// Provides extension methods for <see cref="IApiVersionSelector"/>. +/// </summary> +public static class IApiVersionSelectorExtensions +{ + /// <summary> + /// Selects an API version given the specified API version information. + /// </summary> + /// <param name="selector">The extended <see cref="IApiVersionSelector"/>.</param> + /// <param name="model">The <see cref="ApiVersionModel">model</see> to select the version from.</param> + /// <returns>The selected <see cref="ApiVersion">API version</see>.</returns> + public static ApiVersion SelectVersion( this IApiVersionSelector selector, ApiVersionModel model ) + { + ArgumentNullException.ThrowIfNull( selector ); + using var request = new HttpRequestMessage(); + return selector.SelectVersion( request, model ); + } +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/IApiVersionSelectorExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/IApiVersionSelectorExtensions.cs new file mode 100644 index 00000000..a88a8e13 --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/IApiVersionSelectorExtensions.cs @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning; + +using Microsoft.AspNetCore.Http; + +/// <summary> +/// Provides extension methods for <see cref="IApiVersionSelector"/>. +/// </summary> +[CLSCompliant( false )] +public static class IApiVersionSelectorExtensions +{ + /// <summary> + /// Selects an API version given the specified API version information. + /// </summary> + /// <param name="selector">The extended <see cref="IApiVersionSelector"/>.</param> + /// <param name="model">The <see cref="ApiVersionModel">model</see> to select the version from.</param> + /// <returns>The selected <see cref="ApiVersion">API version</see>.</returns> + public static ApiVersion SelectVersion( this IApiVersionSelector selector, ApiVersionModel model ) + { + ArgumentNullException.ThrowIfNull( selector ); + var context = new DefaultHttpContext(); + return selector.SelectVersion( context.Request, model ); + } +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs index 40c48639..136ad268 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs @@ -453,8 +453,7 @@ private static bool FirstParameterIsOptional( var mapping = ApiVersionMapping.Explicit | ApiVersionMapping.Implicit; var model = apiDescription.ActionDescriptor.GetApiVersionMetadata().Map( mapping ); - var context = new Microsoft.AspNetCore.Http.DefaultHttpContext(); - var defaultApiVersion = options.ApiVersionSelector.SelectVersion( context.Request, model ); + var defaultApiVersion = options.ApiVersionSelector.SelectVersion( model ); return apiVersion == defaultApiVersion; } From c95613642c2fc6fb7a18fb33fed7c7aba4c44bd8 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 15:37:57 -0800 Subject: [PATCH 089/131] Fix matched pattern for implicit type --- .../Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs index 23b412c6..8cf589be 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/Batch/ODataBatchPathMapping.cs @@ -59,9 +59,9 @@ public bool TryGetHandler( HttpContext context, [NotNullWhen( true )] out ODataB var routeData = new RouteValueDictionary(); var candidates = new Dictionary<ApiVersion, int>( capacity: mappings.Length ); - if ( SelectExactMatch( context, routeData, candidates ) is { } handler ) + if ( SelectExactMatch( context, routeData, candidates ) is var handler ) { - return ValueTask.FromResult<ODataBatchHandler?>( handler ); + return ValueTask.FromResult( handler ); } return SelectBestCandidateAsync( context, candidates, routeData, cancellationToken ); From 7c84967cec80a18a09b5e44c935b00861856b424 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 18:45:51 -0800 Subject: [PATCH 090/131] Use frozen collections --- .../Http/HttpRequestExtensions.cs | 6 ++++-- .../Routing/ApiVersionMatcherPolicy.cs | 3 ++- .../Routing/ApiVersionPolicyJumpTable.cs | 11 ++++++----- .../Common/MediaTypeApiVersionReaderBuilder.cs | 18 ++++++++++++------ 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpRequestExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpRequestExtensions.cs index 406e7a61..6ce309bd 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpRequestExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Http/HttpRequestExtensions.cs @@ -16,6 +16,7 @@ public static class HttpRequestExtensions /// <summary> /// Attempts to get the API version from current request path using the provided patterns. /// </summary> + /// <typeparam name="TList">The type of <see cref="IReadOnlyList{T}">read-only list</see>.</typeparam> /// <param name="request">The current <see cref="HttpRequest">HTTP request</see>.</param> /// <param name="routePatterns">The <see cref="IReadOnlyList{T}">read-only list</see> of /// <see cref="RoutePattern">patterns</see> to evaluate.</param> @@ -23,11 +24,12 @@ public static class HttpRequestExtensions /// <param name="apiVersion">The raw API version, if retrieved.</param> /// <returns>True if the raw API version was retrieved; otherwise, false.</returns> [EditorBrowsable( EditorBrowsableState.Never )] - public static bool TryGetApiVersionFromPath( + public static bool TryGetApiVersionFromPath<TList>( this HttpRequest request, - IReadOnlyList<RoutePattern> routePatterns, + TList routePatterns, string constraintName, [NotNullWhen( true )] out string? apiVersion ) + where TList : IReadOnlyList<RoutePattern> { ArgumentNullException.ThrowIfNull( routePatterns ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs index 918d84b2..e43223aa 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionMatcherPolicy.cs @@ -10,6 +10,7 @@ namespace Asp.Versioning.Routing; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Buffers; +using System.Collections.Frozen; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using static Asp.Versioning.ApiVersionMapping; @@ -157,7 +158,7 @@ public PolicyJumpTable BuildJumpTable( int exitDestination, IReadOnlyList<Policy return new ApiVersionPolicyJumpTable( rejection, - destinations, + destinations.ToFrozenDictionary( destinations.Comparer ), NewPolicyFeature( supported, deprecated ), routePatterns ?? [], apiVersionParser, diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionPolicyJumpTable.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionPolicyJumpTable.cs index a7eb03da..24bb4a23 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionPolicyJumpTable.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersionPolicyJumpTable.cs @@ -6,6 +6,7 @@ namespace Asp.Versioning.Routing; using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.Net.Http.Headers; +using System.Collections.Frozen; using System.Runtime.CompilerServices; internal sealed class ApiVersionPolicyJumpTable : PolicyJumpTable @@ -14,17 +15,17 @@ internal sealed class ApiVersionPolicyJumpTable : PolicyJumpTable private readonly bool versionsByUrlOnly; private readonly bool versionsByMediaTypeOnly; private readonly RouteDestination rejection; - private readonly IReadOnlyDictionary<ApiVersion, int> destinations; + private readonly FrozenDictionary<ApiVersion, int> destinations; private readonly ApiVersionPolicyFeature? policyFeature; - private readonly IReadOnlyList<RoutePattern> routePatterns; + private readonly RoutePattern[] routePatterns; private readonly IApiVersionParser parser; private readonly ApiVersioningOptions options; internal ApiVersionPolicyJumpTable( RouteDestination rejection, - IReadOnlyDictionary<ApiVersion, int> destinations, + FrozenDictionary<ApiVersion, int> destinations, ApiVersionPolicyFeature? policyFeature, - IReadOnlyList<RoutePattern> routePatterns, + RoutePattern[] routePatterns, IApiVersionParser parser, IApiVersionParameterSource source, ApiVersioningOptions options ) @@ -35,7 +36,7 @@ internal ApiVersionPolicyJumpTable( this.routePatterns = routePatterns; this.parser = parser; this.options = options; - versionsByUrl = routePatterns.Count > 0; + versionsByUrl = routePatterns.Length > 0; versionsByUrlOnly = source.VersionsByUrl( allowMultipleLocations: false ); versionsByMediaTypeOnly = source.VersionsByMediaType( allowMultipleLocations: false ); } diff --git a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs index 64e9d573..deeec5f8 100644 --- a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs +++ b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs @@ -18,13 +18,16 @@ namespace Asp.Versioning; #if !NETFRAMEWORK using System.Buffers; +using System.Collections.Frozen; #endif using System.Runtime.CompilerServices; using System.Text.RegularExpressions; #if NETFRAMEWORK -using Str = System.String; +using FrozenSet = System.Collections.Generic.HashSet<System.String>; +using Str = string; using StrComparer = System.StringComparer; #else +using FrozenSet = System.Collections.Frozen.FrozenSet<Microsoft.Extensions.Primitives.StringSegment>; using Str = Microsoft.Extensions.Primitives.StringSegment; using StrComparer = Microsoft.Extensions.Primitives.StringSegmentComparer; #endif @@ -142,9 +145,12 @@ public virtual IApiVersionReader Build() => #if NET45 included ?? [], excluded ?? [], -#else +#elif NETFRAMEWORK included ?? new( capacity: 0 ), excluded ?? new( capacity: 0 ), +#else + included?.ToFrozenSet( included.Comparer ) ?? FrozenSet<Str>.Empty, + excluded?.ToFrozenSet( excluded.Comparer ) ?? FrozenSet<Str>.Empty, #endif select ?? DefaultSelector, readers?.ToArray() ?? [] ); @@ -285,15 +291,15 @@ private static string[] ReadMediaTypeParameter( private sealed class BuiltMediaTypeApiVersionReader : IApiVersionReader { private readonly string[] parameters; - private readonly HashSet<Str> included; - private readonly HashSet<Str> excluded; + private readonly FrozenSet included; + private readonly FrozenSet excluded; private readonly SelectorCallback selector; private readonly ReaderCallback[] readers; internal BuiltMediaTypeApiVersionReader( string[] parameters, - HashSet<Str> included, - HashSet<Str> excluded, + FrozenSet included, + FrozenSet excluded, SelectorCallback selector, ReaderCallback[] readers ) { From 49b70cdb56fa3d8742a5acb4f54536ea4469e20c Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 18:46:22 -0800 Subject: [PATCH 091/131] Use default constructors --- .../ApiVersionModelDebugView.cs | 5 +---- .../Asp.Versioning.Abstractions/LinkHeaderValue.cs | 12 +++--------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelDebugView.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelDebugView.cs index 632500d0..00cccc5b 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelDebugView.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ApiVersionModelDebugView.cs @@ -4,12 +4,9 @@ namespace Asp.Versioning; using static System.String; -internal sealed class ApiVersionModelDebugView +internal sealed class ApiVersionModelDebugView( ApiVersionModel model ) { private const string Comma = ", "; - private readonly ApiVersionModel model; - - public ApiVersionModelDebugView( ApiVersionModel model ) => this.model = model; public bool VersionNeutral => model.IsApiVersionNeutral; diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs index 44e0e095..94088bc9 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/LinkHeaderValue.cs @@ -532,16 +532,10 @@ private static ref StringSegment ValidateKey( ref StringSegment key ) } } - private struct TargetAttributesEnumerator : IEnumerable<KeyValuePair<StringSegment, StringSegment>> + private struct TargetAttributesEnumerator( StringSegment remaining ) + : IEnumerable<KeyValuePair<StringSegment, StringSegment>> { - private readonly StringSegment remaining; - private int start; - - public TargetAttributesEnumerator( StringSegment remaining ) - { - this.remaining = remaining; - start = 0; - } + private int start = 0; public IEnumerator<KeyValuePair<StringSegment, StringSegment>> GetEnumerator() { From 34c2c60252f8748ae2a3909f3296f8b89dd81a92 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 18:46:31 -0800 Subject: [PATCH 092/131] Use static lambda --- .../src/Common.OData/OData/VersionedODataModelBuilder.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Common/src/Common.OData/OData/VersionedODataModelBuilder.cs b/src/Common/src/Common.OData/OData/VersionedODataModelBuilder.cs index b5fc95ea..a18cc8dc 100644 --- a/src/Common/src/Common.OData/OData/VersionedODataModelBuilder.cs +++ b/src/Common/src/Common.OData/OData/VersionedODataModelBuilder.cs @@ -19,9 +19,12 @@ public partial class VersionedODataModelBuilder /// <summary> /// Gets or sets the factory method used to create model builders. /// </summary> - /// <value>The factory <see cref="Func{TResult}">method</see> used to create <see cref="ODataModelBuilder">model builders</see>.</value> - /// <remarks>The default implementation creates default instances of the <see cref="ODataConventionModelBuilder"/> class.</remarks> - public Func<ODataModelBuilder> ModelBuilderFactory { get; set; } = () => new ODataConventionModelBuilder().EnableLowerCamelCase(); + /// <value>The factory <see cref="Func{TResult}">method</see> used to create + /// <see cref="ODataModelBuilder">model builders</see>.</value> + /// <remarks>The default implementation creates default instances of the + /// <see cref="ODataConventionModelBuilder"/> class.</remarks> + public Func<ODataModelBuilder> ModelBuilderFactory { get; set; } = + static () => new ODataConventionModelBuilder().EnableLowerCamelCase(); /// <summary> /// Gets or sets the default model configuration. From 791d8a416163c02cdbb2c49f4f88c45d6e1d5aa0 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 18:46:41 -0800 Subject: [PATCH 093/131] Add to dictionary --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 31fcbe59..974ded80 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,7 +7,7 @@ root = true [*] indent_style = space vsspell_section_id = 41b65011239a40959ccaae2a4ec7044a -vsspell_ignored_words_41b65011239a40959ccaae2a4ec7044a = clr|Edm|middleware|Mvc|odata|Validator|Accessor|app|inline +vsspell_ignored_words_41b65011239a40959ccaae2a4ec7044a = Accessor|app|clr|Edm|inline|middleware|Mvc|odata|Validator|Deconstruct # code files [*.{cs,csx,vb,vbx}] From cece3a11ec68d9ee5e174e7af534d36c9d2028cc Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 18:48:10 -0800 Subject: [PATCH 094/131] Simplify initialization --- .../Controllers/HttpControllerDescriptorGroupTest.cs | 4 ++-- .../DefaultApiControllerFilterTest.cs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Controllers/HttpControllerDescriptorGroupTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Controllers/HttpControllerDescriptorGroupTest.cs index 2d4227c3..41ad70d7 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Controllers/HttpControllerDescriptorGroupTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Controllers/HttpControllerDescriptorGroupTest.cs @@ -51,12 +51,12 @@ public void get_custom_attributes_should_aggregate_attributes() var configuration = new HttpConfiguration(); descriptor1.Setup( d => d.GetCustomAttributes<ApiVersionAttribute>( It.IsAny<bool>() ) ) - .Returns( () => new Collection<ApiVersionAttribute>() { new ApiVersionAttribute( "1.0" ) } ); + .Returns( () => new Collection<ApiVersionAttribute>() { new( "1.0" ) } ); descriptor1.Object.Configuration = configuration; descriptor1.Object.Properties[typeof( ApiVersionModel )] = new ApiVersionModel( new ApiVersion( 1, 0 ) ); descriptor2.Setup( d => d.GetCustomAttributes<ApiVersionAttribute>( It.IsAny<bool>() ) ) - .Returns( () => new Collection<ApiVersionAttribute>() { new ApiVersionAttribute( "2.0" ) } ); + .Returns( () => new Collection<ApiVersionAttribute>() { new( "2.0" ) } ); descriptor2.Object.Configuration = configuration; descriptor2.Object.Properties[typeof( ApiVersionModel )] = new ApiVersionModel( new ApiVersion( 2, 0 ) ); diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApplicationModels/DefaultApiControllerFilterTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApplicationModels/DefaultApiControllerFilterTest.cs index a156f8cc..b662cf76 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApplicationModels/DefaultApiControllerFilterTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApplicationModels/DefaultApiControllerFilterTest.cs @@ -17,9 +17,9 @@ public void apply_should_not_filter_list_without_specifications() var attributes = Array.Empty<object>(); var controllers = new List<ControllerModel>() { - new ControllerModel( controllerType, attributes ), - new ControllerModel( controllerType, attributes ), - new ControllerModel( controllerType, attributes ), + new( controllerType, attributes ), + new( controllerType, attributes ), + new( controllerType, attributes ), }; // act @@ -44,9 +44,9 @@ public void apply_should_filter_controllers() var attributes = Array.Empty<object>(); var controllers = new List<ControllerModel>() { - new ControllerModel( controllerType, attributes ), - new ControllerModel( controllerBaseType, attributes ), - new ControllerModel( controllerType, attributes ), + new( controllerType, attributes ), + new( controllerBaseType, attributes ), + new( controllerType, attributes ), }; // act From 89cc90c0c770caa17e66cf58d7f6bd400ee23866 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 18:51:41 -0800 Subject: [PATCH 095/131] Use collection initializers --- .../BooksController.cs | 6 +-- .../BooksController.cs | 6 +-- .../ApiVersionTest.cs | 40 +++++++++---------- .../Simulators/V1/BooksController.cs | 6 +-- .../ApiDescriptionExtensionsTest.cs | 2 +- .../HttpConfigurationExtensionsTest.cs | 2 +- .../Description/InternalTypeExtensions.cs | 4 +- .../Simulators/V1/BooksController.cs | 6 +-- .../ModelConfigurationFeatureProviderTest.cs | 2 +- .../Routing/ApiVersionMatcherPolicyTest.cs | 8 ++-- .../TestActionDescriptorCollectionProvider.cs | 2 +- .../OData/DefaultModelTypeBuilderTest.cs | 2 +- 12 files changed, 43 insertions(+), 43 deletions(-) diff --git a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/BooksController.cs b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/BooksController.cs index 05dbf26b..6017627b 100644 --- a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/BooksController.cs +++ b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/BooksController.cs @@ -14,15 +14,15 @@ [RoutePrefix( "api/books" )] public class BooksController : ApiController { - private static readonly Book[] books = new Book[] - { + private static readonly Book[] books = + [ new() { Id = "9781847490599", Title = "Anna Karenina", Author = "Leo Tolstoy", Published = 1878 }, new() { Id = "9780198800545", Title = "War and Peace", Author = "Leo Tolstoy", Published = 1869 }, new() { Id = "9780684801520", Title = "The Great Gatsby", Author = "F. Scott Fitzgerald", Published = 1925 }, new() { Id = "9780486280615", Title = "The Adventures of Huckleberry Finn", Author = "Mark Twain", Published = 1884 }, new() { Id = "9780140430820", Title = "Moby Dick", Author = "Herman Melville", Published = 1851 }, new() { Id = "9780060934347", Title = "Don Quixote", Author = "Miguel de Cervantes", Published = 1605 }, - }; + ]; /// <summary> /// Gets all books. diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/BooksController.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/BooksController.cs index 6266bc41..68e97255 100644 --- a/examples/AspNetCore/OData/SomeODataOpenApiExample/BooksController.cs +++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/BooksController.cs @@ -13,15 +13,15 @@ [Route( "api/[controller]" )] public class BooksController : ControllerBase { - private static readonly Book[] books = new Book[] - { + private static readonly Book[] books = + [ new() { Id = "9781847490599", Title = "Anna Karenina", Author = "Leo Tolstoy", Published = 1878 }, new() { Id = "9780198800545", Title = "War and Peace", Author = "Leo Tolstoy", Published = 1869 }, new() { Id = "9780684801520", Title = "The Great Gatsby", Author = "F. Scott Fitzgerald", Published = 1925 }, new() { Id = "9780486280615", Title = "The Adventures of Huckleberry Finn", Author = "Mark Twain", Published = 1884 }, new() { Id = "9780140430820", Title = "Moby Dick", Author = "Herman Melville", Published = 1851 }, new() { Id = "9780060934347", Title = "Don Quixote", Author = "Miguel de Cervantes", Published = 1605 }, - }; + ]; /// <summary> /// Gets all books. diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionTest.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionTest.cs index 07186a3e..f6069976 100644 --- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionTest.cs +++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionTest.cs @@ -595,27 +595,27 @@ public void api_version_1_ge_api_version_2_should_return_expected_result( string } public static IEnumerable<object[]> FormatData => - new[] + new object[][] { - new[] { null, "2013-08-06.1.1-Alpha", "2013-08-06.1.1-Alpha" }, - new[] { "", "2013-08-06.1.1-Alpha", "2013-08-06.1.1-Alpha" }, - new[] { "F", "2013-08-06.1.1-Alpha", "2013-08-06.1.1-Alpha" }, - new[] { "G", "2013-08-06", "2013-08-06" }, - new[] { "GG", "2013-08-06-Alpha", "2013-08-06-Alpha" }, - new[] { "G", "1.1", "" }, - new[] { "G", "1.1-Alpha", "" }, - new[] { "G", "2013-08-06.1.1", "2013-08-06" }, - new[] { "GG", "2013-08-06.1.1-Alpha", "2013-08-06-Alpha" }, - new[] { "V", "2013-08-06", "" }, - new[] { "VVVV", "2013-08-06-Alpha", "" }, - new[] { "VV", "1.1", "1.1" }, - new[] { "VVVV", "1.1-Alpha", "1.1-Alpha" }, - new[] { "VV", "2013-08-06.1.1", "1.1" }, - new[] { "VVVV", "2013-08-06.1.1-Alpha", "1.1-Alpha" }, - new[] { "S", "1.1-Alpha", "Alpha" }, - new[] { "'v'VVV", "1.1", "v1.1" }, - new[] { "'Major': %V, 'Minor': %v", "1.1", "Major: 1, Minor: 1" }, - new[] { "MMM yyyy '('S')'", "2013-08-06-preview.1", "Aug 2013 (preview.1)" }, + [null, "2013-08-06.1.1-Alpha", "2013-08-06.1.1-Alpha"], + ["", "2013-08-06.1.1-Alpha", "2013-08-06.1.1-Alpha"], + ["F", "2013-08-06.1.1-Alpha", "2013-08-06.1.1-Alpha"], + ["G", "2013-08-06", "2013-08-06"], + ["GG", "2013-08-06-Alpha", "2013-08-06-Alpha"], + ["G", "1.1", ""], + ["G", "1.1-Alpha", ""], + ["G", "2013-08-06.1.1", "2013-08-06"], + ["GG", "2013-08-06.1.1-Alpha", "2013-08-06-Alpha"], + ["V", "2013-08-06", ""], + ["VVVV", "2013-08-06-Alpha", ""], + ["VV", "1.1", "1.1"], + ["VVVV", "1.1-Alpha", "1.1-Alpha"], + ["VV", "2013-08-06.1.1", "1.1"], + ["VVVV", "2013-08-06.1.1-Alpha", "1.1-Alpha"], + ["S", "1.1-Alpha", "Alpha"], + ["'v'VVV", "1.1", "v1.1"], + ["'Major': %V, 'Minor': %v", "1.1", "Major: 1, Minor: 1"], + ["MMM yyyy '('S')'", "2013-08-06-preview.1", "Aug 2013 (preview.1)"], }; #if NETFRAMEWORK diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs index 24d7a24e..bab6814e 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs @@ -17,15 +17,15 @@ namespace Asp.Versioning.Simulators.V1; [RoutePrefix( "api/books" )] public class BooksController : ApiController { - private static readonly Book[] books = new Book[] - { + private static readonly Book[] books = + [ new() { Id = "9781847490599", Title = "Anna Karenina", Author = "Leo Tolstoy", Published = 1878 }, new() { Id = "9780198800545", Title = "War and Peace", Author = "Leo Tolstoy", Published = 1869 }, new() { Id = "9780684801520", Title = "The Great Gatsby", Author = "F. Scott Fitzgerald", Published = 1925 }, new() { Id = "9780486280615", Title = "The Adventures of Huckleberry Finn", Author = "Mark Twain", Published = 1884 }, new() { Id = "9780140430820", Title = "Moby Dick", Author = "Herman Melville", Published = 1851 }, new() { Id = "9780060934347", Title = "Don Quixote", Author = "Miguel de Cervantes", Published = 1605 }, - }; + ]; /// <summary> /// Gets all books. diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/System.Web.Http/Description/ApiDescriptionExtensionsTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/System.Web.Http/Description/ApiDescriptionExtensionsTest.cs index 88a780dc..5d56a57a 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/System.Web.Http/Description/ApiDescriptionExtensionsTest.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/System.Web.Http/Description/ApiDescriptionExtensionsTest.cs @@ -68,7 +68,7 @@ private static VersionedApiDescription CreateApiDescription( IEdmModel model ) { var configuration = new HttpConfiguration(); var controllerType = typeof( Asp.Versioning.Simulators.V1.OrdersController ); - var actionMethod = controllerType.GetRuntimeMethod( "Get", new[] { typeof( int ) } ); + var actionMethod = controllerType.GetRuntimeMethod( "Get", [typeof( int )] ); var controllerDescriptor = new HttpControllerDescriptor( configuration, "Orders", controllerType ); var actionDescriptor = new ReflectedHttpActionDescriptor( controllerDescriptor, actionMethod ); var apiDescription = new VersionedApiDescription() diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs index 4544139c..c2e86cbc 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/System.Web.Http/HttpConfigurationExtensionsTest.cs @@ -46,7 +46,7 @@ public void map_versioned_odata_route_should_return_expected_result( string rout var selector = GetODataRootContainer( configuration, routeName ).GetRequiredService<IEdmModelSelector>(); var routingConventions = GetRoutingConventions( configuration, route ); - selector.ApiVersions.Should().Equal( new ApiVersion[] { new( 1, 0 ), new( 2, 0 ) } ); + selector.ApiVersions.Should().Equal( [new( 1, 0 ), new( 2, 0 )] ); routingConventions[0].Should().BeOfType<VersionedAttributeRoutingConvention>(); routingConventions[1].Should().BeOfType<VersionedMetadataRoutingConvention>(); routingConventions.OfType<MetadataRoutingConvention>().Should().BeEmpty(); diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/InternalTypeExtensions.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/InternalTypeExtensions.cs index b2458e22..4dd18890 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/InternalTypeExtensions.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/InternalTypeExtensions.cs @@ -20,9 +20,9 @@ internal static void EnsureInitialized( this IHttpRoute route, Func<IReadOnlyCol Debug.Assert( route.GetType().Name == "RouteCollectionRoute", "Extension method only intended to support testing RouteCollectionRoute.EnsureInitialized" ); var type = route.GetType(); - var method = type.GetRuntimeMethod( nameof( EnsureInitialized ), new[] { initializer.GetType() } ); + var method = type.GetRuntimeMethod( nameof( EnsureInitialized ), [initializer.GetType()] ); - method.Invoke( route, new object[] { initializer } ); + method.Invoke( route, [initializer] ); } internal static IDirectRouteBuilder NewDirectRouteBuilder( IReadOnlyCollection<HttpActionDescriptor> actions, bool targetIsAction ) diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs index 438a0099..d6b7e96a 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Simulators/V1/BooksController.cs @@ -16,15 +16,15 @@ namespace Asp.Versioning.Simulators.V1; [Route( "api/[controller]" )] public class BooksController : ControllerBase { - private static readonly Book[] books = new Book[] - { + private static readonly Book[] books = + [ new() { Id = "9781847490599", Title = "Anna Karenina", Author = "Leo Tolstoy", Published = 1878 }, new() { Id = "9780198800545", Title = "War and Peace", Author = "Leo Tolstoy", Published = 1869 }, new() { Id = "9780684801520", Title = "The Great Gatsby", Author = "F. Scott Fitzgerald", Published = 1925 }, new() { Id = "9780486280615", Title = "The Adventures of Huckleberry Finn", Author = "Mark Twain", Published = 1884 }, new() { Id = "9780140430820", Title = "Moby Dick", Author = "Herman Melville", Published = 1851 }, new() { Id = "9780060934347", Title = "Don Quixote", Author = "Miguel de Cervantes", Published = 1605 }, - }; + ]; /// <summary> /// Gets all books. diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs index 8255590f..5e245254 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs @@ -28,7 +28,7 @@ public void populate_feature_should_discover_valid_model_configurations() provider.PopulateFeature( partManager.ApplicationParts, feature ); // assert - feature.ModelConfigurations.Should().Equal( new[] { typeof( PublicModelConfiguration ) } ); + feature.ModelConfigurations.Should().Equal( [typeof( PublicModelConfiguration )] ); } } diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Routing/ApiVersionMatcherPolicyTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Routing/ApiVersionMatcherPolicyTest.cs index 17a20ce7..bc8d7e9c 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Routing/ApiVersionMatcherPolicyTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Routing/ApiVersionMatcherPolicyTest.cs @@ -97,7 +97,7 @@ public async Task apply_should_have_candidate_for_matched_api_version() var model = new ApiVersionModel( new ApiVersion( 1, 0 ) ); var items = new object[] { new ApiVersionMetadata( model, model ) }; var endpoint = new Endpoint( Limbo, new( items ), default ); - var candidates = new CandidateSet( new[] { endpoint }, new[] { new RouteValueDictionary() }, new[] { 0 } ); + var candidates = new CandidateSet( new[] { endpoint }, new[] { new RouteValueDictionary() }, [0] ); var policy = NewApiVersionMatcherPolicy(); feature.SetupProperty( f => f.RequestedApiVersion, new ApiVersion( 1, 0 ) ); @@ -125,7 +125,7 @@ public async Task apply_should_use_400_endpoint_for_unmatched_api_version() var model = new ApiVersionModel( new ApiVersion( 1, 0 ) ); var items = new object[] { new ApiVersionMetadata( model, model ) }; var endpoint = new Endpoint( Limbo, new( items ), default ); - var candidates = new CandidateSet( new[] { endpoint }, new[] { new RouteValueDictionary() }, new[] { 0 } ); + var candidates = new CandidateSet( new[] { endpoint }, new[] { new RouteValueDictionary() }, [0] ); var httpContext = NewHttpContext( feature ); // act @@ -192,7 +192,7 @@ public async Task apply_should_use_400_endpoint_for_unspecified_api_version() var model = new ApiVersionModel( new ApiVersion( 1, 0 ) ); var items = new object[] { new ApiVersionMetadata( model, model ) }; var endpoint = new Endpoint( Limbo, new( items ), "Test" ); - var candidates = new CandidateSet( new[] { endpoint }, new[] { new RouteValueDictionary() }, new[] { 0 } ); + var candidates = new CandidateSet( new[] { endpoint }, new[] { new RouteValueDictionary() }, [0] ); var httpContext = NewHttpContext( feature ); // act @@ -210,7 +210,7 @@ public async Task apply_should_have_candidate_for_unspecified_api_version() var model = new ApiVersionModel( new ApiVersion( 1, 0 ) ); var items = new object[] { new ApiVersionMetadata( model, model ) }; var endpoint = new Endpoint( Limbo, new( items ), default ); - var candidates = new CandidateSet( new[] { endpoint }, new[] { new RouteValueDictionary() }, new[] { 0 } ); + var candidates = new CandidateSet( new[] { endpoint }, new[] { new RouteValueDictionary() }, [0] ); var options = new ApiVersioningOptions() { AssumeDefaultVersionWhenUnspecified = true }; var policy = NewApiVersionMatcherPolicy( options ); diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestActionDescriptorCollectionProvider.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestActionDescriptorCollectionProvider.cs index efe83dea..4ed2d30e 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestActionDescriptorCollectionProvider.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/TestActionDescriptorCollectionProvider.cs @@ -17,7 +17,7 @@ public TestActionDescriptorCollectionProvider( ActionDescriptor action, params A if ( otherActions.Length == 0 ) { - actions = new ActionDescriptor[] { action }; + actions = [action]; } else { diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/DefaultModelTypeBuilderTest.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/DefaultModelTypeBuilderTest.cs index fa81ce4c..100af41f 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/DefaultModelTypeBuilderTest.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/DefaultModelTypeBuilderTest.cs @@ -383,7 +383,7 @@ public void substitute_should_get_attributes_from_property_that_has_attributes_t var property = substitutionType.GetRuntimeProperty( "Salary" ); var attributeWithParams = property.GetCustomAttribute<AllowedRolesAttribute>(); - attributeWithParams.AllowedRoles.Should().BeEquivalentTo( new[] { "Manager", "Employer" } ); + attributeWithParams.AllowedRoles.Should().BeEquivalentTo( ["Manager", "Employer"] ); } [Fact] From 499fa9e081dc3c0f2124feb930d298422176bea0 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 18:53:08 -0800 Subject: [PATCH 096/131] Use readonly --- .../OData/ModelConfigurationFeatureProviderTest.cs | 2 +- src/Common/src/Common.Backport/HashCode.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs index 5e245254..0987219f 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/OData/ModelConfigurationFeatureProviderTest.cs @@ -41,7 +41,7 @@ namespace ModelConfigurations { internal struct ValueTypeModelConfiguration : IModelConfiguration { - public void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { } + public readonly void Apply( ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix ) { } } internal sealed class PrivateModelConfiguration : IModelConfiguration diff --git a/src/Common/src/Common.Backport/HashCode.cs b/src/Common/src/Common.Backport/HashCode.cs index bd4a7e7b..d9d2794a 100644 --- a/src/Common/src/Common.Backport/HashCode.cs +++ b/src/Common/src/Common.Backport/HashCode.cs @@ -3,6 +3,7 @@ #pragma warning disable IDE0079 #pragma warning disable IDE0007 // Use implicit type #pragma warning disable IDE0079 // Remove unnecessary suppression +#pragma warning disable IDE0251 // Make member 'readonly' #pragma warning disable SA1108 // Block statements should not contain embedded comments #pragma warning disable SA1132 // Do not combine fields #pragma warning disable SA1200 // Using directives should be placed correctly From 5cbb8904d953d00cfcf90eb859eecaa5eb1a11e5 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 19:55:28 -0800 Subject: [PATCH 097/131] Clean up code analysis --- .editorconfig | 3 --- .../src/Common.Backport/NullableAttributes.cs | 5 ++++- ...aActionQueryOptionsConventionBuilder{T}.cs | 2 ++ ...trollerQueryOptionsConventionBuilder{T}.cs | 2 ++ .../ODataQueryOptionsConventionBuilder.cs | 2 ++ .../MediaTypeApiVersionReaderBuilder.cs | 22 +++++++++++-------- 6 files changed, 23 insertions(+), 13 deletions(-) diff --git a/.editorconfig b/.editorconfig index 974ded80..0f78ff19 100644 --- a/.editorconfig +++ b/.editorconfig @@ -90,9 +90,6 @@ csharp_space_between_method_declaration_parameter_list_parentheses = true csharp_space_between_method_call_parameter_list_parentheses = true csharp_space_between_parentheses = control_flow_statements, expressions -# ide code suppressions -# dotnet_diagnostic.IDE0079.severity = none - # style code suppressions dotnet_diagnostic.SA1002.severity = none dotnet_diagnostic.SA1003.severity = none diff --git a/src/Common/src/Common.Backport/NullableAttributes.cs b/src/Common/src/Common.Backport/NullableAttributes.cs index c5b2c2ae..2cadf664 100644 --- a/src/Common/src/Common.Backport/NullableAttributes.cs +++ b/src/Common/src/Common.Backport/NullableAttributes.cs @@ -1,6 +1,9 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable SA1402, SA1649 +#pragma warning disable IDE0060 +#pragma warning disable IDE0079 +#pragma warning disable SA1402 +#pragma warning disable SA1649 // REF: https://github.com/dotnet/runtime/blob/1c8d37af80667daffb3cb80ce0fe915621e8f039/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs // diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilder{T}.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilder{T}.cs index b662aab7..31dbc4c2 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilder{T}.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataActionQueryOptionsConventionBuilder{T}.cs @@ -32,9 +32,11 @@ public class ODataActionQueryOptionsConventionBuilder<T> : IODataActionQueryOptionsConventionBuilder<T> where T : notnull #if NETFRAMEWORK +#pragma warning disable IDE0079 #pragma warning disable SA1001 // Commas should be spaced correctly , IHttpController #pragma warning restore SA1001 // Commas should be spaced correctly +#pragma warning restore IDE0079 #endif { /// <summary> diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionsConventionBuilder{T}.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionsConventionBuilder{T}.cs index 3d5322bd..8f51e97d 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionsConventionBuilder{T}.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataControllerQueryOptionsConventionBuilder{T}.cs @@ -20,9 +20,11 @@ public class ODataControllerQueryOptionsConventionBuilder<T> : IODataActionQueryOptionsConventionBuilder<T> where T : notnull #if NETFRAMEWORK +#pragma warning disable IDE0079 #pragma warning disable SA1001 // Commas should be spaced correctly , IHttpController #pragma warning restore SA1001 // Commas should be spaced correctly +#pragma warning restore IDE0079 #endif { private ODataActionQueryOptionsConventionBuilderCollection<T>? actionBuilders; diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs index 99c6a919..8d0921f9 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataQueryOptionsConventionBuilder.cs @@ -59,9 +59,11 @@ public IODataQueryOptionDescriptionProvider DescriptionProvider public virtual ODataControllerQueryOptionsConventionBuilder<TController> Controller<TController>() where TController : notnull #if NETFRAMEWORK +#pragma warning disable IDE0079 #pragma warning disable SA1001 // Commas should be spaced correctly , IHttpController #pragma warning restore SA1001 // Commas should be spaced correctly +#pragma warning restore IDE0079 #endif { var key = typeof( TController ); diff --git a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs index deeec5f8..4c3339d7 100644 --- a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs +++ b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs @@ -11,30 +11,34 @@ using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeWithQualityHeaderValue; #else using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; +using System.Collections.Frozen; #endif namespace Asp.Versioning; #if !NETFRAMEWORK using System.Buffers; -using System.Collections.Frozen; #endif using System.Runtime.CompilerServices; using System.Text.RegularExpressions; #if NETFRAMEWORK -using FrozenSet = System.Collections.Generic.HashSet<System.String>; -using Str = string; -using StrComparer = System.StringComparer; +using FrozenSet = HashSet<String>; #else -using FrozenSet = System.Collections.Frozen.FrozenSet<Microsoft.Extensions.Primitives.StringSegment>; -using Str = Microsoft.Extensions.Primitives.StringSegment; -using StrComparer = Microsoft.Extensions.Primitives.StringSegmentComparer; +using FrozenSet = FrozenSet<StringSegment>; #endif -using static Asp.Versioning.ApiVersionParameterLocation; -using static System.StringComparison; using ReaderCallback = Func<IReadOnlyList<MediaTypeHeaderValue>, IReadOnlyList<string>>; using SelectorCallback = Func<HttpRequest, IReadOnlyList<string>, IReadOnlyList<string>>; +#if NETFRAMEWORK +using Str = String; +using StrComparer = StringComparer; +#else +using Str = StringSegment; +using StrComparer = StringSegmentComparer; +#endif +using static Asp.Versioning.ApiVersionParameterLocation; +using static System.StringComparison; /// <summary> /// Represents a builder for an API version reader that reads the value from a media type HTTP header in the request. From ce1e7b243cd57bb65bc87015f447d1500069f0e8 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 20:37:32 -0800 Subject: [PATCH 098/131] More code analysis clean up --- .editorconfig | 3 +++ .../netstandard2.0/FormatWriter.cs | 2 +- .../ApiVersionTest.cs | 2 ++ ... using a url segment and attribute-based routing.cs | 2 ++ ...using a url segment and convention-based routing.cs | 2 ++ .../OData/ODataAcceptanceTest.cs | 2 ++ .../ODataValidationSettingsConventionTest.cs | 5 +++++ .../Routing/VersionedAttributeRoutingConventionTest.cs | 2 ++ .../Controllers/ActionSelectorCacheItem.cs | 2 +- .../ApiExplorer/VersionedApiExplorerTest.cs | 3 +++ .../Description/ApiDescriptionGroupCollectionTest.cs | 2 ++ .../Models/GenericMutableObject{T}.cs | 2 ++ .../Models/MutableObject.cs | 2 ++ .../Controllers/HttpControllerDescriptorGroupTest.cs | 2 ++ .../Dispatcher/ApiVersionControllerSelectorTest.cs | 3 +++ .../MediaTypeApiVersionReaderBuilderTest.cs | 4 ++-- .../MediaTypeApiVersionReaderTest.cs | 2 +- .../QueryStringApiVersionReaderTest.cs | 4 ++-- .../ReportApiVersionsAttributeTest.cs | 2 ++ .../Simulators/AdminController.cs | 2 ++ .../OData/ODataAcceptanceTest.cs | 2 ++ .../ODataValidationSettingsConventionTest.cs | 5 +++++ .../Routing/VersionedAttributeRoutingConventionTest.cs | 2 ++ .../MediaTypeApiVersionReaderTest.cs | 2 ++ .../ApiDescriptionExtensionsTest.cs | 2 ++ .../ApiVersioningApplicationModelProviderTest.cs | 2 ++ .../ReportApiVersionsAttributeTest.cs | 2 ++ src/Common/src/Common/ApiVersionReader.cs | 2 +- .../src/Common/MediaTypeApiVersionReaderBuilder.cs | 2 +- .../Conventions/ApiVersionConventionBuilderTest.cs | 2 ++ .../ControllerApiVersionConventionBuilderTTest.cs | 1 + .../Conventions/DefaultControllerNameConventionTest.cs | 10 +++++----- .../Conventions/GroupedControllerNameConventionTest.cs | 8 ++++---- .../OriginalControllerNameConventionTest.cs | 8 ++++---- .../ODataActionQueryOptionsConventionBuilderTTest.cs | 1 + .../ODataActionQueryOptionsConventionBuilderTest.cs | 1 + .../ODataQueryOptionsConventionBuilderTest.cs | 4 +++- .../Common.OData.ApiExplorer.Tests/OData/Company.cs | 8 ++++---- .../Common.OData.ApiExplorer.Tests/OData/Contact.cs | 8 ++++---- .../Common.OData.ApiExplorer.Tests/OData/Employer.cs | 5 +++-- 40 files changed, 94 insertions(+), 33 deletions(-) diff --git a/.editorconfig b/.editorconfig index 0f78ff19..68505e1c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -90,6 +90,9 @@ csharp_space_between_method_declaration_parameter_list_parentheses = true csharp_space_between_method_call_parameter_list_parentheses = true csharp_space_between_parentheses = control_flow_statements, expressions +# primary construcrtors +csharp_style_prefer_primary_constructors = false:none + # style code suppressions dotnet_diagnostic.SA1002.severity = none dotnet_diagnostic.SA1003.severity = none diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/FormatWriter.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/FormatWriter.cs index 3fa0645f..b0c8019a 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/FormatWriter.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/netstandard2.0/FormatWriter.cs @@ -45,7 +45,7 @@ internal FormatWriter( public bool Succeeded { get; private set; } - public int Written => totalWritten; + public readonly int Written => totalWritten; public void Write( in FormatToken token ) { diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionTest.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionTest.cs index f6069976..11c896a5 100644 --- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionTest.cs +++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ApiVersionTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: DX + namespace Asp.Versioning; public partial class ApiVersionTest diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and attribute-based routing.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and attribute-based routing.cs index 0beae68c..8cd50259 100644 --- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and attribute-based routing.cs +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and attribute-based routing.cs @@ -2,6 +2,8 @@ #pragma warning disable IDE1006 // Naming Styles +//// Ignore Spelling: Dbased + namespace given_a_versioned_ApiController_per_namespace; using Asp.Versioning; diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and convention-based routing.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and convention-based routing.cs index 63801fb2..88f11bf8 100644 --- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and convention-based routing.cs +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/Http/UsingNamespace/given a versioned ApiController per namespace/when using a url segment and convention-based routing.cs @@ -2,6 +2,8 @@ #pragma warning disable IDE1006 // Naming Styles +//// Ignore Spelling: Dbased + namespace given_a_versioned_ApiController_per_namespace; using Asp.Versioning; diff --git a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/ODataAcceptanceTest.cs b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/ODataAcceptanceTest.cs index d69604f8..9c420a43 100644 --- a/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/ODataAcceptanceTest.cs +++ b/src/AspNet/Acceptance/Asp.Versioning.WebApi.Acceptance.Tests/OData/ODataAcceptanceTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dspecific + namespace Asp.Versioning.OData; using static System.Net.HttpStatusCode; diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs index 9d3960fd..72c1025f 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs @@ -1,5 +1,10 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dlike +//// Ignore Spelling: Multipart +//// Ignore Spelling: nonaction +//// Ignore Spelling: nonquery + namespace Asp.Versioning.Conventions; using Asp.Versioning.Description; diff --git a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs index a42188cc..f06fc620 100644 --- a/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs +++ b/src/AspNet/OData/test/Asp.Versioning.WebApi.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dneutral + namespace Asp.Versioning.Routing; using Microsoft.AspNet.OData; diff --git a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs index a56c19c1..4ae17299 100644 --- a/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs +++ b/src/AspNet/WebApi/src/Asp.Versioning.WebApi/Controllers/ActionSelectorCacheItem.cs @@ -23,7 +23,7 @@ namespace Asp.Versioning.Controllers; /// </summary> internal sealed class ActionSelectorCacheItem { - private static readonly HttpMethod[] cacheListMethodKinds = new[] { HttpMethod.Get, HttpMethod.Put, HttpMethod.Post }; + private static readonly HttpMethod[] cacheListMethodKinds = [HttpMethod.Get, HttpMethod.Put, HttpMethod.Post]; private readonly HttpControllerDescriptor controllerDescriptor; private readonly CandidateAction[] combinedCandidateActions; private readonly Dictionary<HttpActionDescriptor, string[]> actionParameterNames = []; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/ApiExplorer/VersionedApiExplorerTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/ApiExplorer/VersionedApiExplorerTest.cs index e42d2651..42c21028 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/ApiExplorer/VersionedApiExplorerTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/ApiExplorer/VersionedApiExplorerTest.cs @@ -1,7 +1,10 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dcase +//// Ignore Spelling: Dinsensitive namespace Asp.Versioning.ApiExplorer; + using Asp.Versioning.Models; using Asp.Versioning.Routing; using Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiDescriptionGroupCollectionTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiDescriptionGroupCollectionTest.cs index 55dc4e1d..bca97b12 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiDescriptionGroupCollectionTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Description/ApiDescriptionGroupCollectionTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: denormalized + namespace Asp.Versioning.Description; using System.Collections.ObjectModel; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/GenericMutableObject{T}.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/GenericMutableObject{T}.cs index db85d146..98fc3dbd 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/GenericMutableObject{T}.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/GenericMutableObject{T}.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Foo + namespace Asp.Versioning.Models; public class GenericMutableObject<T> : List<T> diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/MutableObject.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/MutableObject.cs index d5b0daaf..cef08ef1 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/MutableObject.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.ApiExplorer.Tests/Models/MutableObject.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Foo + namespace Asp.Versioning.Models; public class MutableObject diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Controllers/HttpControllerDescriptorGroupTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Controllers/HttpControllerDescriptorGroupTest.cs index 41ad70d7..0b9ba137 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Controllers/HttpControllerDescriptorGroupTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Controllers/HttpControllerDescriptorGroupTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: eq + namespace Asp.Versioning.Controllers; using System.Collections.ObjectModel; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.cs index 25c3be0e..e4c523fb 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Dispatcher/ApiVersionControllerSelectorTest.cs @@ -1,5 +1,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dbased +//// Ignore Spelling: Dneutral + namespace Asp.Versioning.Dispatcher; using Asp.Versioning; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderBuilderTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderBuilderTest.cs index b980fc31..499d07fe 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderBuilderTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderBuilderTest.cs @@ -125,7 +125,7 @@ public void read_should_retrieve_version_from_content_type_and_accept() var versions = reader.Read( request ); // assert - versions.Should().BeEquivalentTo( new[] { "1.5", "2.0" } ); + versions.Should().BeEquivalentTo( ["1.5", "2.0"] ); } [Fact] @@ -246,7 +246,7 @@ public void read_should_only_retrieve_included_media_types() [InlineData( "application/vnd-v{v}+json", "v", "application/vnd-v2.1+json", "2.1" )] [InlineData( "application/vnd-v{ver}+json", "ver", "application/vnd-v2022-11-01+json", "2022-11-01" )] [InlineData( "application/vnd-{version}+xml", "version", "application/vnd-1.1-beta+xml", "1.1-beta" )] - public void read_should_retreive_version_from_media_type_template( + public void read_should_retrieve_version_from_media_type_template( string template, string parameterName, string mediaType, diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderTest.cs index bc4487b9..ea9ee138 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/MediaTypeApiVersionReaderTest.cs @@ -111,7 +111,7 @@ public void read_should_retrieve_version_from_content_type_and_accept() var versions = reader.Read( request ); // assert - versions.Should().BeEquivalentTo( new[] { "1.5", "2.0" } ); + versions.Should().BeEquivalentTo( ["1.5", "2.0"] ); } [Fact] diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/QueryStringApiVersionReaderTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/QueryStringApiVersionReaderTest.cs index 6355badf..c03525f9 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/QueryStringApiVersionReaderTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/QueryStringApiVersionReaderTest.cs @@ -86,8 +86,8 @@ public void read_should_not_throw_exception_when_duplicate_api_versions_are_requ } [Theory] - [InlineData( new object[] { new string[0] } )] - [InlineData( new object[] { new[] { "api-version" } } )] + [InlineData( [new string[0]] )] + [InlineData( [new[] { "api-version" }] )] public void add_parameters_should_add_single_parameter_from_query_string( string[] parameterNames ) { // arrange diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/ReportApiVersionsAttributeTest.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/ReportApiVersionsAttributeTest.cs index 45ec83cc..ec30300a 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/ReportApiVersionsAttributeTest.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/ReportApiVersionsAttributeTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dneutral + namespace Asp.Versioning; using Asp.Versioning.Simulators; diff --git a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AdminController.cs b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AdminController.cs index ae3bbbd2..6bcd08cc 100644 --- a/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AdminController.cs +++ b/src/AspNet/WebApi/test/Asp.Versioning.WebApi.Tests/Simulators/AdminController.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Admin + namespace Asp.Versioning.Simulators; using System.Web.Http; diff --git a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/ODataAcceptanceTest.cs b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/ODataAcceptanceTest.cs index 940703e6..4287b9a1 100644 --- a/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/ODataAcceptanceTest.cs +++ b/src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/OData/ODataAcceptanceTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dspecific + namespace Asp.Versioning.OData; using static System.Net.HttpStatusCode; diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs index 3386690f..fd3717ba 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/Conventions/ODataValidationSettingsConventionTest.cs @@ -1,5 +1,10 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dlike +//// Ignore Spelling: Multipart +//// Ignore Spelling: nonaction +//// Ignore Spelling: nonquery + namespace Asp.Versioning.Conventions; using Asp.Versioning.OData; diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs index 8f39cd8d..9181574e 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.Tests/Routing/VersionedAttributeRoutingConventionTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dneutral + namespace Asp.Versioning.Routing; using Asp.Versioning.OData; diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/MediaTypeApiVersionReaderTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/MediaTypeApiVersionReaderTest.cs index 43b097cc..16671d3e 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/MediaTypeApiVersionReaderTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/MediaTypeApiVersionReaderTest.cs @@ -33,7 +33,9 @@ public void read_should_retrieve_version_from_content_type() var request = new Mock<HttpRequest>(); var headers = new Mock<IHeaderDictionary>(); +#pragma warning disable ASP0015 // Suggest using IHeaderDictionary properties headers.SetupGet( h => h["Content-Type"] ).Returns( new StringValues( "application/json;v=2.0" ) ); +#pragma warning restore ASP0015 // Suggest using IHeaderDictionary properties request.SetupGet( r => r.Headers ).Returns( headers.Object ); request.SetupProperty( r => r.Body, Null ); request.SetupProperty( r => r.ContentLength, 0L ); diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/ApiDescriptionExtensionsTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/ApiDescriptionExtensionsTest.cs index cf881b07..5f209cf7 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/ApiDescriptionExtensionsTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.ApiExplorer.Tests/ApiDescriptionExtensionsTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dneutral + namespace Asp.Versioning.ApiExplorer; using Microsoft.AspNetCore.Mvc.ApiExplorer; diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApiVersioningApplicationModelProviderTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApiVersioningApplicationModelProviderTest.cs index f0c56c8c..0d4c2ff9 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApiVersioningApplicationModelProviderTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ApiVersioningApplicationModelProviderTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dneutral + namespace Asp.Versioning; using Asp.Versioning.ApplicationModels; diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs index 894d841f..e06a3e0c 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Mvc.Tests/ReportApiVersionsAttributeTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dneutral + namespace Asp.Versioning; using Microsoft.AspNetCore.Http; diff --git a/src/Common/src/Common/ApiVersionReader.cs b/src/Common/src/Common/ApiVersionReader.cs index 90c34294..0fc2f51f 100644 --- a/src/Common/src/Common/ApiVersionReader.cs +++ b/src/Common/src/Common/ApiVersionReader.cs @@ -42,7 +42,7 @@ public static IApiVersionReader Combine( if ( otherApiVersionReaders is null || ( count = otherApiVersionReaders.Length ) == 0 ) { - apiVersionReaders = new[] { apiVersionReader }; + apiVersionReaders = [apiVersionReader]; } else { diff --git a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs index 4c3339d7..af03ef2f 100644 --- a/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs +++ b/src/Common/src/Common/MediaTypeApiVersionReaderBuilder.cs @@ -24,7 +24,7 @@ namespace Asp.Versioning; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; #if NETFRAMEWORK -using FrozenSet = HashSet<String>; +using FrozenSet = HashSet<string>; #else using FrozenSet = FrozenSet<StringSegment>; #endif diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs index 0a4b5fdd..eebd6c1c 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ApiVersionConventionBuilderTest.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +//// Ignore Spelling: Dtime + namespace Asp.Versioning.Conventions { #if NETFRAMEWORK diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs index 47bc0f7a..a0845583 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/ControllerApiVersionConventionBuilderTTest.cs @@ -74,6 +74,7 @@ private sealed class TestControllerApiVersionConventionBuilder : ControllerApiVe internal ActionApiVersionConventionBuilderCollection<ControllerBase> ProtectedActionBuilders => ActionBuilders; } +#pragma warning disable IDE0079 #pragma warning disable CA1812 #if !NETFRAMEWORK diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/DefaultControllerNameConventionTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/DefaultControllerNameConventionTest.cs index 2a4d0b8d..e261f4f9 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/DefaultControllerNameConventionTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/DefaultControllerNameConventionTest.cs @@ -35,14 +35,14 @@ public static IEnumerable<object[]> NormalizeNameData { get { - return new[] + return new object[][] { #if NETFRAMEWORK - new object[] { "ValuesController" }, - new object[] { "Values2Controller" }, + ["ValuesController"], + ["Values2Controller"], #else - new object[] { "Values" }, - new object[] { "Values2" }, + ["Values"], + ["Values2"], #endif }; } diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/GroupedControllerNameConventionTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/GroupedControllerNameConventionTest.cs index 36def81f..a56ad0c8 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/GroupedControllerNameConventionTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/GroupedControllerNameConventionTest.cs @@ -37,13 +37,13 @@ public static IEnumerable<object[]> NormalizeNameData { get { - return new[] + return new object[][] { - new object[] { "Values" }, + ["Values"], #if NETFRAMEWORK - new object[] { "ValuesController2" }, + ["ValuesController2"], #else - new object[] { "Values2" }, + ["Values2"], #endif }; } diff --git a/src/Common/test/Common.Mvc.Tests/Conventions/OriginalControllerNameConventionTest.cs b/src/Common/test/Common.Mvc.Tests/Conventions/OriginalControllerNameConventionTest.cs index f6ef20c1..551c2149 100644 --- a/src/Common/test/Common.Mvc.Tests/Conventions/OriginalControllerNameConventionTest.cs +++ b/src/Common/test/Common.Mvc.Tests/Conventions/OriginalControllerNameConventionTest.cs @@ -35,12 +35,12 @@ public static IEnumerable<object[]> NormalizeNameData { get { - return new[] + return new object[][] { - new object[] { "Values" }, - new object[] { "Values2" }, + ["Values"], + ["Values2"], #if NETFRAMEWORK - new object[] { "ValuesController2" }, + ["ValuesController2"], #endif }; } diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTTest.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTTest.cs index 6cb0b5af..5788c1a2 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTTest.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTTest.cs @@ -239,6 +239,7 @@ public void action_should_call_action_on_controller_builder() controllerBuilder.Verify( cb => cb.Action( method ), Once() ); } +#pragma warning disable IDE0079 #pragma warning disable CA1034 // Nested types should not be visible public sealed class TestController : ControllerBase diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTest.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTest.cs index 69938e1f..80aa26ae 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTest.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataActionQueryOptionsConventionBuilderTest.cs @@ -239,6 +239,7 @@ public void action_should_call_action_on_controller_builder() controllerBuilder.Verify( cb => cb.Action( method ), Once() ); } +#pragma warning disable IDE0079 #pragma warning disable CA1034 // Nested types should not be visible public sealed class TestController : ControllerBase diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataQueryOptionsConventionBuilderTest.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataQueryOptionsConventionBuilderTest.cs index 6ca4f8a9..02e7ce5b 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataQueryOptionsConventionBuilderTest.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/Conventions/ODataQueryOptionsConventionBuilderTest.cs @@ -1,7 +1,8 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -namespace Asp.Versioning.Conventions; +//// Ignore Spelling: Dtime +namespace Asp.Versioning.Conventions; #if NETFRAMEWORK using Microsoft.AspNet.OData; @@ -112,6 +113,7 @@ private sealed class TestODataQueryOptionsConventionBuilder : ODataQueryOptionsC internal new IList<IODataQueryOptionsConvention> Conventions => base.Conventions; } +#pragma warning disable IDE0079 #pragma warning disable CA1034 // Nested types should not be visible public sealed class StubController : ODataController diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Company.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Company.cs index e6daed7c..05f14b99 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Company.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Company.cs @@ -1,5 +1,9 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 +#pragma warning disable CA1002 // Do not expose generic lists +#pragma warning disable CA2227 // Collection properties should be read only + namespace Asp.Versioning.OData; public class Company @@ -8,11 +12,7 @@ public class Company public Company ParentCompany { get; set; } -#pragma warning disable CA1002 // Do not expose generic lists -#pragma warning disable CA2227 // Collection properties should be read only public List<Company> Subsidiaries { get; set; } -#pragma warning restore CA2227 // Collection properties should be read only -#pragma warning restore CA1002 // Do not expose generic lists public string Name { get; set; } diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Contact.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Contact.cs index 414f2094..c3f18d9d 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Contact.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Contact.cs @@ -1,5 +1,9 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 +#pragma warning disable CA1002 // Do not expose generic lists +#pragma warning disable CA2227 // Collection properties should be read only + namespace Asp.Versioning.OData; public class Contact @@ -14,9 +18,5 @@ public class Contact public string Phone { get; set; } -#pragma warning disable CA1002 // Do not expose generic lists -#pragma warning disable CA2227 // Collection properties should be read only public List<Address> Addresses { get; set; } -#pragma warning restore CA2227 // Collection properties should be read only -#pragma warning restore CA1002 // Do not expose generic lists } \ No newline at end of file diff --git a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Employer.cs b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Employer.cs index efa27e3b..03cbd2a5 100644 --- a/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Employer.cs +++ b/src/Common/test/Common.OData.ApiExplorer.Tests/OData/Employer.cs @@ -1,14 +1,15 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +#pragma warning disable IDE0079 +#pragma warning disable CA2227 // Collection properties should be read only + namespace Asp.Versioning.OData; public class Employer { public int EmployerId { get; set; } -#pragma warning disable CA2227 // Collection properties should be read only public ICollection<Employee> Employees { get; set; } -#pragma warning restore CA2227 // Collection properties should be read only public DateTime Birthday { get; set; } From 8baf3e06bfd09d8350448eaa354c5a3889039f4b Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 6 Dec 2023 20:46:58 -0800 Subject: [PATCH 099/131] Update .NET SDK in CI build --- build/steps-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/steps-ci.yml b/build/steps-ci.yml index 7ec23ad6..c2304d7d 100644 --- a/build/steps-ci.yml +++ b/build/steps-ci.yml @@ -11,7 +11,7 @@ steps: displayName: Install .NET SDK inputs: packageType: sdk - version: 7.0.x # https://github.com/dotnet/core/blob/main/release-notes/releases-index.json + version: 8.0.x # https://github.com/dotnet/core/blob/main/release-notes/releases-index.json - task: DotNetCoreCLI@2 displayName: Build and Test From 2df40dfc72914f1799c6915b4282d11d83d039c3 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Fri, 8 Dec 2023 09:11:51 -0800 Subject: [PATCH 100/131] Support AOT compatibility where possible --- .../Asp.Versioning.Abstractions.csproj | 4 ++ .../Asp.Versioning.Http.csproj | 1 + .../IServiceCollectionExtensions.cs | 67 ++++++++++++------- .../ErrorObjectJsonOptionsSetup.cs | 24 +++++++ .../Asp.Versioning.Http/ErrorObjectWriter.cs | 28 ++++++-- .../Routing/ApiVersioningRouteOptionsSetup.cs | 4 +- .../ErrorObjectWriterTest.cs | 10 +-- .../Asp.Versioning.Http.Client.csproj | 6 +- 8 files changed, 109 insertions(+), 35 deletions(-) create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj b/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj index 8757f5da..00dd4809 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj @@ -10,6 +10,10 @@ <PackageTags>Asp;AspNet;AspNetCore;Versioning</PackageTags> </PropertyGroup> + <PropertyGroup Condition=" '$(TargetFramework)' == '$(DefaultTargetFramework)' "> + <IsAotCompatible>true</IsAotCompatible> + </PropertyGroup> + <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.0' "> <Compile Remove="netstandard2.0\**\*.cs;net#.0\**\*.cs" /> <None Include="netstandard2.0\**\*.cs;net#.0\**\*.cs" /> diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj index 9ef9ea44..32e89b8d 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj @@ -8,6 +8,7 @@ <AssemblyTitle>ASP.NET Core API Versioning</AssemblyTitle> <Description>A service API versioning library for Microsoft ASP.NET Core.</Description> <PackageTags>Asp;AspNet;AspNetCore;Versioning</PackageTags> + <IsAotCompatible>true</IsAotCompatible> </PropertyGroup> <ItemGroup> diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs index 508f34d9..07911329 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs @@ -6,6 +6,7 @@ namespace Microsoft.Extensions.DependencyInjection; using Asp.Versioning.ApiExplorer; using Asp.Versioning.Routing; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Json; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; @@ -90,26 +91,7 @@ private static void AddApiVersioningServices( IServiceCollection services ) services.TryAddEnumerable( Singleton<IApiVersionMetadataCollationProvider, EndpointApiVersionMetadataCollationProvider>() ); services.Replace( WithLinkGeneratorDecorator( services ) ); TryAddProblemDetailsRfc7231Compliance( services ); - } - - // REF: https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ServiceDescriptor.cs#L125 - private static Type GetImplementationType( this ServiceDescriptor descriptor ) - { - if ( descriptor.ImplementationType != null ) - { - return descriptor.ImplementationType; - } - else if ( descriptor.ImplementationInstance != null ) - { - return descriptor.ImplementationInstance.GetType(); - } - else if ( descriptor.ImplementationFactory != null ) - { - var typeArguments = descriptor.ImplementationFactory.GetType().GenericTypeArguments; - return typeArguments[1]; - } - - throw new InvalidOperationException(); + TryAddErrorObjectJsonOptions( services ); } private static ServiceDescriptor WithLinkGeneratorDecorator( IServiceCollection services ) @@ -127,12 +109,31 @@ private static ServiceDescriptor WithLinkGeneratorDecorator( IServiceCollection if ( factory == null ) { - var decoratedType = descriptor.GetImplementationType(); - var decoratorType = typeof( ApiVersionLinkGenerator<> ).MakeGenericType( decoratedType ); + // REF: https://github.com/dotnet/aspnetcore/blob/main/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs#L96 + // REF: https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ServiceDescriptor.cs#L292 + var decoratedType = descriptor switch + { + { ImplementationType: var type } when type is not null => type, + { ImplementationInstance: var instance } when instance is not null => instance.GetType(), + _ => throw new InvalidOperationException(), + }; services.Replace( Describe( decoratedType, decoratedType, lifetime ) ); - return Describe( typeof( LinkGenerator ), decoratorType, lifetime ); + LinkGenerator NewFactory( IServiceProvider serviceProvider ) + { + var instance = (LinkGenerator) serviceProvider.GetRequiredService( decoratedType! ); + var source = serviceProvider.GetRequiredService<IApiVersionParameterSource>(); + + if ( source.VersionsByUrl() ) + { + instance = new ApiVersionLinkGenerator( instance ); + } + + return instance; + } + + return Describe( typeof( LinkGenerator ), NewFactory, lifetime ); } else { @@ -177,4 +178,24 @@ static bool IsDefaultProblemDetailsWriter( ServiceDescriptor serviceDescriptor ) static Rfc7231ProblemDetailsWriter NewProblemDetailsWriter( IServiceProvider serviceProvider, Type decoratedType ) => new( (IProblemDetailsWriter) serviceProvider.GetRequiredService( decoratedType ) ); } + + private static void TryAddErrorObjectJsonOptions( IServiceCollection services ) + { + var serviceType = typeof( IProblemDetailsWriter ); + var implementationType = typeof( ErrorObjectWriter ); + + for ( var i = 0; i < services.Count; i++ ) + { + var service = services[i]; + + // inheritance is intentionally not considered here because it will require a user-defined + // JsonSerlizerContext and IConfigureOptions<JsonOptions> + if ( service.ServiceType == serviceType && + service.ImplementationType == implementationType ) + { + services.TryAddEnumerable( Singleton<IConfigureOptions<JsonOptions>, ErrorObjectJsonOptionsSetup>() ); + return; + } + } + } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs new file mode 100644 index 00000000..b2de900c --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + +namespace Asp.Versioning; + +using Microsoft.AspNetCore.Http.Json; +using Microsoft.Extensions.Options; + +/// <summary> +/// Adds the ErrorObjectJsonContext to the current JsonSerializerOptions. +/// +/// This allows for consistent serialization behavior for ErrorObject regardless if the +/// default reflection-based serializer is used and makes it trim/NativeAOT compatible. +/// </summary> +internal sealed class ErrorObjectJsonOptionsSetup : IConfigureOptions<JsonOptions> +{ + // Always insert the ErrorObjectJsonContext to the beginning of the chain at the time this Configure + // is invoked. This JsonTypeInfoResolver will be before the default reflection-based resolver, and + // before any other resolvers currently added. If apps need to customize serialization, they can + // prepend a custom ErrorObject resolver to the chain in an IConfigureOptions<JsonOptions> registered. + public void Configure( JsonOptions options ) => + options.SerializerOptions.TypeInfoResolverChain.Insert( 0, new ErrorObjectWriter.ErrorObjectJsonContext() ); +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs index fed4ed71..82460917 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs @@ -4,8 +4,11 @@ namespace Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using System.Text.Json; using System.Text.Json.Serialization; using static System.Text.Json.Serialization.JsonIgnoreCondition; +using JsonOptions = Microsoft.AspNetCore.Http.Json.JsonOptions; /// <summary> /// Represents a problem details writer that outputs error objects in responses. @@ -15,8 +18,18 @@ namespace Asp.Versioning; /// in the Microsoft REST API Guidelines and /// <a ref="https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#_Toc38457793">OData Error Responses</a>.</remarks> [CLSCompliant( false )] -public class ErrorObjectWriter : IProblemDetailsWriter +public partial class ErrorObjectWriter : IProblemDetailsWriter { + private readonly JsonSerializerOptions options; + + /// <summary> + /// Initializes a new instance of the <see cref="ErrorObjectWriter"/> class. + /// </summary> + /// <param name="options">The current <see cref="JsonOptions">JSON options</see>.</param> + /// <exception cref="ArgumentNullException"><paramref name="options"/> is <c>null</c>.</exception> + public ErrorObjectWriter( IOptions<JsonOptions> options ) => + this.options = ( options ?? throw new ArgumentNullException( nameof( options ) ) ).Value.SerializerOptions; + /// <inheritdoc /> public virtual bool CanWrite( ProblemDetailsContext context ) { @@ -40,7 +53,7 @@ public virtual ValueTask WriteAsync( ProblemDetailsContext context ) OnBeforeWrite( context, ref obj ); - return new( response.WriteAsJsonAsync( obj ) ); + return new( response.WriteAsJsonAsync( obj, options.GetTypeInfo( obj.GetType() ) ) ); } /// <summary> @@ -58,7 +71,7 @@ protected virtual void OnBeforeWrite( ProblemDetailsContext context, ref ErrorOb /// <summary> /// Represents an error object. /// </summary> - protected readonly struct ErrorObject + protected internal readonly partial struct ErrorObject { internal ErrorObject( ProblemDetails problemDetails ) => Error = new( problemDetails ); @@ -74,7 +87,7 @@ internal ErrorObject( ProblemDetails problemDetails ) => /// <summary> /// Represents the error detail. /// </summary> - protected readonly struct ErrorDetail + protected internal readonly partial struct ErrorDetail { private readonly ProblemDetails problemDetails; private readonly InnerError? innerError; @@ -154,7 +167,7 @@ public string? Target /// <summary> /// Represents an inner error. /// </summary> - protected readonly struct InnerError + protected internal readonly partial struct InnerError { private readonly ProblemDetails problemDetails; private readonly Dictionary<string, object> extensions = []; @@ -181,4 +194,9 @@ public string? Message [JsonExtensionData] public IDictionary<string, object> Extensions => extensions; } + + [JsonSerializable( typeof( ErrorObject ) )] + internal sealed partial class ErrorObjectJsonContext : JsonSerializerContext + { + } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersioningRouteOptionsSetup.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersioningRouteOptionsSetup.cs index 332c1842..8d6a5755 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersioningRouteOptionsSetup.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Routing/ApiVersioningRouteOptionsSetup.cs @@ -24,7 +24,7 @@ public virtual void PostConfigure( string? name, RouteOptions options ) { ArgumentNullException.ThrowIfNull( options ); - var key = versioningOptions.Value.RouteConstraintName; - options.ConstraintMap.Add( key, typeof( ApiVersionRouteConstraint ) ); + var token = versioningOptions.Value.RouteConstraintName; + options.SetParameterPolicy<ApiVersionRouteConstraint>( token ); } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs index ddfe96c4..822f619b 100644 --- a/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs +++ b/src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/ErrorObjectWriterTest.cs @@ -3,6 +3,8 @@ namespace Asp.Versioning; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Json; +using Microsoft.Extensions.Options; using System.Text.Json; using System.Threading.Tasks; @@ -16,7 +18,7 @@ public class ErrorObjectWriterTest public void can_write_should_be_true_for_api_versioning_problem_types( string type ) { // arrange - var writer = new ErrorObjectWriter(); + var writer = new ErrorObjectWriter( Options.Create( new JsonOptions() ) ); var context = new ProblemDetailsContext() { HttpContext = new DefaultHttpContext(), @@ -38,7 +40,7 @@ public void can_write_should_be_false_for_other_problem_types() { // arrange const string BadRequest = "https://tools.ietf.org/html/rfc7231#section-6.5.1"; - var writer = new ErrorObjectWriter(); + var writer = new ErrorObjectWriter( Options.Create( new JsonOptions() ) ); var context = new ProblemDetailsContext() { HttpContext = new DefaultHttpContext(), @@ -73,14 +75,14 @@ public async Task write_async_should_output_expected_json() }, }; - var writer = new ErrorObjectWriter(); + var writer = new ErrorObjectWriter( Options.Create( new JsonOptions() ) ); using var stream = new MemoryStream(); var response = new Mock<HttpResponse>() { CallBase = true }; var httpContext = new Mock<HttpContext>() { CallBase = true }; response.SetupGet( r => r.Body ).Returns( stream ); response.SetupProperty( r => r.ContentType ); - response.SetupGet(r => r.HttpContext).Returns(() => httpContext.Object ); + response.SetupGet( r => r.HttpContext ).Returns( () => httpContext.Object ); httpContext.SetupGet( c => c.Response ).Returns( response.Object ); var context = new ProblemDetailsContext() diff --git a/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj b/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj index d47be589..75416005 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj +++ b/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj @@ -10,6 +10,10 @@ <PackageTags>Asp;AspNet;AspNetCore;Versioning;Http</PackageTags> </PropertyGroup> + <PropertyGroup Condition=" '$(TargetFramework)' == '$(DefaultTargetFramework)' "> + <IsAotCompatible>true</IsAotCompatible> + </PropertyGroup> + <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' "> <Compile Remove="net#.0\**\*.cs" /> <None Include="net#.0\**\*.cs" /> @@ -44,5 +48,5 @@ <ItemGroup> <ProjectReference Include="..\..\..\Abstractions\src\Asp.Versioning.Abstractions\Asp.Versioning.Abstractions.csproj" /> </ItemGroup> - + </Project> From 9376399a24348f2a1ff3f0d5103412e6b31ae598 Mon Sep 17 00:00:00 2001 From: Xavier <xavierjohn@hotmail.com> Date: Fri, 8 Dec 2023 10:07:10 -0800 Subject: [PATCH 101/131] Use ErrorObjectJsonContext.Default --- .../src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs index b2de900c..664c4840 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs @@ -20,5 +20,5 @@ internal sealed class ErrorObjectJsonOptionsSetup : IConfigureOptions<JsonOption // before any other resolvers currently added. If apps need to customize serialization, they can // prepend a custom ErrorObject resolver to the chain in an IConfigureOptions<JsonOptions> registered. public void Configure( JsonOptions options ) => - options.SerializerOptions.TypeInfoResolverChain.Insert( 0, new ErrorObjectWriter.ErrorObjectJsonContext() ); + options.SerializerOptions.TypeInfoResolverChain.Insert( 0, ErrorObjectWriter.ErrorObjectJsonContext.Default ); } \ No newline at end of file From 7fde9956aa4f330c13130ba8715f7a78cca04578 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Fri, 8 Dec 2023 11:18:07 -0800 Subject: [PATCH 102/131] Remove Dependabot from release/5.x; no longer necessary --- .github/dependabot.yml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4cedbb39..14290721 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -14,20 +14,4 @@ updates: - "commonsensesoftware" commit-message: prefix: "[main] " - include: scope - - # only servicing 5.x release - - package-ecosystem: "nuget" - directory: "/" - target-branch: "release/5.1" - schedule: - interval: "monthly" - allow: - - dependency-type: "all" - assignees: - - "commonsensesoftware" - reviewers: - - "commonsensesoftware" - commit-message: - prefix: "[release/5.1] " include: scope \ No newline at end of file From ef0aa0872872569d1e25084a20638b707c3b4fcd Mon Sep 17 00:00:00 2001 From: Xavier <xavierjohn@hotmail.com> Date: Tue, 9 Jan 2024 11:02:24 -0800 Subject: [PATCH 103/131] Move .net core packages to the top. --- README.md | 58 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index c0284787..d6828747 100644 --- a/README.md +++ b/README.md @@ -23,22 +23,6 @@ versioning in the past or supported API versioning with semantics that are diffe The supported flavors of ASP.NET are: -* **ASP.NET Web API** - <div>Adds API versioning to your Web API applications</div> - - [![NuGet Package](https://img.shields.io/nuget/v/Asp.Versioning.WebApi.svg)](https://www.nuget.org/packages/Asp.Versioning.WebApi) - [![NuGet Downloads](https://img.shields.io/nuget/dt/Asp.Versioning.WebApi.svg?color=green)](https://www.nuget.org/packages/Asp.Versioning.WebApi) - [![Quick Start](https://img.shields.io/badge/quick-start-9B6CD1)](../../wiki/New-Services-Quick-Start#aspnet-web-api) - [![Examples](https://img.shields.io/badge/example-code-2B91AF)](../../tree/main/examples/AspNet/WebApi) - -* **ASP.NET Web API and OData** - <div>Adds API versioning to your Web API applications using OData v4.0</div> - - [![NuGet Package](https://img.shields.io/nuget/v/Asp.Versioning.WebApi.OData.svg)](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData) - [![NuGet Downloads](https://img.shields.io/nuget/dt/Asp.Versioning.WebApi.OData.svg?color=green)](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData) - [![Quick Start](https://img.shields.io/badge/quick-start-9B6CD1)](../../wiki/New-Services-Quick-Start#aspnet-web-api-with-odata-v40) - [![Examples](https://img.shields.io/badge/example-code-2B91AF)](../../tree/main/examples/AspNet/OData) - * **ASP.NET Core** <div>Adds API versioning to your ASP.NET Core <i>Minimal API</i> applications</div> @@ -63,23 +47,23 @@ The supported flavors of ASP.NET are: [![Quick Start](https://img.shields.io/badge/quick-start-9B6CD1)](../../wiki/New-Services-Quick-Start#aspnet-core-with-odata-v40) [![Examples](https://img.shields.io/badge/example-code-2B91AF)](../../tree/main/examples/AspNetCore/OData) -This is also the home of the ASP.NET API versioning API explorers that you can use to easily document your REST APIs with OpenAPI: +* **ASP.NET Web API** + <div>Adds API versioning to your Web API applications</div> -* **ASP.NET Web API Versioned API Explorer** - <div>Replaces the default API explorer in your Web API applications</div> + [![NuGet Package](https://img.shields.io/nuget/v/Asp.Versioning.WebApi.svg)](https://www.nuget.org/packages/Asp.Versioning.WebApi) + [![NuGet Downloads](https://img.shields.io/nuget/dt/Asp.Versioning.WebApi.svg?color=green)](https://www.nuget.org/packages/Asp.Versioning.WebApi) + [![Quick Start](https://img.shields.io/badge/quick-start-9B6CD1)](../../wiki/New-Services-Quick-Start#aspnet-web-api) + [![Examples](https://img.shields.io/badge/example-code-2B91AF)](../../tree/main/examples/AspNet/WebApi) - [![NuGet Package](https://img.shields.io/nuget/v/Asp.Versioning.WebApi.ApiExplorer.svg)](https://www.nuget.org/packages/Asp.Versioning.WebApi.ApiExplorer) - [![NuGet Downloads](https://img.shields.io/nuget/dt/Asp.Versioning.WebApi.ApiExplorer.svg?color=green)](https://www.nuget.org/packages/Asp.Versioning.WebApi.ApiExplorer) - [![Quick Start](https://img.shields.io/badge/quick-start-9B6CD1)](../../wiki/API-Documentation#aspnet-web-api) - [![Examples](https://img.shields.io/badge/example-code-2B91AF)](../../tree/main/examples/AspNet/WebApi/OpenApiWebApiSample) +* **ASP.NET Web API and OData** + <div>Adds API versioning to your Web API applications using OData v4.0</div> -* **ASP.NET Web API with OData API Explorer** - <div>Adds an API explorer to your Web API applications using OData v4.0</div> + [![NuGet Package](https://img.shields.io/nuget/v/Asp.Versioning.WebApi.OData.svg)](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData) + [![NuGet Downloads](https://img.shields.io/nuget/dt/Asp.Versioning.WebApi.OData.svg?color=green)](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData) + [![Quick Start](https://img.shields.io/badge/quick-start-9B6CD1)](../../wiki/New-Services-Quick-Start#aspnet-web-api-with-odata-v40) + [![Examples](https://img.shields.io/badge/example-code-2B91AF)](../../tree/main/examples/AspNet/OData) - [![NuGet Package](https://img.shields.io/nuget/v/Asp.Versioning.WebApi.OData.ApiExplorer.svg)](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData.ApiExplorer) - [![NuGet Downloads](https://img.shields.io/nuget/dt/Asp.Versioning.WebApi.OData.ApiExplorer.svg?color=green)](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData.ApiExplorer) - [![Quick Start](https://img.shields.io/badge/quick-start-9B6CD1)](../../wiki/API-Documentation#aspnet-web-api-with-odata) - [![Examples](https://img.shields.io/badge/example-code-2B91AF)](../../tree/main/examples/AspNet/OData/OpenApiODataWebApiSample) +This is also the home of the ASP.NET API versioning API explorers that you can use to easily document your REST APIs with OpenAPI: * **ASP.NET Core Versioned API Explorer** <div>Adds additional API explorer support to your ASP.NET Core applications</div> @@ -97,6 +81,22 @@ This is also the home of the ASP.NET API versioning API explorers that you can u [![Quick Start](https://img.shields.io/badge/quick-start-9B6CD1)](../../wiki/API-Documentation#aspnet-core-with-odata) [![Examples](https://img.shields.io/badge/example-code-2B91AF)](../../tree/main/examples/AspNetCore/OData/OpenApiODataSample) +* **ASP.NET Web API Versioned API Explorer** + <div>Replaces the default API explorer in your Web API applications</div> + + [![NuGet Package](https://img.shields.io/nuget/v/Asp.Versioning.WebApi.ApiExplorer.svg)](https://www.nuget.org/packages/Asp.Versioning.WebApi.ApiExplorer) + [![NuGet Downloads](https://img.shields.io/nuget/dt/Asp.Versioning.WebApi.ApiExplorer.svg?color=green)](https://www.nuget.org/packages/Asp.Versioning.WebApi.ApiExplorer) + [![Quick Start](https://img.shields.io/badge/quick-start-9B6CD1)](../../wiki/API-Documentation#aspnet-web-api) + [![Examples](https://img.shields.io/badge/example-code-2B91AF)](../../tree/main/examples/AspNet/WebApi/OpenApiWebApiSample) + +* **ASP.NET Web API with OData API Explorer** + <div>Adds an API explorer to your Web API applications using OData v4.0</div> + + [![NuGet Package](https://img.shields.io/nuget/v/Asp.Versioning.WebApi.OData.ApiExplorer.svg)](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData.ApiExplorer) + [![NuGet Downloads](https://img.shields.io/nuget/dt/Asp.Versioning.WebApi.OData.ApiExplorer.svg?color=green)](https://www.nuget.org/packages/Asp.Versioning.WebApi.OData.ApiExplorer) + [![Quick Start](https://img.shields.io/badge/quick-start-9B6CD1)](../../wiki/API-Documentation#aspnet-web-api-with-odata) + [![Examples](https://img.shields.io/badge/example-code-2B91AF)](../../tree/main/examples/AspNet/OData/OpenApiODataWebApiSample) + The client-side libraries make it simple to create API version-aware HTTP clients. * **HTTP Client API Versioning Extensions** From 1c136280aab973a70315ae3826873f4de67df6a7 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Wed, 10 Jan 2024 15:44:05 -0800 Subject: [PATCH 104/131] Correct sunset policy resolution when falling back. Fixes #1064 --- .../Asp.Versioning.Abstractions.csproj | 2 +- .../ISunsetPolicyManagerExtensions.cs | 7 ++++--- .../src/Asp.Versioning.Abstractions/ReleaseNotes.txt | 2 +- .../ISunsetPolicyManagerExtensionsTest.cs | 5 ++--- src/Common/src/Common/DefaultApiVersionReporter.cs | 4 +--- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj b/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj index 00dd4809..6fb59849 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>8.0.0</VersionPrefix> + <VersionPrefix>8.0.1</VersionPrefix> <AssemblyVersion>8.0.0.0</AssemblyVersion> <TargetFrameworks>$(DefaultTargetFramework);netstandard1.0;netstandard2.0</TargetFrameworks> <AssemblyTitle>API Versioning Abstractions</AssemblyTitle> diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs index 37be6965..f28f9cca 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ISunsetPolicyManagerExtensions.cs @@ -46,7 +46,7 @@ public static bool TryGetPolicy( /// <param name="name">The name of the API.</param> /// <param name="apiVersion">The API version to get the policy for.</param> /// <returns>The applicable <see cref="SunsetPolicy">sunset policy</see>, if any.</returns> - /// <remarks>The resolution or is as follows: + /// <remarks>The resolution order is as follows: /// <list type="bullet"> /// <item><paramref name="name"/> and <paramref name="apiVersion"/></item> /// <item><paramref name="name"/> only</item> @@ -76,7 +76,7 @@ public static bool TryGetPolicy( /// <param name="apiVersion">The API version to get the policy for.</param> /// /// <param name="sunsetPolicy">The applicable <see cref="SunsetPolicy">sunset policy</see>, if any.</param> /// <returns>True if the <paramref name="sunsetPolicy">sunset policy</paramref> was retrieved; otherwise, false.</returns> - /// <remarks>The resolution or is as follows: + /// <remarks>The resolution order is as follows: /// <list type="bullet"> /// <item><paramref name="name"/> and <paramref name="apiVersion"/></item> /// <item><paramref name="name"/> only</item> @@ -102,7 +102,8 @@ public static bool TryResolvePolicy( return true; } } - else if ( apiVersion != null && policyManager.TryGetPolicy( apiVersion, out sunsetPolicy ) ) + + if ( apiVersion != null && policyManager.TryGetPolicy( apiVersion, out sunsetPolicy ) ) { return true; } diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ReleaseNotes.txt b/src/Abstractions/src/Asp.Versioning.Abstractions/ReleaseNotes.txt index 5f282702..d6943ffb 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ReleaseNotes.txt +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +TryResolvePolicy should correctly fall back ([#1064](https://github.com/dotnet/aspnet-api-versioning/issues/1064)) \ No newline at end of file diff --git a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ISunsetPolicyManagerExtensionsTest.cs b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ISunsetPolicyManagerExtensionsTest.cs index ac1cb2c0..9cc9b693 100644 --- a/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ISunsetPolicyManagerExtensionsTest.cs +++ b/src/Abstractions/test/Asp.Versioning.Abstractions.Tests/ISunsetPolicyManagerExtensionsTest.cs @@ -65,11 +65,10 @@ public void resolve_policy_should_fall_back_to_global_result() var expected = new SunsetPolicy(); var other = new SunsetPolicy(); - manager.Setup( m => m.TryGetPolicy( "Test", new ApiVersion( 1.0, null ), out other ) ).Returns( true ); - manager.Setup( m => m.TryGetPolicy( default, new ApiVersion( 1.0, null ), out expected ) ).Returns( true ); + manager.Setup( m => m.TryGetPolicy( It.IsAny<string>(), new ApiVersion( 1.0, null ), out expected ) ).Returns( true ); // act - var policy = manager.Object.ResolvePolicyOrDefault( default, new ApiVersion( 1.0 ) ); + var policy = manager.Object.ResolvePolicyOrDefault( "Test", new ApiVersion( 1.0 ) ); // assert policy.Should().BeSameAs( expected ); diff --git a/src/Common/src/Common/DefaultApiVersionReporter.cs b/src/Common/src/Common/DefaultApiVersionReporter.cs index 95e4d017..72ed1408 100644 --- a/src/Common/src/Common/DefaultApiVersionReporter.cs +++ b/src/Common/src/Common/DefaultApiVersionReporter.cs @@ -91,9 +91,7 @@ public void Report( HttpResponse response, ApiVersionModel apiVersionModel ) #endif var name = metadata.Name; - if ( sunsetPolicyManager.TryGetPolicy( name, version, out var policy ) || - ( !string.IsNullOrEmpty( name ) && sunsetPolicyManager.TryGetPolicy( name, out policy ) ) || - ( version != null && sunsetPolicyManager.TryGetPolicy( version, out policy ) ) ) + if ( sunsetPolicyManager.TryResolvePolicy( name, version, out var policy ) ) { response.WriteSunsetPolicy( policy ); } From 9d18108b8758dcf190ead2d9f579b7bce921e078 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Fri, 12 Jan 2024 15:16:21 -0800 Subject: [PATCH 105/131] Added IEndpointInspector so that controller action endpoints are not processed by min api endpoint collators. Related #1066 --- .../ApiExplorer/DefaultEndpointInspector.cs | 15 ++++++++ ...ointApiVersionMetadataCollationProvider.cs | 19 ++++++++-- .../ApiExplorer/IEndpointInspector.cs | 19 ++++++++++ .../IServiceCollectionExtensions.cs | 1 + .../ApiVersionDescriptionProviderFactory.cs | 5 ++- .../IApiVersioningBuilderExtensions.cs | 36 +++++++++---------- .../ApiExplorer/MvcEndpointInspector.cs | 21 +++++++++++ .../IApiVersioningBuilderExtensions.cs | 18 ++++++++++ 8 files changed, 112 insertions(+), 22 deletions(-) create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/DefaultEndpointInspector.cs create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IEndpointInspector.cs create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiExplorer/MvcEndpointInspector.cs diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/DefaultEndpointInspector.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/DefaultEndpointInspector.cs new file mode 100644 index 00000000..7109d87f --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/DefaultEndpointInspector.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer; + +using Microsoft.AspNetCore.Http; + +/// <summary> +/// Represents the default <see cref="IEndpointInspector">endpoint inspector</see>. +/// </summary> +[CLSCompliant(false)] +public sealed class DefaultEndpointInspector : IEndpointInspector +{ + /// <inheritdoc /> + public bool IsControllerAction( Endpoint endpoint ) => false; +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/EndpointApiVersionMetadataCollationProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/EndpointApiVersionMetadataCollationProvider.cs index 45c14725..e5ce20fb 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/EndpointApiVersionMetadataCollationProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/EndpointApiVersionMetadataCollationProvider.cs @@ -12,15 +12,29 @@ namespace Asp.Versioning.ApiExplorer; public sealed class EndpointApiVersionMetadataCollationProvider : IApiVersionMetadataCollationProvider { private readonly EndpointDataSource endpointDataSource; + private readonly IEndpointInspector endpointInspector; private int version; /// <summary> /// Initializes a new instance of the <see cref="EndpointApiVersionMetadataCollationProvider"/> class. /// </summary> /// <param name="endpointDataSource">The underlying <see cref="endpointDataSource">endpoint data source</see>.</param> + [Obsolete( "Use the constructor that accepts IEndpointInspector. This constructor will be removed in a future version." )] public EndpointApiVersionMetadataCollationProvider( EndpointDataSource endpointDataSource ) + : this( endpointDataSource, new DefaultEndpointInspector() ) { } + + /// <summary> + /// Initializes a new instance of the <see cref="EndpointApiVersionMetadataCollationProvider"/> class. + /// </summary> + /// <param name="endpointDataSource">The underlying <see cref="endpointDataSource">endpoint data source</see>.</param> + /// <param name="endpointInspector">The <see cref="IEndpointInspector">endpoint inspector</see> used to inspect endpoints.</param> + public EndpointApiVersionMetadataCollationProvider( EndpointDataSource endpointDataSource, IEndpointInspector endpointInspector ) { - this.endpointDataSource = endpointDataSource ?? throw new ArgumentNullException( nameof( endpointDataSource ) ); + ArgumentNullException.ThrowIfNull( endpointDataSource ); + ArgumentNullException.ThrowIfNull( endpointInspector ); + + this.endpointDataSource = endpointDataSource; + this.endpointInspector = endpointInspector; ChangeToken.OnChange( endpointDataSource.GetChangeToken, () => ++version ); } @@ -38,7 +52,8 @@ public void Execute( ApiVersionMetadataCollationContext context ) { var endpoint = endpoints[i]; - if ( endpoint.Metadata.GetMetadata<ApiVersionMetadata>() is not ApiVersionMetadata item ) + if ( endpoint.Metadata.GetMetadata<ApiVersionMetadata>() is not ApiVersionMetadata item || + endpointInspector.IsControllerAction( endpoint ) ) { continue; } diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IEndpointInspector.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IEndpointInspector.cs new file mode 100644 index 00000000..900edf94 --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IEndpointInspector.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer; + +using Microsoft.AspNetCore.Http; + +/// <summary> +/// Defines the behavior of an endpoint inspector. +/// </summary> +[CLSCompliant( false )] +public interface IEndpointInspector +{ + /// <summary> + /// Determines whether the specified endpoint is a controller action. + /// </summary> + /// <param name="endpoint">The <see cref="Endpoint">endpoint</see> to inspect.</param> + /// <returns>True if the <paramref name="endpoint"/> is for a controller action; otherwise, false.</returns> + bool IsControllerAction( Endpoint endpoint ); +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs index 07911329..14606936 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs @@ -89,6 +89,7 @@ private static void AddApiVersioningServices( IServiceCollection services ) services.TryAddEnumerable( Transient<IPostConfigureOptions<RouteOptions>, ApiVersioningRouteOptionsSetup>() ); services.TryAddEnumerable( Singleton<MatcherPolicy, ApiVersionMatcherPolicy>() ); services.TryAddEnumerable( Singleton<IApiVersionMetadataCollationProvider, EndpointApiVersionMetadataCollationProvider>() ); + services.TryAddTransient<IEndpointInspector, DefaultEndpointInspector>(); services.Replace( WithLinkGeneratorDecorator( services ) ); TryAddProblemDetailsRfc7231Compliance( services ); TryAddErrorObjectJsonOptions( services ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs index 05b1cba1..75b99644 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs @@ -16,6 +16,7 @@ internal sealed class ApiVersionDescriptionProviderFactory : IApiVersionDescript { private readonly ISunsetPolicyManager sunsetPolicyManager; private readonly IApiVersionMetadataCollationProvider[] providers; + private readonly IEndpointInspector endpointInspector; private readonly IOptions<ApiExplorerOptions> options; private readonly Activator activator; @@ -23,11 +24,13 @@ public ApiVersionDescriptionProviderFactory( Activator activator, ISunsetPolicyManager sunsetPolicyManager, IEnumerable<IApiVersionMetadataCollationProvider> providers, + IEndpointInspector endpointInspector, IOptions<ApiExplorerOptions> options ) { this.activator = activator; this.sunsetPolicyManager = sunsetPolicyManager; this.providers = providers.ToArray(); + this.endpointInspector = endpointInspector; this.options = options; } @@ -35,7 +38,7 @@ public IApiVersionDescriptionProvider Create( EndpointDataSource endpointDataSou { var collators = new List<IApiVersionMetadataCollationProvider>( capacity: providers.Length + 1 ) { - new EndpointApiVersionMetadataCollationProvider( endpointDataSource ), + new EndpointApiVersionMetadataCollationProvider( endpointDataSource, endpointInspector ), }; collators.AddRange( providers ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs index 1c1d2e63..29f0fba9 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -5,11 +5,13 @@ namespace Microsoft.Extensions.DependencyInjection; using Asp.Versioning; using Asp.Versioning.ApiExplorer; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; using static ServiceDescriptor; /// <summary> @@ -70,40 +72,36 @@ private static IApiVersionDescriptionProviderFactory ResolveApiVersionDescriptio { var sunsetPolicyManager = serviceProvider.GetRequiredService<ISunsetPolicyManager>(); var providers = serviceProvider.GetServices<IApiVersionMetadataCollationProvider>(); + var inspector = serviceProvider.GetRequiredService<IEndpointInspector>(); var options = serviceProvider.GetRequiredService<IOptions<ApiExplorerOptions>>(); - var mightUseCustomGroups = options.Value.FormatGroupName is not null; return new ApiVersionDescriptionProviderFactory( - mightUseCustomGroups ? NewGroupedProvider : NewDefaultProvider, + NewDefaultProvider, sunsetPolicyManager, providers, + inspector, options ); - static IApiVersionDescriptionProvider NewDefaultProvider( + static DefaultApiVersionDescriptionProvider NewDefaultProvider( IEnumerable<IApiVersionMetadataCollationProvider> providers, ISunsetPolicyManager sunsetPolicyManager, IOptions<ApiExplorerOptions> apiExplorerOptions ) => - new DefaultApiVersionDescriptionProvider( providers, sunsetPolicyManager, apiExplorerOptions ); - - static IApiVersionDescriptionProvider NewGroupedProvider( - IEnumerable<IApiVersionMetadataCollationProvider> providers, - ISunsetPolicyManager sunsetPolicyManager, - IOptions<ApiExplorerOptions> apiExplorerOptions ) => - new GroupedApiVersionDescriptionProvider( providers, sunsetPolicyManager, apiExplorerOptions ); + new( providers, sunsetPolicyManager, apiExplorerOptions ); } private static IApiVersionDescriptionProvider ResolveApiVersionDescriptionProvider( IServiceProvider serviceProvider ) { - var providers = serviceProvider.GetServices<IApiVersionMetadataCollationProvider>(); - var sunsetPolicyManager = serviceProvider.GetRequiredService<ISunsetPolicyManager>(); - var options = serviceProvider.GetRequiredService<IOptions<ApiExplorerOptions>>(); - var mightUseCustomGroups = options.Value.FormatGroupName is not null; + var factory = serviceProvider.GetRequiredService<IApiVersionDescriptionProviderFactory>(); + var endpointDataSource = new EmptyEndpointDataSource(); + return factory.Create( endpointDataSource ); + } + + private sealed class EmptyEndpointDataSource : EndpointDataSource + { + public override IReadOnlyList<Endpoint> Endpoints => Array.Empty<Endpoint>(); - if ( mightUseCustomGroups ) - { - return new GroupedApiVersionDescriptionProvider( providers, sunsetPolicyManager, options ); - } + public override IChangeToken GetChangeToken() => new CancellationChangeToken( CancellationToken.None ); - return new DefaultApiVersionDescriptionProvider( providers, sunsetPolicyManager, options ); + public override IReadOnlyList<Endpoint> GetGroupedEndpoints( RouteGroupContext context ) => Array.Empty<Endpoint>(); } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiExplorer/MvcEndpointInspector.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiExplorer/MvcEndpointInspector.cs new file mode 100644 index 00000000..8729791c --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiExplorer/MvcEndpointInspector.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer; + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +/// <summary> +/// Represents the <see cref="IEndpointInspector">inspector</see> that understands +/// <see cref="Endpoint">endpoints</see> defined by MVC controllers. +/// </summary> +[CLSCompliant(false)] +public sealed class MvcEndpointInspector : IEndpointInspector +{ + /// <inheritdoc /> + public bool IsControllerAction( Endpoint endpoint ) + { + ArgumentNullException.ThrowIfNull( endpoint ); + return endpoint.Metadata.Any( static attribute => attribute is ControllerAttribute ); + } +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs index 7796ca96..5f895312 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -67,6 +67,7 @@ private static void AddServices( IServiceCollection services ) services.TryAddEnumerable( Transient<IApiControllerSpecification, ApiBehaviorSpecification>() ); services.TryAddEnumerable( Singleton<IApiVersionMetadataCollationProvider, ActionApiVersionMetadataCollationProvider>() ); services.Replace( WithUrlHelperFactoryDecorator( services ) ); + services.TryReplace<IEndpointInspector, DefaultEndpointInspector, MvcEndpointInspector>(); } private static object CreateInstance( this IServiceProvider services, ServiceDescriptor descriptor ) @@ -84,6 +85,23 @@ private static object CreateInstance( this IServiceProvider services, ServiceDes return ActivatorUtilities.GetServiceOrCreateInstance( services, descriptor.ImplementationType! ); } + private static void TryReplace<TService, TImplementation, TReplacement>( this IServiceCollection services ) + { + var serviceType = typeof( TService ); + var implementationType = typeof( TImplementation ); + + for ( var i = services.Count - 1; i >= 0; i-- ) + { + var service = services[i]; + + if ( service.ServiceType == serviceType && service.ImplementationType == implementationType ) + { + services[i] = Describe( serviceType, typeof( TReplacement ), service.Lifetime ); + break; + } + } + } + [SkipLocalsInit] private static DecoratedServiceDescriptor WithUrlHelperFactoryDecorator( IServiceCollection services ) { From 59ff9e687b4f5cc7bcf1f48f701117b694a50da9 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Fri, 12 Jan 2024 15:17:24 -0800 Subject: [PATCH 106/131] Refactor and unify IApiVersionDescriptionProvider implementations. Explicit group names can only be determined at runtime. Related #1066 --- .../DefaultApiVersionDescriptionProvider.cs | 147 +++---------- .../GroupedApiVersionDescriptionProvider.cs | 200 ++---------------- .../ApiVersionDescriptionCollection.cs | 76 +++++++ .../Internal/ApiVersionDescriptionComparer.cs | 28 +++ .../Internal/DescriptionProvider.cs | 107 ++++++++++ .../Internal/GroupedApiVersion.cs | 5 + .../Internal/IGroupedApiVersionMetadata.cs | 20 ++ .../IGroupedApiVersionMetadataFactory.cs | 9 + 8 files changed, 291 insertions(+), 301 deletions(-) create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/ApiVersionDescriptionCollection.cs create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/ApiVersionDescriptionComparer.cs create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/DescriptionProvider.cs create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/GroupedApiVersion.cs create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/IGroupedApiVersionMetadata.cs create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/IGroupedApiVersionMetadataFactory.cs diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs index 63408cad..22151e11 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DefaultApiVersionDescriptionProvider.cs @@ -2,9 +2,8 @@ namespace Asp.Versioning.ApiExplorer; +using Asp.Versioning.ApiExplorer.Internal; using Microsoft.Extensions.Options; -using static Asp.Versioning.ApiVersionMapping; -using static System.Globalization.CultureInfo; /// <summary> /// Represents the default implementation of an object that discovers and describes the API version information within an application. @@ -12,7 +11,7 @@ namespace Asp.Versioning.ApiExplorer; [CLSCompliant( false )] public class DefaultApiVersionDescriptionProvider : IApiVersionDescriptionProvider { - private readonly ApiVersionDescriptionCollection collection; + private readonly ApiVersionDescriptionCollection<GroupedApiVersionMetadata> collection; private readonly IOptions<ApiExplorerOptions> options; /// <summary> @@ -28,7 +27,7 @@ public DefaultApiVersionDescriptionProvider( ISunsetPolicyManager sunsetPolicyManager, IOptions<ApiExplorerOptions> apiExplorerOptions ) { - collection = new( this, providers ?? throw new ArgumentNullException( nameof( providers ) ) ); + collection = new( Describe, providers ?? throw new ArgumentNullException( nameof( providers ) ) ); SunsetPolicyManager = sunsetPolicyManager; options = apiExplorerOptions; } @@ -58,133 +57,53 @@ protected virtual IReadOnlyList<ApiVersionDescription> Describe( IReadOnlyList<A { ArgumentNullException.ThrowIfNull( metadata ); - var descriptions = new List<ApiVersionDescription>( capacity: metadata.Count ); - var supported = new HashSet<ApiVersion>(); - var deprecated = new HashSet<ApiVersion>(); - - BucketizeApiVersions( metadata, supported, deprecated ); - AppendDescriptions( descriptions, supported, deprecated: false ); - AppendDescriptions( descriptions, deprecated, deprecated: true ); - - return descriptions.OrderBy( d => d.ApiVersion ).ToArray(); - } - - private void BucketizeApiVersions( IReadOnlyList<ApiVersionMetadata> metadata, HashSet<ApiVersion> supported, HashSet<ApiVersion> deprecated ) - { - var declared = new HashSet<ApiVersion>(); - var advertisedSupported = new HashSet<ApiVersion>(); - var advertisedDeprecated = new HashSet<ApiVersion>(); - - for ( var i = 0; i < metadata.Count; i++ ) + // TODO: consider refactoring and removing GroupedApiVersionDescriptionProvider as both implementations are now + // effectively the same. this cast is safe as an internal implementation detail. if this method is + // overridden, then this code doesn't even run + // + // REF: https://github.com/dotnet/aspnet-api-versioning/issues/1066 + if ( metadata is GroupedApiVersionMetadata[] groupedMetadata ) { - var model = metadata[i].Map( Explicit | Implicit ); - var versions = model.DeclaredApiVersions; - - for ( var j = 0; j < versions.Count; j++ ) - { - declared.Add( versions[j] ); - } - - versions = model.SupportedApiVersions; - - for ( var j = 0; j < versions.Count; j++ ) - { - var version = versions[j]; - supported.Add( version ); - advertisedSupported.Add( version ); - } - - versions = model.DeprecatedApiVersions; - - for ( var j = 0; j < versions.Count; j++ ) - { - var version = versions[j]; - deprecated.Add( version ); - advertisedDeprecated.Add( version ); - } + return DescriptionProvider.Describe( groupedMetadata, SunsetPolicyManager, Options ); } - advertisedSupported.ExceptWith( declared ); - advertisedDeprecated.ExceptWith( declared ); - supported.ExceptWith( advertisedSupported ); - deprecated.ExceptWith( supported.Concat( advertisedDeprecated ) ); - - if ( supported.Count == 0 && deprecated.Count == 0 ) - { - supported.Add( Options.DefaultApiVersion ); - } + return Array.Empty<ApiVersionDescription>(); } - private void AppendDescriptions( List<ApiVersionDescription> descriptions, IEnumerable<ApiVersion> versions, bool deprecated ) + private sealed class GroupedApiVersionMetadata : + ApiVersionMetadata, + IEquatable<GroupedApiVersionMetadata>, + IGroupedApiVersionMetadata, + IGroupedApiVersionMetadataFactory<GroupedApiVersionMetadata> { - foreach ( var version in versions ) - { - var groupName = version.ToString( Options.GroupNameFormat, CurrentCulture ); - var sunsetPolicy = SunsetPolicyManager.TryGetPolicy( version, out var policy ) ? policy : default; - descriptions.Add( new( version, groupName, deprecated, sunsetPolicy ) ); - } - } + private GroupedApiVersionMetadata( string? groupName, ApiVersionMetadata metadata ) + : base( metadata ) => GroupName = groupName; - private sealed class ApiVersionDescriptionCollection( - DefaultApiVersionDescriptionProvider provider, - IEnumerable<IApiVersionMetadataCollationProvider> collators ) - { - private readonly object syncRoot = new(); - private readonly DefaultApiVersionDescriptionProvider provider = provider; - private readonly IApiVersionMetadataCollationProvider[] collators = collators.ToArray(); - private IReadOnlyList<ApiVersionDescription>? items; - private int version; - - public IReadOnlyList<ApiVersionDescription> Items - { - get - { - if ( items is not null && version == ComputeVersion() ) - { - return items; - } + public string? GroupName { get; } - lock ( syncRoot ) - { - var currentVersion = ComputeVersion(); + static GroupedApiVersionMetadata IGroupedApiVersionMetadataFactory<GroupedApiVersionMetadata>.New( + string? groupName, + ApiVersionMetadata metadata ) => new( groupName, metadata ); - if ( items is not null && version == currentVersion ) - { - return items; - } + public bool Equals( GroupedApiVersionMetadata? other ) => + other is not null && other.GetHashCode() == GetHashCode(); - var context = new ApiVersionMetadataCollationContext(); + public override bool Equals( object? obj ) => + obj is not null && + GetType().Equals( obj.GetType() ) && + GetHashCode() == obj.GetHashCode(); - for ( var i = 0; i < collators.Length; i++ ) - { - collators[i].Execute( context ); - } - - items = provider.Describe( context.Results ); - version = currentVersion; - } - - return items; - } - } - - private int ComputeVersion() => - collators.Length switch - { - 0 => 0, - 1 => collators[0].Version, - _ => ComputeVersion( collators ), - }; - - private static int ComputeVersion( IApiVersionMetadataCollationProvider[] providers ) + public override int GetHashCode() { var hash = default( HashCode ); - for ( var i = 0; i < providers.Length; i++ ) + if ( !string.IsNullOrEmpty( GroupName ) ) { - hash.Add( providers[i].Version ); + hash.Add( GroupName, StringComparer.Ordinal ); } + hash.Add( base.GetHashCode() ); + return hash.ToHashCode(); } } diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs index ca31264b..294db52c 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/GroupedApiVersionDescriptionProvider.cs @@ -2,10 +2,8 @@ namespace Asp.Versioning.ApiExplorer; +using Asp.Versioning.ApiExplorer.Internal; using Microsoft.Extensions.Options; -using System.Buffers; -using static Asp.Versioning.ApiVersionMapping; -using static System.Globalization.CultureInfo; /// <summary> /// Represents the default implementation of an object that discovers and describes the API version information within an application. @@ -13,7 +11,7 @@ namespace Asp.Versioning.ApiExplorer; [CLSCompliant( false )] public class GroupedApiVersionDescriptionProvider : IApiVersionDescriptionProvider { - private readonly ApiVersionDescriptionCollection collection; + private readonly ApiVersionDescriptionCollection<GroupedApiVersionMetadata> collection; private readonly IOptions<ApiExplorerOptions> options; /// <summary> @@ -29,7 +27,7 @@ public GroupedApiVersionDescriptionProvider( ISunsetPolicyManager sunsetPolicyManager, IOptions<ApiExplorerOptions> apiExplorerOptions ) { - collection = new( this, providers ?? throw new ArgumentNullException( nameof( providers ) ) ); + collection = new( Describe, providers ?? throw new ArgumentNullException( nameof( providers ) ) ); SunsetPolicyManager = sunsetPolicyManager; options = apiExplorerOptions; } @@ -59,191 +57,17 @@ public GroupedApiVersionDescriptionProvider( protected virtual IReadOnlyList<ApiVersionDescription> Describe( IReadOnlyList<GroupedApiVersionMetadata> metadata ) { ArgumentNullException.ThrowIfNull( metadata ); - - var descriptions = new SortedSet<ApiVersionDescription>( new ApiVersionDescriptionComparer() ); - var supported = new HashSet<GroupedApiVersion>(); - var deprecated = new HashSet<GroupedApiVersion>(); - - BucketizeApiVersions( metadata, supported, deprecated ); - AppendDescriptions( descriptions, supported, deprecated: false ); - AppendDescriptions( descriptions, deprecated, deprecated: true ); - - return descriptions.ToArray(); - } - - private void BucketizeApiVersions( - IReadOnlyList<GroupedApiVersionMetadata> list, - ISet<GroupedApiVersion> supported, - ISet<GroupedApiVersion> deprecated ) - { - var declared = new HashSet<GroupedApiVersion>(); - var advertisedSupported = new HashSet<GroupedApiVersion>(); - var advertisedDeprecated = new HashSet<GroupedApiVersion>(); - - for ( var i = 0; i < list.Count; i++ ) - { - var metadata = list[i]; - var groupName = metadata.GroupName; - var model = metadata.Map( Explicit | Implicit ); - var versions = model.DeclaredApiVersions; - - for ( var j = 0; j < versions.Count; j++ ) - { - declared.Add( new( groupName, versions[j] ) ); - } - - versions = model.SupportedApiVersions; - - for ( var j = 0; j < versions.Count; j++ ) - { - var version = versions[j]; - supported.Add( new( groupName, version ) ); - advertisedSupported.Add( new( groupName, version ) ); - } - - versions = model.DeprecatedApiVersions; - - for ( var j = 0; j < versions.Count; j++ ) - { - var version = versions[j]; - deprecated.Add( new( groupName, version ) ); - advertisedDeprecated.Add( new( groupName, version ) ); - } - } - - advertisedSupported.ExceptWith( declared ); - advertisedDeprecated.ExceptWith( declared ); - supported.ExceptWith( advertisedSupported ); - deprecated.ExceptWith( supported.Concat( advertisedDeprecated ) ); - - if ( supported.Count == 0 && deprecated.Count == 0 ) - { - supported.Add( new( default, Options.DefaultApiVersion ) ); - } - } - - private void AppendDescriptions( - ICollection<ApiVersionDescription> descriptions, - IEnumerable<GroupedApiVersion> versions, - bool deprecated ) - { - var format = Options.GroupNameFormat; - var formatGroupName = Options.FormatGroupName; - - foreach ( var (groupName, version) in versions ) - { - var formattedVersion = version.ToString( format, CurrentCulture ); - var formattedGroupName = - string.IsNullOrEmpty( groupName ) || formatGroupName is null - ? formattedVersion - : formatGroupName( groupName, formattedVersion ); - - var sunsetPolicy = SunsetPolicyManager.TryGetPolicy( version, out var policy ) ? policy : default; - descriptions.Add( new( version, formattedGroupName, deprecated, sunsetPolicy ) ); - } - } - - private sealed class ApiVersionDescriptionCollection( - GroupedApiVersionDescriptionProvider provider, - IEnumerable<IApiVersionMetadataCollationProvider> collators ) - { - private readonly object syncRoot = new(); - private readonly GroupedApiVersionDescriptionProvider provider = provider; - private readonly IApiVersionMetadataCollationProvider[] collators = collators.ToArray(); - private IReadOnlyList<ApiVersionDescription>? items; - private int version; - - public IReadOnlyList<ApiVersionDescription> Items - { - get - { - if ( items is not null && version == ComputeVersion() ) - { - return items; - } - - lock ( syncRoot ) - { - var currentVersion = ComputeVersion(); - - if ( items is not null && version == currentVersion ) - { - return items; - } - - var context = new ApiVersionMetadataCollationContext(); - - for ( var i = 0; i < collators.Length; i++ ) - { - collators[i].Execute( context ); - } - - var results = context.Results; - var metadata = new GroupedApiVersionMetadata[results.Count]; - - for ( var i = 0; i < metadata.Length; i++ ) - { - metadata[i] = new( context.Results.GroupName( i ), results[i] ); - } - - items = provider.Describe( metadata ); - version = currentVersion; - } - - return items; - } - } - - private int ComputeVersion() => - collators.Length switch - { - 0 => 0, - 1 => collators[0].Version, - _ => ComputeVersion( collators ), - }; - - private static int ComputeVersion( IApiVersionMetadataCollationProvider[] providers ) - { - var hash = default( HashCode ); - - for ( var i = 0; i < providers.Length; i++ ) - { - hash.Add( providers[i].Version ); - } - - return hash.ToHashCode(); - } - } - - private sealed class ApiVersionDescriptionComparer : IComparer<ApiVersionDescription> - { - public int Compare( ApiVersionDescription? x, ApiVersionDescription? y ) - { - if ( x is null ) - { - return y is null ? 0 : -1; - } - - if ( y is null ) - { - return 1; - } - - var result = x.ApiVersion.CompareTo( y.ApiVersion ); - - if ( result == 0 ) - { - result = StringComparer.Ordinal.Compare( x.GroupName, y.GroupName ); - } - - return result; - } + return DescriptionProvider.Describe( metadata, SunsetPolicyManager, Options ); } /// <summary> /// Represents the API version metadata applied to an endpoint with an optional group name. /// </summary> - protected class GroupedApiVersionMetadata : ApiVersionMetadata, IEquatable<GroupedApiVersionMetadata> + protected class GroupedApiVersionMetadata : + ApiVersionMetadata, + IEquatable<GroupedApiVersionMetadata>, + IGroupedApiVersionMetadata, + IGroupedApiVersionMetadataFactory<GroupedApiVersionMetadata> { /// <summary> /// Initializes a new instance of the <see cref="GroupedApiVersionMetadata"/> class. @@ -259,6 +83,10 @@ public GroupedApiVersionMetadata( string? groupName, ApiVersionMetadata metadata /// <value>The associated group name, if any.</value> public string? GroupName { get; } + static GroupedApiVersionMetadata IGroupedApiVersionMetadataFactory<GroupedApiVersionMetadata>.New( + string? groupName, + ApiVersionMetadata metadata ) => new( groupName, metadata ); + /// <inheritdoc /> public bool Equals( GroupedApiVersionMetadata? other ) => other is not null && other.GetHashCode() == GetHashCode(); @@ -284,6 +112,4 @@ public override int GetHashCode() return hash.ToHashCode(); } } - - private record struct GroupedApiVersion( string? GroupName, ApiVersion ApiVersion ); } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/ApiVersionDescriptionCollection.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/ApiVersionDescriptionCollection.cs new file mode 100644 index 00000000..f5847dd0 --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/ApiVersionDescriptionCollection.cs @@ -0,0 +1,76 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer.Internal; + +internal sealed class ApiVersionDescriptionCollection<T>( + Func<IReadOnlyList<T>, IReadOnlyList<ApiVersionDescription>> describe, + IEnumerable<IApiVersionMetadataCollationProvider> collators ) + where T : IGroupedApiVersionMetadata, IGroupedApiVersionMetadataFactory<T> +{ + private readonly object syncRoot = new(); + private readonly Func<IReadOnlyList<T>, IReadOnlyList<ApiVersionDescription>> describe = describe; + private readonly IApiVersionMetadataCollationProvider[] collators = collators.ToArray(); + private IReadOnlyList<ApiVersionDescription>? items; + private int version; + + public IReadOnlyList<ApiVersionDescription> Items + { + get + { + if ( items is not null && version == ComputeVersion() ) + { + return items; + } + + lock ( syncRoot ) + { + var currentVersion = ComputeVersion(); + + if ( items is not null && version == currentVersion ) + { + return items; + } + + var context = new ApiVersionMetadataCollationContext(); + + for ( var i = 0; i < collators.Length; i++ ) + { + collators[i].Execute( context ); + } + + var results = context.Results; + var metadata = new T[results.Count]; + + for ( var i = 0; i < metadata.Length; i++ ) + { + metadata[i] = T.New( context.Results.GroupName( i ), results[i] ); + } + + items = describe( metadata ); + version = currentVersion; + } + + return items; + } + } + + private int ComputeVersion() => + collators.Length switch + { + 0 => 0, + 1 => collators[0].Version, + _ => ComputeVersion( collators ), + }; + + private static int ComputeVersion( IApiVersionMetadataCollationProvider[] providers ) + { + var hash = default( HashCode ); + + for ( var i = 0; i < providers.Length; i++ ) + { + hash.Add( providers[i].Version ); + } + + return hash.ToHashCode(); + } +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/ApiVersionDescriptionComparer.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/ApiVersionDescriptionComparer.cs new file mode 100644 index 00000000..3fb73385 --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/ApiVersionDescriptionComparer.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer.Internal; + +internal sealed class ApiVersionDescriptionComparer : IComparer<ApiVersionDescription> +{ + public int Compare( ApiVersionDescription? x, ApiVersionDescription? y ) + { + if ( x is null ) + { + return y is null ? 0 : -1; + } + + if ( y is null ) + { + return 1; + } + + var result = x.ApiVersion.CompareTo( y.ApiVersion ); + + if ( result == 0 ) + { + result = StringComparer.Ordinal.Compare( x.GroupName, y.GroupName ); + } + + return result; + } +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/DescriptionProvider.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/DescriptionProvider.cs new file mode 100644 index 00000000..ce3a0dbd --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/DescriptionProvider.cs @@ -0,0 +1,107 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer.Internal; + +using static Asp.Versioning.ApiVersionMapping; +using static System.Globalization.CultureInfo; + +internal static class DescriptionProvider +{ + internal static ApiVersionDescription[] Describe<T>( + IReadOnlyList<T> metadata, + ISunsetPolicyManager sunsetPolicyManager, + ApiExplorerOptions options ) + where T : IGroupedApiVersionMetadata, IEquatable<T> + { + var descriptions = new SortedSet<ApiVersionDescription>( new ApiVersionDescriptionComparer() ); + var supported = new HashSet<GroupedApiVersion>(); + var deprecated = new HashSet<GroupedApiVersion>(); + + BucketizeApiVersions( metadata, supported, deprecated, options ); + AppendDescriptions( descriptions, supported, sunsetPolicyManager, options, deprecated: false ); + AppendDescriptions( descriptions, deprecated, sunsetPolicyManager, options, deprecated: true ); + + return [.. descriptions]; + } + + private static void BucketizeApiVersions<T>( + IReadOnlyList<T> list, + HashSet<GroupedApiVersion> supported, + HashSet<GroupedApiVersion> deprecated, + ApiExplorerOptions options ) + where T : IGroupedApiVersionMetadata + { + var declared = new HashSet<GroupedApiVersion>(); + var advertisedSupported = new HashSet<GroupedApiVersion>(); + var advertisedDeprecated = new HashSet<GroupedApiVersion>(); + + for ( var i = 0; i < list.Count; i++ ) + { + var metadata = list[i]; + var groupName = metadata.GroupName; + var model = metadata.Map( Explicit | Implicit ); + var versions = model.DeclaredApiVersions; + + for ( var j = 0; j < versions.Count; j++ ) + { + declared.Add( new( groupName, versions[j] ) ); + } + + versions = model.SupportedApiVersions; + + for ( var j = 0; j < versions.Count; j++ ) + { + var version = versions[j]; + supported.Add( new( groupName, version ) ); + advertisedSupported.Add( new( groupName, version ) ); + } + + versions = model.DeprecatedApiVersions; + + for ( var j = 0; j < versions.Count; j++ ) + { + var version = versions[j]; + deprecated.Add( new( groupName, version ) ); + advertisedDeprecated.Add( new( groupName, version ) ); + } + } + + advertisedSupported.ExceptWith( declared ); + advertisedDeprecated.ExceptWith( declared ); + supported.ExceptWith( advertisedSupported ); + deprecated.ExceptWith( supported.Concat( advertisedDeprecated ) ); + + if ( supported.Count == 0 && deprecated.Count == 0 ) + { + supported.Add( new( default, options.DefaultApiVersion ) ); + } + } + + private static void AppendDescriptions( + SortedSet<ApiVersionDescription> descriptions, + HashSet<GroupedApiVersion> versions, + ISunsetPolicyManager sunsetPolicyManager, + ApiExplorerOptions options, + bool deprecated ) + { + var format = options.GroupNameFormat; + var formatGroupName = options.FormatGroupName; + + foreach ( var (groupName, version) in versions ) + { + var formattedGroupName = groupName; + + if ( string.IsNullOrEmpty( formattedGroupName ) ) + { + formattedGroupName = version.ToString( format, CurrentCulture ); + } + else if ( formatGroupName is not null ) + { + formattedGroupName = formatGroupName( formattedGroupName, version.ToString( format, CurrentCulture ) ); + } + + var sunsetPolicy = sunsetPolicyManager.TryGetPolicy( version, out var policy ) ? policy : default; + descriptions.Add( new( version, formattedGroupName, deprecated, sunsetPolicy ) ); + } + } +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/GroupedApiVersion.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/GroupedApiVersion.cs new file mode 100644 index 00000000..8d276e60 --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/GroupedApiVersion.cs @@ -0,0 +1,5 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer.Internal; + +internal record struct GroupedApiVersion( string? GroupName, ApiVersion ApiVersion ); \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/IGroupedApiVersionMetadata.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/IGroupedApiVersionMetadata.cs new file mode 100644 index 00000000..ec0c13e3 --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/IGroupedApiVersionMetadata.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer.Internal; + +internal interface IGroupedApiVersionMetadata +{ + string? GroupName { get; } + + string Name { get; } + + bool IsApiVersionNeutral { get; } + + ApiVersionModel Map( ApiVersionMapping mapping ); + + ApiVersionMapping MappingTo( ApiVersion? apiVersion ); + + bool IsMappedTo( ApiVersion? apiVersion ); + + void Deconstruct( out ApiVersionModel apiModel, out ApiVersionModel endpointModel ); +} \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/IGroupedApiVersionMetadataFactory.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/IGroupedApiVersionMetadataFactory.cs new file mode 100644 index 00000000..ac9d885f --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Internal/IGroupedApiVersionMetadataFactory.cs @@ -0,0 +1,9 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer.Internal; + +internal interface IGroupedApiVersionMetadataFactory<out T> + where T : IGroupedApiVersionMetadata +{ + static abstract T New( string? groupName, ApiVersionMetadata metadata ); +} \ No newline at end of file From 8ac43c59ad6aaadeb53d7d1f910d767e8a792bff Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Fri, 22 Mar 2024 10:09:45 -0700 Subject: [PATCH 107/131] Parse versions single header CSVs. Fixes #1070 --- .../ApiVersionEnumerator.cs | 73 ++++++++--- .../ApiVersionEnumeratorTest.cs | 116 ++++++++++++++++++ 2 files changed, 173 insertions(+), 16 deletions(-) create mode 100644 src/Client/test/Asp.Versioning.Http.Client.Tests/ApiVersionEnumeratorTest.cs diff --git a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs index 49139052..6f940aee 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs +++ b/src/Client/src/Asp.Versioning.Http.Client/ApiVersionEnumerator.cs @@ -5,14 +5,20 @@ namespace Asp.Versioning.Http; +#if NET +using System.Buffers; +#endif using System.Collections; +#if NET +using static System.StringSplitOptions; +#endif /// <summary> /// Represents an enumerator of API versions from a HTTP header. /// </summary> public readonly struct ApiVersionEnumerator : IEnumerable<ApiVersion> { - private readonly IEnumerable<string> values; + private readonly string[] values; private readonly IApiVersionParser parser; /// <summary> @@ -29,37 +35,72 @@ public ApiVersionEnumerator( ArgumentNullException.ThrowIfNull( response ); ArgumentException.ThrowIfNullOrEmpty( headerName ); - this.values = - response.Headers.TryGetValues( headerName, out var values ) - ? values - : Enumerable.Empty<string>(); - + this.values = response.Headers.TryGetValues( headerName, out var values ) ? values.ToArray() : []; this.parser = parser ?? ApiVersionParser.Default; } /// <inheritdoc /> public IEnumerator<ApiVersion> GetEnumerator() { - using var iterator = values.GetEnumerator(); +#if NETSTANDARD + for ( var i = 0; i < values.Length; i++ ) + { + var items = values[i].Split( ',' ); - if ( !iterator.MoveNext() ) + for ( var j = 0; j < items.Length; j++ ) + { + var item = items[j].Trim(); + + if ( item.Length > 0 && parser.TryParse( item, out var result ) ) + { + yield return result!; + } + } + } +#else + for ( var i = 0; i < values.Length; i++ ) { - yield break; + var (count, versions) = ParseVersions( values[i] ); + + for ( var j = 0; j < count; j++ ) + { + yield return versions[j]; + } } +#endif + } - if ( parser.TryParse( iterator.Current, out var value ) ) + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +#if NET + private (int Count, ApiVersion[] Results) ParseVersions( ReadOnlySpan<char> value ) + { + var pool = ArrayPool<Range>.Shared; + var ranges = pool.Rent( 5 ); + var length = value.Split( ranges, ',', RemoveEmptyEntries | TrimEntries ); + + while ( length >= ranges.Length ) { - yield return value!; + pool.Return( ranges ); + length <<= 1; + ranges = pool.Rent( length ); + length = value.Split( ranges, ',', RemoveEmptyEntries | TrimEntries ); } - while ( iterator.MoveNext() ) + var results = new ApiVersion[length]; + var count = 0; + + for ( var i = 0; i < length; i++ ) { - if ( parser.TryParse( iterator.Current, out value ) ) + var text = value[ranges[i]]; + + if ( text.Length > 0 && parser.TryParse( text, out var result ) ) { - yield return value!; + results[count++] = result; } } - } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + pool.Return( ranges ); + return (count, results); + } +#endif } \ No newline at end of file diff --git a/src/Client/test/Asp.Versioning.Http.Client.Tests/ApiVersionEnumeratorTest.cs b/src/Client/test/Asp.Versioning.Http.Client.Tests/ApiVersionEnumeratorTest.cs new file mode 100644 index 00000000..864a8d92 --- /dev/null +++ b/src/Client/test/Asp.Versioning.Http.Client.Tests/ApiVersionEnumeratorTest.cs @@ -0,0 +1,116 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.Http; + +public class ApiVersionEnumeratorTest +{ + [Fact] + public void enumerator_should_process_single_header_value() + { + // arrange + var response = new HttpResponseMessage(); + + response.Headers.Add( "api-supported-versions", "1.0" ); + + var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" ); + + // act + var results = enumerator.ToArray(); + + // assert + results.Should().BeEquivalentTo( [new ApiVersion( 1.0 )] ); + } + + [Fact] + public void enumerator_should_process_multiple_header_values() + { + // arrange + var response = new HttpResponseMessage(); + + response.Headers.Add( "api-supported-versions", ["1.0", "2.0"] ); + + var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" ); + + // act + var results = enumerator.ToArray(); + + // assert + results.Should().BeEquivalentTo( new ApiVersion[] { new( 1.0 ), new( 2.0 ) } ); + } + + [Theory] + [InlineData( "1.0,2.0" )] + [InlineData( "1.0, 2.0" )] + [InlineData( "1.0,,2.0" )] + [InlineData( "1.0, abc, 2.0" )] + public void enumerator_should_process_single_header_comma_separated_values( string value ) + { + // arrange + var response = new HttpResponseMessage(); + + response.Headers.Add( "api-supported-versions", [value] ); + + var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" ); + + // act + var results = enumerator.ToArray(); + + // assert + results.Should().BeEquivalentTo( new ApiVersion[] { new( 1.0 ), new( 2.0 ) } ); + } + + [Fact] + public void enumerator_should_process_many_header_comma_separated_values() + { + // arrange + const string Value = "1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0"; + var response = new HttpResponseMessage(); + + response.Headers.Add( "api-supported-versions", [Value] ); + + var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" ); + + // act + var results = enumerator.ToArray(); + + // assert + results.Should().BeEquivalentTo( + new ApiVersion[] + { + new( 1.0 ), + new( 2.0 ), + new( 3.0 ), + new( 4.0 ), + new( 5.0 ), + new( 6.0 ), + new( 7.0 ), + new( 8.0 ), + new( 9.0 ), + new( 10.0 ), + } ); + } + + [Fact] + public void enumerator_should_process_multiple_header_comma_separated_values() + { + // arrange + var response = new HttpResponseMessage(); + + response.Headers.Add( "api-supported-versions", ["1.0, 2.0", "3.0, 4.0"] ); + + var enumerator = new ApiVersionEnumerator( response, "api-supported-versions" ); + + // act + var results = enumerator.ToArray(); + + // assert + results.Should().BeEquivalentTo( + new ApiVersion[] + { + new( 1.0 ), + new( 2.0 ), + new( 3.0 ), + new( 4.0 ), + } ); + } +} \ No newline at end of file From 0b50d4541881437df5a1a7011d44121d0ad6fb1c Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Fri, 22 Mar 2024 10:10:05 -0700 Subject: [PATCH 108/131] Bump version and add release notes --- .../Asp.Versioning.Http.Client.csproj | 2 +- src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj b/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj index 75416005..e508c993 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj +++ b/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj @@ -1,7 +1,7 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>8.0.0</VersionPrefix> + <VersionPrefix>8.0.1</VersionPrefix> <AssemblyVersion>8.0.0.0</AssemblyVersion> <TargetFrameworks>$(DefaultTargetFramework);netstandard1.1;netstandard2.0</TargetFrameworks> <RootNamespace>Asp.Versioning.Http</RootNamespace> diff --git a/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt b/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt index 5f282702..0cf60623 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt +++ b/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +Parse single header CSVs ([#1070](https://github.com/dotnet/aspnet-api-versioning/issues/1070)) \ No newline at end of file From f9aa66da1b9a479fe79a8a3de7fcfc031b6f5b1f Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Sun, 24 Mar 2024 10:57:36 -0700 Subject: [PATCH 109/131] Expose JsonSerializerContext for extenders. Related to #1072 --- .../Asp.Versioning.Http/ErrorObjectWriter.cs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs index 82460917..b02d1260 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectWriter.cs @@ -1,5 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. +// Ignore Spelling: Serializer namespace Asp.Versioning; using Microsoft.AspNetCore.Http; @@ -30,6 +31,19 @@ public partial class ErrorObjectWriter : IProblemDetailsWriter public ErrorObjectWriter( IOptions<JsonOptions> options ) => this.options = ( options ?? throw new ArgumentNullException( nameof( options ) ) ).Value.SerializerOptions; + /// <summary> + /// Gets the associated, default <see cref="JsonSerializerContext"/>. + /// </summary> + /// <value>The associated, default <see cref="JsonSerializerContext"/>.</value> + public static JsonSerializerContext DefaultJsonSerializerContext => ErrorObjectJsonContext.Default; + + /// <summary> + /// Creates and returns a new <see cref="JsonSerializerContext"/> associated with the writer. + /// </summary> + /// <param name="options">The <see cref="JsonSerializerOptions">JSON serializer options</see> to use.</param> + /// <returns>A new <see cref="JsonSerializerContext"/>.</returns> + public static JsonSerializerContext NewJsonSerializerContext( JsonSerializerOptions options ) => new ErrorObjectJsonContext( options ); + /// <inheritdoc /> public virtual bool CanWrite( ProblemDetailsContext context ) { @@ -89,6 +103,7 @@ internal ErrorObject( ProblemDetails problemDetails ) => /// </summary> protected internal readonly partial struct ErrorDetail { + private const string CodeProperty = "code"; private readonly ProblemDetails problemDetails; private readonly InnerError? innerError; private readonly Dictionary<string, object> extensions = []; @@ -103,23 +118,21 @@ internal ErrorDetail( ProblemDetails problemDetails ) /// Gets or sets one of a server-defined set of error codes. /// </summary> /// <value>A server-defined error code.</value> - [JsonPropertyName( "code" )] + [JsonPropertyName( CodeProperty )] [JsonIgnore( Condition = WhenWritingNull )] public string? Code { - get => problemDetails.Extensions.TryGetValue( "code", out var value ) && - value is string code ? - code : - default; + get => problemDetails.Extensions.TryGetValue( CodeProperty, out var value ) && + value is string code ? code : default; set { if ( value is null ) { - problemDetails.Extensions.Remove( "code" ); + problemDetails.Extensions.Remove( CodeProperty ); } else { - problemDetails.Extensions["code"] = value; + problemDetails.Extensions[CodeProperty] = value; } } } From 06d3d628ab5c57b6038a23b1ca5492bf6c7c6e06 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Sun, 24 Mar 2024 10:58:10 -0700 Subject: [PATCH 110/131] Simplify the setup of Error Objects. Related to #1072 --- .../IServiceCollectionExtensions.cs | 96 +++++++++++++++++-- .../ErrorObjectJsonOptionsSetup.cs | 24 ----- 2 files changed, 90 insertions(+), 30 deletions(-) delete mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs index 14606936..367c908c 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs @@ -11,7 +11,9 @@ namespace Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using System; +using System.Diagnostics.CodeAnalysis; using static Microsoft.Extensions.DependencyInjection.ServiceDescriptor; +using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; /// <summary> /// Provides extension methods for the <see cref="IServiceCollection"/> interface. @@ -75,6 +77,65 @@ public static IApiVersioningBuilder EnableApiVersionBinding( this IApiVersioning return builder; } + /// <summary> + /// Adds error object support in problem details. + /// </summary> + /// <param name="services">The <see cref="IServiceCollection">services</see> available in the application.</param> + /// <param name="setup">The <see cref="JsonOptions">JSON options</see> setup <see cref="Action{T}"/> to perform, if any.</param> + /// <returns>The original <paramref name="services"/>.</returns> + /// <remarks> + /// <para> + /// This method is only intended to provide backward compatibility with previous library versions by converting + /// <see cref="Microsoft.AspNetCore.Mvc.ProblemDetails"/> into Error Objects that conform to the + /// <a ref="https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#7102-error-condition-responses">Error Responses</a> + /// in the Microsoft REST API Guidelines and + /// <a ref="https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#_Toc38457793">OData Error Responses</a>. + /// </para> + /// <para> + /// This method should be called before <see cref="ProblemDetailsServiceCollectionExtensions.AddProblemDetails(IServiceCollection)"/>. + /// </para> + /// </remarks> + public static IServiceCollection AddErrorObjects( this IServiceCollection services, Action<JsonOptions>? setup = default ) => + AddErrorObjects<ErrorObjectWriter>( services, setup ); + + /// <summary> + /// Adds error object support in problem details. + /// </summary> + /// <typeparam name="TWriter">The type of <see cref="ErrorObjectWriter"/>.</typeparam> + /// <param name="services">The <see cref="IServiceCollection">services</see> available in the application.</param> + /// <param name="setup">The <see cref="JsonOptions">JSON options</see> setup <see cref="Action{T}"/> to perform, if any.</param> + /// <returns>The original <paramref name="services"/>.</returns> + /// <remarks> + /// <para> + /// This method is only intended to provide backward compatibility with previous library versions by converting + /// <see cref="Microsoft.AspNetCore.Mvc.ProblemDetails"/> into Error Objects that conform to the + /// <a ref="https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#7102-error-condition-responses">Error Responses</a> + /// in the Microsoft REST API Guidelines and + /// <a ref="https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#_Toc38457793">OData Error Responses</a>. + /// </para> + /// <para> + /// This method should be called before <see cref="ProblemDetailsServiceCollectionExtensions.AddProblemDetails(IServiceCollection)"/>. + /// </para> + /// </remarks> + public static IServiceCollection AddErrorObjects<[DynamicallyAccessedMembers( PublicConstructors )] TWriter>( + this IServiceCollection services, + Action<JsonOptions>? setup = default ) + where TWriter : ErrorObjectWriter + { + ArgumentNullException.ThrowIfNull( services ); + + services.TryAddEnumerable( Singleton<IProblemDetailsWriter, TWriter>() ); + services.Configure( setup ?? DefaultErrorObjectJsonConfig ); + + // TODO: remove with TryAddErrorObjectJsonOptions in 9.0+ + services.AddTransient<ErrorObjectsAdded>(); + + return services; + } + + private static void DefaultErrorObjectJsonConfig( JsonOptions options ) => + options.SerializerOptions.TypeInfoResolverChain.Insert( 0, ErrorObjectWriter.ErrorObjectJsonContext.Default ); + private static void AddApiVersioningServices( IServiceCollection services ) { ArgumentNullException.ThrowIfNull( services ); @@ -180,23 +241,46 @@ static Rfc7231ProblemDetailsWriter NewProblemDetailsWriter( IServiceProvider ser new( (IProblemDetailsWriter) serviceProvider.GetRequiredService( decoratedType ) ); } + // TODO: retain for 8.1.x back-compat, but remove in 9.0+ in favor of AddErrorObjects for perf private static void TryAddErrorObjectJsonOptions( IServiceCollection services ) { var serviceType = typeof( IProblemDetailsWriter ); var implementationType = typeof( ErrorObjectWriter ); + var markerType = typeof( ErrorObjectsAdded ); + var hasErrorObjects = false; + var hasErrorObjectsJsonConfig = false; for ( var i = 0; i < services.Count; i++ ) { var service = services[i]; - // inheritance is intentionally not considered here because it will require a user-defined - // JsonSerlizerContext and IConfigureOptions<JsonOptions> - if ( service.ServiceType == serviceType && - service.ImplementationType == implementationType ) + if ( !hasErrorObjects && + service.ServiceType == serviceType && + implementationType.IsAssignableFrom( service.ImplementationType ) ) { - services.TryAddEnumerable( Singleton<IConfigureOptions<JsonOptions>, ErrorObjectJsonOptionsSetup>() ); - return; + hasErrorObjects = true; + + if ( hasErrorObjectsJsonConfig ) + { + break; + } } + else if ( service.ServiceType == markerType ) + { + hasErrorObjectsJsonConfig = true; + + if ( hasErrorObjects ) + { + break; + } + } + } + + if ( hasErrorObjects && !hasErrorObjectsJsonConfig ) + { + services.Configure<JsonOptions>( DefaultErrorObjectJsonConfig ); } } + + private sealed class ErrorObjectsAdded { } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs deleted file mode 100644 index 664c4840..00000000 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectJsonOptionsSetup.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. - -#pragma warning disable CA1812 // Avoid uninstantiated internal classes - -namespace Asp.Versioning; - -using Microsoft.AspNetCore.Http.Json; -using Microsoft.Extensions.Options; - -/// <summary> -/// Adds the ErrorObjectJsonContext to the current JsonSerializerOptions. -/// -/// This allows for consistent serialization behavior for ErrorObject regardless if the -/// default reflection-based serializer is used and makes it trim/NativeAOT compatible. -/// </summary> -internal sealed class ErrorObjectJsonOptionsSetup : IConfigureOptions<JsonOptions> -{ - // Always insert the ErrorObjectJsonContext to the beginning of the chain at the time this Configure - // is invoked. This JsonTypeInfoResolver will be before the default reflection-based resolver, and - // before any other resolvers currently added. If apps need to customize serialization, they can - // prepend a custom ErrorObject resolver to the chain in an IConfigureOptions<JsonOptions> registered. - public void Configure( JsonOptions options ) => - options.SerializerOptions.TypeInfoResolverChain.Insert( 0, ErrorObjectWriter.ErrorObjectJsonContext.Default ); -} \ No newline at end of file From b0c34031a330d50fe095720c350956ecd696bdf9 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Sun, 24 Mar 2024 11:45:29 -0700 Subject: [PATCH 111/131] Fix code analysis --- .../DependencyInjection/IServiceCollectionExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs index 367c908c..46ecac55 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs @@ -282,5 +282,7 @@ private static void TryAddErrorObjectJsonOptions( IServiceCollection services ) } } +// TEMP: this is a marker class to test whether Error Objects have been explicitly added. remove in 9.0+ +#pragma warning disable CA1812 // Avoid uninstantiated internal classes private sealed class ErrorObjectsAdded { } } \ No newline at end of file From 7aad49f5246d32f4e977c8c424fb3a822e366e8f Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 25 Mar 2024 09:32:11 -0700 Subject: [PATCH 112/131] Improve string formatting --- .../ApiDescriptionExtensions.cs | 9 +++++++-- .../ApiVersionParameterDescriptionContext.cs | 19 +++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiDescriptionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiDescriptionExtensions.cs index a0cf3835..30aaf0cd 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiDescriptionExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiDescriptionExtensions.cs @@ -106,9 +106,14 @@ public static bool TryUpdateRelativePathAndRemoveApiVersionParameter( this ApiDe return false; } - var token = '{' + parameter.Name + '}'; + Span<char> token = stackalloc char[parameter.Name.Length + 2]; + + token[0] = '{'; + token[^1] = '}'; + parameter.Name.AsSpan().CopyTo( token.Slice( 1, parameter.Name.Length ) ); + var value = apiVersion.ToString( options.SubstitutionFormat, CultureInfo.InvariantCulture ); - var newRelativePath = relativePath.Replace( token, value, StringComparison.Ordinal ); + var newRelativePath = relativePath.Replace( token.ToString(), value, StringComparison.Ordinal ); if ( relativePath == newRelativePath ) { diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs index 136ad268..da70b954 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs @@ -9,6 +9,7 @@ namespace Asp.Versioning.ApiExplorer; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Template; +using System.Runtime.CompilerServices; using static Asp.Versioning.ApiVersionParameterLocation; using static System.Linq.Enumerable; using static System.StringComparison; @@ -304,7 +305,7 @@ routeInfo.Constraints is IEnumerable<IRouteConstraint> constraints && continue; } - var token = $"{parameter.Name}:{constraintName}"; + var token = FormatToken( parameter.Name, constraintName ); parameterDescription.Name = parameter.Name; description.RelativePath = relativePath.Replace( token, parameter.Name, Ordinal ); @@ -375,7 +376,7 @@ routeInfo.Constraints is IEnumerable<IRouteConstraint> constraints && }, Source = BindingSource.Path, }; - var token = $"{parameter.Name}:{constraintName}"; + var token = FormatToken( parameter.Name!, constraintName! ); description.RelativePath = relativePath.Replace( token, parameter.Name, Ordinal ); description.ParameterDescriptions.Insert( 0, result ); @@ -457,4 +458,18 @@ private static bool FirstParameterIsOptional( return apiVersion == defaultApiVersion; } + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static string FormatToken( ReadOnlySpan<char> parameterName, ReadOnlySpan<char> constraintName ) + { + var left = parameterName.Length; + var right = constraintName.Length; + Span<char> token = stackalloc char[left + right + 1]; + + parameterName.CopyTo( token[..left] ); + token[left] = ':'; + constraintName.CopyTo( token.Slice( left + 1, right ) ); + + return token.ToString(); + } } \ No newline at end of file From be7021cf87385fa7dae11eba79e05a11026c2042 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 25 Mar 2024 09:32:46 -0700 Subject: [PATCH 113/131] Use collection initializers --- .../ApiExplorer/PartialODataDescriptionProvider.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs index fc1ff1b6..47eac901 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/PartialODataDescriptionProvider.cs @@ -145,14 +145,10 @@ private static int ODataOrder() => new ODataApiDescriptionProvider( new StubModelMetadataProvider(), new StubModelTypeBuilder(), - new OptionsFactory<ODataOptions>( - Enumerable.Empty<IConfigureOptions<ODataOptions>>(), - Enumerable.Empty<IPostConfigureOptions<ODataOptions>>() ), + new OptionsFactory<ODataOptions>( [], [] ), Opts.Create( new ODataApiExplorerOptions( - new( - new StubODataApiVersionCollectionProvider(), - Enumerable.Empty<IModelConfiguration>() ) ) ) ).Order; + new( new StubODataApiVersionCollectionProvider(), [] ) ) ) ).Order; [MethodImpl( MethodImplOptions.AggressiveInlining )] private static void MarkAsAdHoc( ODataModelBuilder builder, IEdmModel model ) => From 42607c5513ba11381d79a4313eb3e0eb8f8fc5f4 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 25 Mar 2024 09:34:37 -0700 Subject: [PATCH 114/131] Support collection parameters in functions. Fixes #999 --- .../ODataApiDescriptionProvider.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs index 5736e8f4..fdb4ff8d 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ApiExplorer/ODataApiDescriptionProvider.cs @@ -142,6 +142,7 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context ) else { UpdateModelTypes( result, matched ); + UpdateFunctionCollectionParameters( result, matched ); } } @@ -456,6 +457,75 @@ private void UpdateModelTypes( ApiDescription description, IODataRoutingMetadata } } + private static void UpdateFunctionCollectionParameters( ApiDescription description, IODataRoutingMetadata metadata ) + { + var parameters = description.ParameterDescriptions; + + if ( parameters.Count == 0 ) + { + return; + } + + var function = default( IEdmFunction ); + var mapping = default( IDictionary<string, string> ); + + for ( var i = 0; i < metadata.Template.Count; i++ ) + { + var segment = metadata.Template[i]; + + if ( segment is FunctionSegmentTemplate func ) + { + function = func.Function; + mapping = func.ParameterMappings; + break; + } + else if ( segment is FunctionImportSegmentTemplate import ) + { + function = import.FunctionImport.Function; + mapping = import.ParameterMappings; + break; + } + } + + if ( function is null || mapping is null ) + { + return; + } + + var name = default( string ); + + foreach ( var parameter in function.Parameters ) + { + if ( parameter.Type.IsCollection() && + mapping.TryGetValue( parameter.Name, out name ) && + parameters.SingleOrDefault( p => p.Name == name ) is { } param ) + { + param.Source = BindingSource.Path; + break; + } + } + + var path = description.RelativePath; + + if ( string.IsNullOrEmpty( name ) || string.IsNullOrEmpty( path ) ) + { + return; + } + + var span = name.AsSpan(); + Span<char> oldValue = stackalloc char[name.Length + 2]; + Span<char> newValue = stackalloc char[name.Length + 4]; + + newValue[1] = oldValue[0] = '{'; + newValue[^2] = oldValue[^1] = '}'; + newValue[0] = '['; + newValue[^1] = ']'; + span.CopyTo( oldValue.Slice( 1, name.Length ) ); + span.CopyTo( newValue.Slice( 2, name.Length ) ); + + description.RelativePath = path.Replace( oldValue.ToString(), newValue.ToString(), Ordinal ); + } + private sealed class ApiDescriptionComparer : IEqualityComparer<ApiDescription> { private readonly IEqualityComparer<string?> comparer = StringComparer.OrdinalIgnoreCase; From ea90819930becbe48b40ebee977667f002e44ef1 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 25 Mar 2024 18:37:59 -0700 Subject: [PATCH 115/131] Fix spacing --- .../src/Asp.Versioning.Mvc/ApiExplorer/MvcEndpointInspector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiExplorer/MvcEndpointInspector.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiExplorer/MvcEndpointInspector.cs index 8729791c..28bed7bf 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiExplorer/MvcEndpointInspector.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApiExplorer/MvcEndpointInspector.cs @@ -9,7 +9,7 @@ namespace Asp.Versioning.ApiExplorer; /// Represents the <see cref="IEndpointInspector">inspector</see> that understands /// <see cref="Endpoint">endpoints</see> defined by MVC controllers. /// </summary> -[CLSCompliant(false)] +[CLSCompliant( false )] public sealed class MvcEndpointInspector : IEndpointInspector { /// <inheritdoc /> From cff65fcecc588176c9f4ebb423485f198b401eb1 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 25 Mar 2024 18:39:09 -0700 Subject: [PATCH 116/131] Use static lambdas --- .../DependencyInjection/IServiceCollectionExtensions.cs | 9 ++++----- .../IApiVersioningBuilderExtensions.cs | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs index 46ecac55..83521614 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs @@ -10,9 +10,8 @@ namespace Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; -using System; using System.Diagnostics.CodeAnalysis; -using static Microsoft.Extensions.DependencyInjection.ServiceDescriptor; +using static ServiceDescriptor; using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; /// <summary> @@ -141,9 +140,9 @@ private static void AddApiVersioningServices( IServiceCollection services ) ArgumentNullException.ThrowIfNull( services ); services.TryAddSingleton<IApiVersionParser, ApiVersionParser>(); - services.AddSingleton( sp => sp.GetRequiredService<IOptions<ApiVersioningOptions>>().Value.ApiVersionReader ); - services.AddSingleton( sp => (IApiVersionParameterSource) sp.GetRequiredService<IOptions<ApiVersioningOptions>>().Value.ApiVersionReader ); - services.AddSingleton( sp => sp.GetRequiredService<IOptions<ApiVersioningOptions>>().Value.ApiVersionSelector ); + services.AddSingleton( static sp => sp.GetRequiredService<IOptions<ApiVersioningOptions>>().Value.ApiVersionReader ); + services.AddSingleton( static sp => (IApiVersionParameterSource) sp.GetRequiredService<IOptions<ApiVersioningOptions>>().Value.ApiVersionReader ); + services.AddSingleton( static sp => sp.GetRequiredService<IOptions<ApiVersioningOptions>>().Value.ApiVersionSelector ); services.TryAddSingleton<IReportApiVersions, DefaultApiVersionReporter>(); services.TryAddSingleton<ISunsetPolicyManager, SunsetPolicyManager>(); services.TryAddEnumerable( Transient<IValidateOptions<ApiVersioningOptions>, ValidateApiVersioningOptions>() ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs index 5f895312..eef6b507 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -15,7 +15,7 @@ namespace Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using System.Runtime.CompilerServices; -using static Microsoft.Extensions.DependencyInjection.ServiceDescriptor; +using static ServiceDescriptor; /// <summary> /// Provides ASP.NET Core MVC specific extension methods for <see cref="IApiVersioningBuilder"/>. @@ -57,9 +57,9 @@ private static void AddServices( IServiceCollection services ) services.AddMvcCore(); services.TryAddSingleton<IOptionsFactory<MvcApiVersioningOptions>, MvcApiVersioningOptionsFactory<MvcApiVersioningOptions>>(); services.TryAddSingleton<IControllerNameConvention, DefaultControllerNameConvention>(); - services.TryAddSingleton<IApiVersionConventionBuilder>( sp => new ApiVersionConventionBuilder( sp.GetRequiredService<IControllerNameConvention>() ) ); + services.TryAddSingleton<IApiVersionConventionBuilder>( static sp => new ApiVersionConventionBuilder( sp.GetRequiredService<IControllerNameConvention>() ) ); services.TryAddSingleton<IApiControllerFilter, DefaultApiControllerFilter>(); - services.TryAddSingleton( sp => new ReportApiVersionsAttribute( sp.GetRequiredService<IReportApiVersions>() ) ); + services.TryAddSingleton( static sp => new ReportApiVersionsAttribute( sp.GetRequiredService<IReportApiVersions>() ) ); services.AddSingleton<ApplyContentTypeVersionActionFilter>(); services.TryAddEnumerable( Transient<IPostConfigureOptions<MvcOptions>, ApiVersioningMvcOptionsSetup>() ); services.TryAddEnumerable( Transient<IApplicationModelProvider, ApiVersioningApplicationModelProvider>() ); From 51033a9ba82a5140e47aabbbc3a49b452f01b726 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 25 Mar 2024 18:40:29 -0700 Subject: [PATCH 117/131] Add parameterless extension method for creating IApiVersionDescriptionProvider --- ...ionDescriptionProviderFactoryExtensions.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IApiVersionDescriptionProviderFactoryExtensions.cs diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IApiVersionDescriptionProviderFactoryExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IApiVersionDescriptionProviderFactoryExtensions.cs new file mode 100644 index 00000000..4a20e9c1 --- /dev/null +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/ApiExplorer/IApiVersionDescriptionProviderFactoryExtensions.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. + +namespace Asp.Versioning.ApiExplorer; + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Primitives; + +/// <summary> +/// Provides extension methods for <see cref="IApiVersionDescriptionProviderFactory"/>. +/// </summary> +[CLSCompliant( false )] +public static class IApiVersionDescriptionProviderFactoryExtensions +{ + /// <summary> + /// Creates and returns an API version description provider. + /// </summary> + /// <param name="factory">The extended <see cref="IApiVersionDescriptionProviderFactory"/>.</param> + /// <returns>A new <see cref="IApiVersionDescriptionProvider">API version description provider</see>.</returns> + public static IApiVersionDescriptionProvider Create( this IApiVersionDescriptionProviderFactory factory ) + { + ArgumentNullException.ThrowIfNull( factory ); + return factory.Create( new EmptyEndpointDataSource() ); + } + + private sealed class EmptyEndpointDataSource : EndpointDataSource + { + public override IReadOnlyList<Endpoint> Endpoints { get; } = []; + + public override IChangeToken GetChangeToken() => new CancellationChangeToken( CancellationToken.None ); + + public override IReadOnlyList<Endpoint> GetGroupedEndpoints( RouteGroupContext context ) => Endpoints; + } +} \ No newline at end of file From b71cdc437a02239eec3f6aa4457eb66f873700bc Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Mon, 25 Mar 2024 18:41:45 -0700 Subject: [PATCH 118/131] Refactor DI so that IApiVersionDescriptionProviderFactory creates IApiVersionDescriptionProvider in all paths and can be the single source of replacement --- .../ApiVersionDescriptionProviderFactory.cs | 16 +++---- .../IApiVersioningBuilderExtensions.cs | 45 ++----------------- 2 files changed, 8 insertions(+), 53 deletions(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs index 75b99644..cec78062 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionDescriptionProviderFactory.cs @@ -1,16 +1,13 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. -#pragma warning disable SA1135 // Using directives should be qualified -#pragma warning disable SA1200 // Using directives should be placed correctly - -using Asp.Versioning; -using Asp.Versioning.ApiExplorer; -using Microsoft.Extensions.Options; +#pragma warning disable CA1812 // Avoid uninstantiated internal classes namespace Microsoft.AspNetCore.Builder; +using Asp.Versioning; +using Asp.Versioning.ApiExplorer; using Microsoft.AspNetCore.Routing; -using Activator = Func<IEnumerable<IApiVersionMetadataCollationProvider>, ISunsetPolicyManager, IOptions<ApiExplorerOptions>, IApiVersionDescriptionProvider>; +using Microsoft.Extensions.Options; internal sealed class ApiVersionDescriptionProviderFactory : IApiVersionDescriptionProviderFactory { @@ -18,16 +15,13 @@ internal sealed class ApiVersionDescriptionProviderFactory : IApiVersionDescript private readonly IApiVersionMetadataCollationProvider[] providers; private readonly IEndpointInspector endpointInspector; private readonly IOptions<ApiExplorerOptions> options; - private readonly Activator activator; public ApiVersionDescriptionProviderFactory( - Activator activator, ISunsetPolicyManager sunsetPolicyManager, IEnumerable<IApiVersionMetadataCollationProvider> providers, IEndpointInspector endpointInspector, IOptions<ApiExplorerOptions> options ) { - this.activator = activator; this.sunsetPolicyManager = sunsetPolicyManager; this.providers = providers.ToArray(); this.endpointInspector = endpointInspector; @@ -43,6 +37,6 @@ public IApiVersionDescriptionProvider Create( EndpointDataSource endpointDataSou collators.AddRange( providers ); - return activator( collators, sunsetPolicyManager, options ); + return new DefaultApiVersionDescriptionProvider( collators, sunsetPolicyManager, options ); } } \ No newline at end of file diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs index 29f0fba9..c1a689fe 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/DependencyInjection/IApiVersioningBuilderExtensions.cs @@ -5,13 +5,11 @@ namespace Microsoft.Extensions.DependencyInjection; using Asp.Versioning; using Asp.Versioning.ApiExplorer; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; -using Microsoft.Extensions.Primitives; using static ServiceDescriptor; /// <summary> @@ -54,54 +52,17 @@ private static void AddApiExplorerServices( IApiVersioningBuilder builder ) services.AddMvcCore().AddApiExplorer(); services.TryAddSingleton<IOptionsFactory<ApiExplorerOptions>, ApiExplorerOptionsFactory<ApiExplorerOptions>>(); - services.TryAddTransient( ResolveApiVersionDescriptionProviderFactory ); - services.TryAddSingleton( ResolveApiVersionDescriptionProvider ); + services.TryAddTransient<IApiVersionDescriptionProviderFactory, ApiVersionDescriptionProviderFactory>(); + services.TryAddSingleton( static sp => sp.GetRequiredService<IApiVersionDescriptionProviderFactory>().Create() ); // use internal constructor until ASP.NET Core fixes their bug // BUG: https://github.com/dotnet/aspnetcore/issues/41773 services.TryAddEnumerable( Transient<IApiDescriptionProvider, VersionedApiDescriptionProvider>( - sp => new( + static sp => new( sp.GetRequiredService<ISunsetPolicyManager>(), sp.GetRequiredService<IModelMetadataProvider>(), sp.GetRequiredService<IInlineConstraintResolver>(), sp.GetRequiredService<IOptions<ApiExplorerOptions>>() ) ) ); } - - private static IApiVersionDescriptionProviderFactory ResolveApiVersionDescriptionProviderFactory( IServiceProvider serviceProvider ) - { - var sunsetPolicyManager = serviceProvider.GetRequiredService<ISunsetPolicyManager>(); - var providers = serviceProvider.GetServices<IApiVersionMetadataCollationProvider>(); - var inspector = serviceProvider.GetRequiredService<IEndpointInspector>(); - var options = serviceProvider.GetRequiredService<IOptions<ApiExplorerOptions>>(); - - return new ApiVersionDescriptionProviderFactory( - NewDefaultProvider, - sunsetPolicyManager, - providers, - inspector, - options ); - - static DefaultApiVersionDescriptionProvider NewDefaultProvider( - IEnumerable<IApiVersionMetadataCollationProvider> providers, - ISunsetPolicyManager sunsetPolicyManager, - IOptions<ApiExplorerOptions> apiExplorerOptions ) => - new( providers, sunsetPolicyManager, apiExplorerOptions ); - } - - private static IApiVersionDescriptionProvider ResolveApiVersionDescriptionProvider( IServiceProvider serviceProvider ) - { - var factory = serviceProvider.GetRequiredService<IApiVersionDescriptionProviderFactory>(); - var endpointDataSource = new EmptyEndpointDataSource(); - return factory.Create( endpointDataSource ); - } - - private sealed class EmptyEndpointDataSource : EndpointDataSource - { - public override IReadOnlyList<Endpoint> Endpoints => Array.Empty<Endpoint>(); - - public override IChangeToken GetChangeToken() => new CancellationChangeToken( CancellationToken.None ); - - public override IReadOnlyList<Endpoint> GetGroupedEndpoints( RouteGroupContext context ) => Array.Empty<Endpoint>(); - } } \ No newline at end of file From 4c6091f01451c05323ac55c2f0a4f9c144bf3c9c Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Tue, 26 Mar 2024 09:44:50 -0700 Subject: [PATCH 119/131] Simplify usings --- .../DependencyInjection/IServiceCollectionExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs index 83521614..89d6d51c 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/DependencyInjection/IServiceCollectionExtensions.cs @@ -10,7 +10,6 @@ namespace Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; -using System.Diagnostics.CodeAnalysis; using static ServiceDescriptor; using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; From 22a775b8eae36147587faaa062d7555260c94736 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Tue, 26 Mar 2024 10:21:09 -0700 Subject: [PATCH 120/131] Bump versions and update release notes --- .../Asp.Versioning.Abstractions.csproj | 4 ++-- .../src/Asp.Versioning.Abstractions/ReleaseNotes.txt | 2 +- .../Asp.Versioning.OData.ApiExplorer.csproj | 4 ++-- .../src/Asp.Versioning.OData/Asp.Versioning.OData.csproj | 4 ++-- .../WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj | 4 ++-- .../Asp.Versioning.Mvc.ApiExplorer.csproj | 4 ++-- .../WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj | 4 ++-- .../Asp.Versioning.Http.Client.csproj | 4 ++-- src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj b/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj index 6fb59849..68dd7991 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/Asp.Versioning.Abstractions.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>8.0.1</VersionPrefix> - <AssemblyVersion>8.0.0.0</AssemblyVersion> + <VersionPrefix>8.1.0</VersionPrefix> + <AssemblyVersion>8.1.0.0</AssemblyVersion> <TargetFrameworks>$(DefaultTargetFramework);netstandard1.0;netstandard2.0</TargetFrameworks> <AssemblyTitle>API Versioning Abstractions</AssemblyTitle> <Description>The abstractions library for API versioning.</Description> diff --git a/src/Abstractions/src/Asp.Versioning.Abstractions/ReleaseNotes.txt b/src/Abstractions/src/Asp.Versioning.Abstractions/ReleaseNotes.txt index d6943ffb..5f282702 100644 --- a/src/Abstractions/src/Asp.Versioning.Abstractions/ReleaseNotes.txt +++ b/src/Abstractions/src/Asp.Versioning.Abstractions/ReleaseNotes.txt @@ -1 +1 @@ -TryResolvePolicy should correctly fall back ([#1064](https://github.com/dotnet/aspnet-api-versioning/issues/1064)) \ No newline at end of file + \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj index d132353a..c78ccdc9 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>8.0.0</VersionPrefix> - <AssemblyVersion>8.0.0.0</AssemblyVersion> + <VersionPrefix>8.1.0</VersionPrefix> + <AssemblyVersion>8.1.0.0</AssemblyVersion> <TargetFramework>$(DefaultTargetFramework)</TargetFramework> <RootNamespace>Asp.Versioning</RootNamespace> <AssemblyTitle>ASP.NET Core API Versioning API Explorer for OData v4.0</AssemblyTitle> diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj index ee194d18..cec744b0 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>8.0.0</VersionPrefix> - <AssemblyVersion>8.0.0.0</AssemblyVersion> + <VersionPrefix>8.1.0</VersionPrefix> + <AssemblyVersion>8.1.0.0</AssemblyVersion> <TargetFramework>$(DefaultTargetFramework)</TargetFramework> <RootNamespace>Asp.Versioning</RootNamespace> <AssemblyTitle>ASP.NET Core API Versioning with OData v4.0</AssemblyTitle> diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj index 32e89b8d..3abd2a2c 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>8.0.0</VersionPrefix> - <AssemblyVersion>8.0.0.0</AssemblyVersion> + <VersionPrefix>8.1.0</VersionPrefix> + <AssemblyVersion>8.1.0.0</AssemblyVersion> <TargetFramework>$(DefaultTargetFramework)</TargetFramework> <RootNamespace>Asp.Versioning</RootNamespace> <AssemblyTitle>ASP.NET Core API Versioning</AssemblyTitle> diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj index 35aa3746..528a203a 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/Asp.Versioning.Mvc.ApiExplorer.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>8.0.0</VersionPrefix> - <AssemblyVersion>8.0.0.0</AssemblyVersion> + <VersionPrefix>8.1.0</VersionPrefix> + <AssemblyVersion>8.1.0.0</AssemblyVersion> <TargetFramework>$(DefaultTargetFramework)</TargetFramework> <RootNamespace>Asp.Versioning.ApiExplorer</RootNamespace> <AssemblyTitle>ASP.NET Core API Versioning API Explorer</AssemblyTitle> diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj index 0871cc58..52963338 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/Asp.Versioning.Mvc.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>8.0.0</VersionPrefix> - <AssemblyVersion>8.0.0.0</AssemblyVersion> + <VersionPrefix>8.1.0</VersionPrefix> + <AssemblyVersion>8.1.0.0</AssemblyVersion> <TargetFramework>$(DefaultTargetFramework)</TargetFramework> <RootNamespace>Asp.Versioning</RootNamespace> <AssemblyTitle>ASP.NET Core API Versioning</AssemblyTitle> diff --git a/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj b/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj index e508c993..b704593d 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj +++ b/src/Client/src/Asp.Versioning.Http.Client/Asp.Versioning.Http.Client.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>8.0.1</VersionPrefix> - <AssemblyVersion>8.0.0.0</AssemblyVersion> + <VersionPrefix>8.1.0</VersionPrefix> + <AssemblyVersion>8.1.0.0</AssemblyVersion> <TargetFrameworks>$(DefaultTargetFramework);netstandard1.1;netstandard2.0</TargetFrameworks> <RootNamespace>Asp.Versioning.Http</RootNamespace> <AssemblyTitle>API Versioning Client Extensions</AssemblyTitle> diff --git a/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt b/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt index 0cf60623..5f282702 100644 --- a/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt +++ b/src/Client/src/Asp.Versioning.Http.Client/ReleaseNotes.txt @@ -1 +1 @@ -Parse single header CSVs ([#1070](https://github.com/dotnet/aspnet-api-versioning/issues/1070)) \ No newline at end of file + \ No newline at end of file From 4bd1c2d9a1da065af1de8af1423bf2b8fb845eed Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Sun, 14 Apr 2024 11:45:44 -0700 Subject: [PATCH 121/131] Render Swagger UI links in a fancier, more useful way --- .../OpenApiODataWebApiExample/Startup.cs | 31 +++++++++++++++--- .../SomeOpenApiODataWebApiExample/Startup.cs | 31 +++++++++++++++--- .../WebApi/OpenApiWebApiExample/Startup.cs | 32 ++++++++++++++++--- .../ConfigureSwaggerOptions.cs | 23 ++++++++++--- .../ConfigureSwaggerOptions.cs | 23 ++++++++++--- .../ConfigureSwaggerOptions.cs | 23 ++++++++++--- .../OpenApiExample/ConfigureSwaggerOptions.cs | 23 ++++++++++--- 7 files changed, 155 insertions(+), 31 deletions(-) diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs index e867f18d..e9041933 100644 --- a/examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs +++ b/examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs @@ -5,6 +5,7 @@ using Asp.Versioning.Conventions; using Asp.Versioning.OData; using Microsoft.AspNet.OData.Extensions; +using Microsoft.Extensions.Primitives; using Microsoft.OData; using Newtonsoft.Json.Serialization; using Owin; @@ -131,25 +132,45 @@ public void Configuration( IAppBuilder builder ) { description.AppendLine(); + var rendered = false; + for ( var i = 0; i < policy.Links.Count; i++ ) { var link = policy.Links[i]; if ( link.Type == "text/html" ) { - description.AppendLine(); - - if ( link.Title.HasValue ) + if ( !rendered ) { - description.Append( link.Title.Value ).Append( ": " ); + description.AppendLine(); + description.Append( "**Links**" ); + description.AppendLine(); + rendered = true; } - description.Append( link.LinkTarget.OriginalString ); + if ( StringSegment.IsNullOrEmpty( link.Title ) ) + { + if ( link.LinkTarget.IsAbsoluteUri ) + { + description.AppendLine( $"- {link.LinkTarget.OriginalString}" ); + } + else + { + description.AppendFormat( "- <a href=\"{0}\">{0}</a>", link.LinkTarget.OriginalString ); + description.AppendLine(); + } + } + else + { + description.AppendLine( $"- [{link.Title}]({link.LinkTarget.OriginalString})" ); + } } } } } + description.AppendLine(); + description.AppendLine( "**Additional Information**" ); info.Version( group.Name, $"Sample API {group.ApiVersion}" ) .Contact( c => c.Name( "Bill Mei" ).Email( "bill.mei@somewhere.com" ) ) .Description( description.ToString() ) diff --git a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs index dca602ca..9e223a7e 100644 --- a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs +++ b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs @@ -3,6 +3,7 @@ using Asp.Versioning; using Asp.Versioning.Conventions; using Microsoft.AspNet.OData.Extensions; +using Microsoft.Extensions.Primitives; using Microsoft.OData; using Newtonsoft.Json.Serialization; using Owin; @@ -105,25 +106,45 @@ public void Configuration( IAppBuilder builder ) { description.AppendLine(); + var rendered = false; + for ( var i = 0; i < policy.Links.Count; i++ ) { var link = policy.Links[i]; if ( link.Type == "text/html" ) { - description.AppendLine(); - - if ( link.Title.HasValue ) + if ( !rendered ) { - description.Append( link.Title.Value ).Append( ": " ); + description.AppendLine(); + description.Append( "**Links**" ); + description.AppendLine(); + rendered = true; } - description.Append( link.LinkTarget.OriginalString ); + if ( StringSegment.IsNullOrEmpty( link.Title ) ) + { + if ( link.LinkTarget.IsAbsoluteUri ) + { + description.AppendLine( $"- {link.LinkTarget.OriginalString}" ); + } + else + { + description.AppendFormat( "- <a href=\"{0}\">{0}</a>", link.LinkTarget.OriginalString ); + description.AppendLine(); + } + } + else + { + description.AppendLine( $"- [{link.Title}]({link.LinkTarget.OriginalString})" ); + } } } } } + description.AppendLine(); + description.AppendLine( "**Additional Information**" ); info.Version( group.Name, $"Sample API {group.ApiVersion}" ) .Contact( c => c.Name( "Bill Mei" ).Email( "bill.mei@somewhere.com" ) ) .Description( description.ToString() ) diff --git a/examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs b/examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs index b3651855..848f77f5 100644 --- a/examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs +++ b/examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs @@ -2,6 +2,7 @@ using Asp.Versioning; using Asp.Versioning.Routing; +using Microsoft.Extensions.Primitives; using Owin; using Swashbuckle.Application; using System.IO; @@ -10,6 +11,7 @@ using System.Web.Http; using System.Web.Http.Description; using System.Web.Http.Routing; +using static System.Net.Mime.MediaTypeNames; /// <summary> /// Represents the startup process for the application. @@ -88,25 +90,45 @@ public void Configuration( IAppBuilder builder ) { description.AppendLine(); + var rendered = false; + for ( var i = 0; i < policy.Links.Count; i++ ) { var link = policy.Links[i]; if ( link.Type == "text/html" ) { - description.AppendLine(); - - if ( link.Title.HasValue ) + if ( !rendered ) { - description.Append( link.Title.Value ).Append( ": " ); + description.AppendLine(); + description.Append( "**Links**" ); + description.AppendLine(); + rendered = true; } - description.Append( link.LinkTarget.OriginalString ); + if ( StringSegment.IsNullOrEmpty( link.Title ) ) + { + if ( link.LinkTarget.IsAbsoluteUri ) + { + description.AppendLine( $"- {link.LinkTarget.OriginalString}" ); + } + else + { + description.AppendFormat( "- <a href=\"{0}\">{0}</a>", link.LinkTarget.OriginalString ); + description.AppendLine(); + } + } + else + { + description.AppendLine( $"- [{link.Title}]({link.LinkTarget.OriginalString})" ); + } } } } } + description.AppendLine(); + description.AppendLine( "**Additional Information**" ); info.Version( group.Name, $"Example API {group.ApiVersion}" ) .Contact( c => c.Name( "Bill Mei" ).Email( "bill.mei@somewhere.com" ) ) .Description( description.ToString() ) diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs index d0575a5e..f81ed540 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs @@ -4,6 +4,7 @@ using Asp.Versioning.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using System.Text; @@ -63,25 +64,39 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri { text.AppendLine(); + var rendered = false; + for ( var i = 0; i < policy.Links.Count; i++ ) { var link = policy.Links[i]; if ( link.Type == "text/html" ) { - text.AppendLine(); - - if ( link.Title.HasValue ) + if ( !rendered ) { - text.Append( link.Title.Value ).Append( ": " ); + text.Append( "<h4>Links</h4><ul>" ); + rendered = true; } + text.Append( "<li><a href=\"" ); text.Append( link.LinkTarget.OriginalString ); + text.Append( "\">" ); + text.Append( + StringSegment.IsNullOrEmpty( link.Title ) + ? link.LinkTarget.OriginalString + : link.Title.ToString() ); + text.Append( "</a></li>" ); } } + + if ( rendered ) + { + text.Append( "</ul>" ); + } } } + text.Append( "<h4>Additional Information</h4>" ); info.Description = text.ToString(); return info; diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs index 987145a1..4cad607a 100644 --- a/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs +++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs @@ -4,6 +4,7 @@ using Asp.Versioning.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using System.Text; @@ -63,25 +64,39 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri { text.AppendLine(); + var rendered = false; + for ( var i = 0; i < policy.Links.Count; i++ ) { var link = policy.Links[i]; if ( link.Type == "text/html" ) { - text.AppendLine(); - - if ( link.Title.HasValue ) + if ( !rendered ) { - text.Append( link.Title.Value ).Append( ": " ); + text.Append( "<h4>Links</h4><ul>" ); + rendered = true; } + text.Append( "<li><a href=\"" ); text.Append( link.LinkTarget.OriginalString ); + text.Append( "\">" ); + text.Append( + StringSegment.IsNullOrEmpty( link.Title ) + ? link.LinkTarget.OriginalString + : link.Title.ToString() ); + text.Append( "</a></li>" ); } } + + if ( rendered ) + { + text.Append( "</ul>" ); + } } } + text.Append( "<h4>Additional Information</h4>" ); info.Description = text.ToString(); return info; diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs index bee2b1f6..4673321d 100644 --- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs +++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs @@ -4,6 +4,7 @@ using Asp.Versioning.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using System.Text; @@ -63,25 +64,39 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri { text.AppendLine(); + var rendered = false; + for ( var i = 0; i < policy.Links.Count; i++ ) { var link = policy.Links[i]; if ( link.Type == "text/html" ) { - text.AppendLine(); - - if ( link.Title.HasValue ) + if ( !rendered ) { - text.Append( link.Title.Value ).Append( ": " ); + text.Append( "<h4>Links</h4><ul>" ); + rendered = true; } + text.Append( "<li><a href=\"" ); text.Append( link.LinkTarget.OriginalString ); + text.Append( "\">" ); + text.Append( + StringSegment.IsNullOrEmpty( link.Title ) + ? link.LinkTarget.OriginalString + : link.Title.ToString() ); + text.Append( "</a></li>" ); } } + + if ( rendered ) + { + text.Append( "</ul>" ); + } } } + text.Append( "<h4>Additional Information</h4>" ); info.Description = text.ToString(); return info; diff --git a/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs index bee2b1f6..4673321d 100644 --- a/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs +++ b/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs @@ -4,6 +4,7 @@ using Asp.Versioning.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using System.Text; @@ -63,25 +64,39 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri { text.AppendLine(); + var rendered = false; + for ( var i = 0; i < policy.Links.Count; i++ ) { var link = policy.Links[i]; if ( link.Type == "text/html" ) { - text.AppendLine(); - - if ( link.Title.HasValue ) + if ( !rendered ) { - text.Append( link.Title.Value ).Append( ": " ); + text.Append( "<h4>Links</h4><ul>" ); + rendered = true; } + text.Append( "<li><a href=\"" ); text.Append( link.LinkTarget.OriginalString ); + text.Append( "\">" ); + text.Append( + StringSegment.IsNullOrEmpty( link.Title ) + ? link.LinkTarget.OriginalString + : link.Title.ToString() ); + text.Append( "</a></li>" ); } } + + if ( rendered ) + { + text.Append( "</ul>" ); + } } } + text.Append( "<h4>Additional Information</h4>" ); info.Description = text.ToString(); return info; From 8eec498d29069640322b31352bc7c7f61e3d06b3 Mon Sep 17 00:00:00 2001 From: Chris Martinez <sydefekt@hotmail.com> Date: Sun, 14 Apr 2024 11:57:14 -0700 Subject: [PATCH 122/131] Use more succinct pattern matching --- examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs | 4 ++-- .../AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs | 5 ++--- examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs | 5 ++--- .../OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs | 5 ++--- .../OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs | 5 ++--- .../WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs | 5 ++--- .../WebApi/OpenApiExample/ConfigureSwaggerOptions.cs | 5 ++--- 7 files changed, 14 insertions(+), 20 deletions(-) diff --git a/examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs b/examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs index e9041933..2577d01c 100644 --- a/examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs +++ b/examples/AspNet/OData/OpenApiODataWebApiExample/Startup.cs @@ -119,9 +119,9 @@ public void Configuration( IAppBuilder builder ) description.Append( " This API version has been deprecated." ); } - if ( group.SunsetPolicy is SunsetPolicy policy ) + if ( group.SunsetPolicy is { } policy ) { - if ( policy.Date is DateTimeOffset when ) + if ( policy.Date is { } when ) { description.Append( " The API will be sunset on " ) .Append( when.Date.ToShortDateString() ) diff --git a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs index 9e223a7e..1ac7209a 100644 --- a/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs +++ b/examples/AspNet/OData/SomeOpenApiODataWebApiExample/Startup.cs @@ -1,6 +1,5 @@ namespace ApiVersioning.Examples; -using Asp.Versioning; using Asp.Versioning.Conventions; using Microsoft.AspNet.OData.Extensions; using Microsoft.Extensions.Primitives; @@ -93,9 +92,9 @@ public void Configuration( IAppBuilder builder ) description.Append( " This API version has been deprecated." ); } - if ( group.SunsetPolicy is SunsetPolicy policy ) + if ( group.SunsetPolicy is { } policy ) { - if ( policy.Date is DateTimeOffset when ) + if ( policy.Date is { } when ) { description.Append( " The API will be sunset on " ) .Append( when.Date.ToShortDateString() ) diff --git a/examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs b/examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs index 848f77f5..2a060f11 100644 --- a/examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs +++ b/examples/AspNet/WebApi/OpenApiWebApiExample/Startup.cs @@ -11,7 +11,6 @@ using System.Web.Http; using System.Web.Http.Description; using System.Web.Http.Routing; -using static System.Net.Mime.MediaTypeNames; /// <summary> /// Represents the startup process for the application. @@ -77,9 +76,9 @@ public void Configuration( IAppBuilder builder ) description.Append( " This API version has been deprecated." ); } - if ( group.SunsetPolicy is SunsetPolicy policy ) + if ( group.SunsetPolicy is { } policy ) { - if ( policy.Date is DateTimeOffset when ) + if ( policy.Date is { } when ) { description.Append( " The API will be sunset on " ) .Append( when.Date.ToShortDateString() ) diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs index f81ed540..5384c160 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/ConfigureSwaggerOptions.cs @@ -1,6 +1,5 @@ namespace ApiVersioning.Examples; -using Asp.Versioning; using Asp.Versioning.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -51,9 +50,9 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri text.Append( " This API version has been deprecated." ); } - if ( description.SunsetPolicy is SunsetPolicy policy ) + if ( description.SunsetPolicy is { } policy ) { - if ( policy.Date is DateTimeOffset when ) + if ( policy.Date is { } when ) { text.Append( " The API will be sunset on " ) .Append( when.Date.ToShortDateString() ) diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs index 4cad607a..b725ab72 100644 --- a/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs +++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/ConfigureSwaggerOptions.cs @@ -1,6 +1,5 @@ namespace ApiVersioning.Examples; -using Asp.Versioning; using Asp.Versioning.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -51,9 +50,9 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri text.Append( " This API version has been deprecated." ); } - if ( description.SunsetPolicy is SunsetPolicy policy ) + if ( description.SunsetPolicy is { } policy ) { - if ( policy.Date is DateTimeOffset when ) + if ( policy.Date is { } when ) { text.Append( " The API will be sunset on " ) .Append( when.Date.ToShortDateString() ) diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs index 4673321d..d531cea4 100644 --- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs +++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/ConfigureSwaggerOptions.cs @@ -1,6 +1,5 @@ namespace ApiVersioning.Examples; -using Asp.Versioning; using Asp.Versioning.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -51,9 +50,9 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri text.Append( " This API version has been deprecated." ); } - if ( description.SunsetPolicy is SunsetPolicy policy ) + if ( description.SunsetPolicy is { } policy ) { - if ( policy.Date is DateTimeOffset when ) + if ( policy.Date is { } when ) { text.Append( " The API will be sunset on " ) .Append( when.Date.ToShortDateString() ) diff --git a/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs b/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs index 4673321d..d531cea4 100644 --- a/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs +++ b/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs @@ -1,6 +1,5 @@ namespace ApiVersioning.Examples; -using Asp.Versioning; using Asp.Versioning.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -51,9 +50,9 @@ private static OpenApiInfo CreateInfoForApiVersion( ApiVersionDescription descri text.Append( " This API version has been deprecated." ); } - if ( description.SunsetPolicy is SunsetPolicy policy ) + if ( description.SunsetPolicy is { } policy ) { - if ( policy.Date is DateTimeOffset when ) + if ( policy.Date is { } when ) { text.Append( " The API will be sunset on " ) .Append( when.Date.ToShortDateString() ) From 3468695ffb41e57709350764a2e5063a426b5f12 Mon Sep 17 00:00:00 2001 From: Rick Anderson <3605364+Rick-Anderson@users.noreply.github.com> Date: Wed, 17 Apr 2024 13:59:12 -1000 Subject: [PATCH 123/131] use IsDevelopment --- .../WebApi/MinimalOpenApiExample/Program.cs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs b/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs index acf998ed..7ca38f15 100644 --- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs +++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs @@ -264,18 +264,20 @@ .Produces( 400 ); app.UseSwagger(); -app.UseSwaggerUI( - options => - { - var descriptions = app.DescribeApiVersions(); - - // build a swagger endpoint for each discovered API version - foreach ( var description in descriptions ) +if ( builder.Environment.IsDevelopment() ) +{ + app.UseSwaggerUI( + options => { - var url = $"/swagger/{description.GroupName}/swagger.json"; - var name = description.GroupName.ToUpperInvariant(); - options.SwaggerEndpoint( url, name ); - } - } ); + var descriptions = app.DescribeApiVersions(); + // build a swagger endpoint for each discovered API version + foreach ( var description in descriptions ) + { + var url = $"/swagger/{description.GroupName}/swagger.json"; + var name = description.GroupName.ToUpperInvariant(); + options.SwaggerEndpoint( url, name ); + } + } ); +} app.Run(); \ No newline at end of file From 84b1b8083287b9fc448ffe02a55bca50d7d0e7c5 Mon Sep 17 00:00:00 2001 From: Rick Anderson <3605364+Rick-Anderson@users.noreply.github.com> Date: Wed, 17 Apr 2024 14:19:01 -1000 Subject: [PATCH 124/131] use IsDevelopment --- .../AspNetCore/OData/SomeODataOpenApiExample/Program.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs index 7dc6c5c0..eefc3dca 100644 --- a/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs +++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs @@ -64,7 +64,9 @@ // Configure the HTTP request pipeline. app.UseSwagger(); -app.UseSwaggerUI( +if ( builder.Environment.IsDevelopment() ) +{ + app.UseSwaggerUI( options => { var descriptions = app.DescribeApiVersions(); @@ -77,7 +79,7 @@ options.SwaggerEndpoint( url, name ); } } ); - +} app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); From 06c47d5599f925e92cd3263741d9e918dd9d8a34 Mon Sep 17 00:00:00 2001 From: Rick Anderson <3605364+Rick-Anderson@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:07:34 -1000 Subject: [PATCH 125/131] use IsDevelopment --- examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs | 2 +- examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs index eefc3dca..953e3bf5 100644 --- a/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs +++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs @@ -64,7 +64,7 @@ // Configure the HTTP request pipeline. app.UseSwagger(); -if ( builder.Environment.IsDevelopment() ) +if ( app.Environment.IsDevelopment() ) { app.UseSwaggerUI( options => diff --git a/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs b/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs index 7ca38f15..909d8261 100644 --- a/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs +++ b/examples/AspNetCore/WebApi/MinimalOpenApiExample/Program.cs @@ -264,7 +264,7 @@ .Produces( 400 ); app.UseSwagger(); -if ( builder.Environment.IsDevelopment() ) +if ( app.Environment.IsDevelopment() ) { app.UseSwaggerUI( options => From bb62b0bd721022ed097ce096211c8f27d7b006db Mon Sep 17 00:00:00 2001 From: Rick Anderson <3605364+Rick-Anderson@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:41:57 -1000 Subject: [PATCH 126/131] use IsDevelopment --- examples/AspNetCore/OData/ODataOpenApiExample/Program.cs | 3 +++ examples/AspNetCore/WebApi/OpenApiExample/Program.cs | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs index 1fd344e5..5afe9e5a 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs @@ -89,6 +89,8 @@ } app.UseSwagger(); +if ( app.Environment.IsDevelopment() ) +{ app.UseSwaggerUI( options => { @@ -102,6 +104,7 @@ options.SwaggerEndpoint( url, name ); } } ); +} app.UseHttpsRedirection(); app.UseAuthorization(); diff --git a/examples/AspNetCore/WebApi/OpenApiExample/Program.cs b/examples/AspNetCore/WebApi/OpenApiExample/Program.cs index 64ad0ad1..8e1bc9a8 100644 --- a/examples/AspNetCore/WebApi/OpenApiExample/Program.cs +++ b/examples/AspNetCore/WebApi/OpenApiExample/Program.cs @@ -57,7 +57,9 @@ // Configure the HTTP request pipeline. app.UseSwagger(); -app.UseSwaggerUI( +if ( app.Environment.IsDevelopment() ) +{ + app.UseSwaggerUI( options => { var descriptions = app.DescribeApiVersions(); @@ -70,6 +72,7 @@ options.SwaggerEndpoint( url, name ); } } ); +} app.UseHttpsRedirection(); app.UseAuthorization(); From 99e527f595e1267a8a27d47f4b47171c47c698cc Mon Sep 17 00:00:00 2001 From: Rick Anderson <3605364+Rick-Anderson@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:52:13 -1000 Subject: [PATCH 127/131] use IsDevelopment --- examples/AspNetCore/OData/ODataOpenApiExample/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs b/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs index 5afe9e5a..4dd5e683 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs +++ b/examples/AspNetCore/OData/ODataOpenApiExample/Program.cs @@ -80,11 +80,11 @@ var app = builder.Build(); -// Configure the HTTP request pipeline. +// Configure HTTP request pipeline. if ( app.Environment.IsDevelopment() ) { - // navigate to ~/$odata to determine whether any endpoints did not match an odata route template + // Access ~/$odata to identify OData endpoints that failed to match a route template. app.UseODataRouteDebug(); } From 3fc071913dcded23eeb5ebe55bca44f3828488bf Mon Sep 17 00:00:00 2001 From: Rick Anderson <3605364+Rick-Anderson@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:40:36 -1000 Subject: [PATCH 128/131] use IsDevelopment --- .../OData/SomeODataOpenApiExample/Program.cs | 22 ++++++++--------- .../WebApi/OpenApiExample/Program.cs | 24 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs b/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs index 953e3bf5..55e70b17 100644 --- a/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs +++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/Program.cs @@ -68,17 +68,17 @@ { app.UseSwaggerUI( options => - { - var descriptions = app.DescribeApiVersions(); - - // build a swagger endpoint for each discovered API version - foreach ( var description in descriptions ) - { - var url = $"/swagger/{description.GroupName}/swagger.json"; - var name = description.GroupName.ToUpperInvariant(); - options.SwaggerEndpoint( url, name ); - } - } ); + { + var descriptions = app.DescribeApiVersions(); + + // build a swagger endpoint for each discovered API version + foreach ( var description in descriptions ) + { + var url = $"/swagger/{description.GroupName}/swagger.json"; + var name = description.GroupName.ToUpperInvariant(); + options.SwaggerEndpoint( url, name ); + } + } ); } app.UseHttpsRedirection(); app.UseAuthorization(); diff --git a/examples/AspNetCore/WebApi/OpenApiExample/Program.cs b/examples/AspNetCore/WebApi/OpenApiExample/Program.cs index 8e1bc9a8..454c60d7 100644 --- a/examples/AspNetCore/WebApi/OpenApiExample/Program.cs +++ b/examples/AspNetCore/WebApi/OpenApiExample/Program.cs @@ -60,18 +60,18 @@ if ( app.Environment.IsDevelopment() ) { app.UseSwaggerUI( - options => - { - var descriptions = app.DescribeApiVersions(); - - // build a swagger endpoint for each discovered API version - foreach ( var description in descriptions ) - { - var url = $"/swagger/{description.GroupName}/swagger.json"; - var name = description.GroupName.ToUpperInvariant(); - options.SwaggerEndpoint( url, name ); - } - } ); + options => + { + var descriptions = app.DescribeApiVersions(); + + // build a swagger endpoint for each discovered API version + foreach ( var description in descriptions ) + { + var url = $"/swagger/{description.GroupName}/swagger.json"; + var name = description.GroupName.ToUpperInvariant(); + options.SwaggerEndpoint( url, name ); + } + } ); } app.UseHttpsRedirection(); From 470dafdec490f23da486a721bfe583346aa91afd Mon Sep 17 00:00:00 2001 From: Xavier <xavierjohn@hotmail.com> Date: Fri, 11 Apr 2025 10:41:20 -0700 Subject: [PATCH 129/131] Update Microsoft.AspNetCore.OData package reference to support v 9.2.1 --- .../OData/ODataAdvancedExample/ODataAdvancedExample.csproj | 4 ++++ .../OData/ODataBasicExample/ODataBasicExample.csproj | 4 ++++ .../ODataConventionsExample/ODataConventionsExample.csproj | 4 ++++ .../OData/ODataOpenApiExample/ODataOpenApiExample.csproj | 4 ++++ .../SomeODataOpenApiExample/SomeODataOpenApiExample.csproj | 4 ++++ .../Conventions/ODataAttributeVisitor.cs | 2 +- .../Asp.Versioning.OData.ApiExplorer.csproj | 4 ++-- .../src/Asp.Versioning.OData/Asp.Versioning.OData.csproj | 6 +++--- .../OData/ODataApplicationModelProvider.cs | 2 +- .../ApiExplorer/ODataApiDescriptionProviderTest.cs | 4 ++-- .../ApplicationModels/DefaultApiControllerFilter.cs | 4 ++-- .../Conventions/ODataAttributeVisitor.cs | 4 ++-- 12 files changed, 33 insertions(+), 13 deletions(-) diff --git a/examples/AspNetCore/OData/ODataAdvancedExample/ODataAdvancedExample.csproj b/examples/AspNetCore/OData/ODataAdvancedExample/ODataAdvancedExample.csproj index ecccb3a9..f990c588 100644 --- a/examples/AspNetCore/OData/ODataAdvancedExample/ODataAdvancedExample.csproj +++ b/examples/AspNetCore/OData/ODataAdvancedExample/ODataAdvancedExample.csproj @@ -4,4 +4,8 @@ <ProjectReference Include="..\..\..\..\src\AspNetCore\OData\src\Asp.Versioning.OData\Asp.Versioning.OData.csproj" /> </ItemGroup> + <ItemGroup> + <PackageReference Update="Microsoft.AspNetCore.OData" Version="9.2.1" /> + </ItemGroup> + </Project> \ No newline at end of file diff --git a/examples/AspNetCore/OData/ODataBasicExample/ODataBasicExample.csproj b/examples/AspNetCore/OData/ODataBasicExample/ODataBasicExample.csproj index 6bdc8cef..389b3dc6 100644 --- a/examples/AspNetCore/OData/ODataBasicExample/ODataBasicExample.csproj +++ b/examples/AspNetCore/OData/ODataBasicExample/ODataBasicExample.csproj @@ -4,4 +4,8 @@ <ProjectReference Include="..\..\..\..\src\AspNetCore\OData\src\Asp.Versioning.OData\Asp.Versioning.OData.csproj" /> </ItemGroup> + <ItemGroup> + <PackageReference Update="Microsoft.AspNetCore.OData" Version="9.2.1" /> + </ItemGroup> + </Project> \ No newline at end of file diff --git a/examples/AspNetCore/OData/ODataConventionsExample/ODataConventionsExample.csproj b/examples/AspNetCore/OData/ODataConventionsExample/ODataConventionsExample.csproj index ecccb3a9..f990c588 100644 --- a/examples/AspNetCore/OData/ODataConventionsExample/ODataConventionsExample.csproj +++ b/examples/AspNetCore/OData/ODataConventionsExample/ODataConventionsExample.csproj @@ -4,4 +4,8 @@ <ProjectReference Include="..\..\..\..\src\AspNetCore\OData\src\Asp.Versioning.OData\Asp.Versioning.OData.csproj" /> </ItemGroup> + <ItemGroup> + <PackageReference Update="Microsoft.AspNetCore.OData" Version="9.2.1" /> + </ItemGroup> + </Project> \ No newline at end of file diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/ODataOpenApiExample.csproj b/examples/AspNetCore/OData/ODataOpenApiExample/ODataOpenApiExample.csproj index 3bffcf5e..af705328 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/ODataOpenApiExample.csproj +++ b/examples/AspNetCore/OData/ODataOpenApiExample/ODataOpenApiExample.csproj @@ -12,4 +12,8 @@ <ProjectReference Include="..\..\..\..\src\AspNetCore\OData\src\Asp.Versioning.OData.ApiExplorer\Asp.Versioning.OData.ApiExplorer.csproj" /> </ItemGroup> + <ItemGroup> + <PackageReference Update="Microsoft.AspNetCore.OData" Version="9.2.1" /> + </ItemGroup> + </Project> \ No newline at end of file diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/SomeODataOpenApiExample.csproj b/examples/AspNetCore/OData/SomeODataOpenApiExample/SomeODataOpenApiExample.csproj index 3bffcf5e..af705328 100644 --- a/examples/AspNetCore/OData/SomeODataOpenApiExample/SomeODataOpenApiExample.csproj +++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/SomeODataOpenApiExample.csproj @@ -12,4 +12,8 @@ <ProjectReference Include="..\..\..\..\src\AspNetCore\OData\src\Asp.Versioning.OData.ApiExplorer\Asp.Versioning.OData.ApiExplorer.csproj" /> </ItemGroup> + <ItemGroup> + <PackageReference Update="Microsoft.AspNetCore.OData" Version="9.2.1" /> + </ItemGroup> + </Project> \ No newline at end of file diff --git a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs index 1c1a2f49..410f48ff 100644 --- a/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs +++ b/src/AspNet/OData/src/Asp.Versioning.WebApi.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs @@ -13,6 +13,6 @@ private void VisitAction( HttpActionDescriptor action ) var attributes = new List<EnableQueryAttribute>( controller.GetCustomAttributes<EnableQueryAttribute>( inherit: true ) ); attributes.AddRange( action.GetCustomAttributes<EnableQueryAttribute>( inherit: true ) ); - VisitEnableQuery( attributes ); + VisitEnableQuery( attributes.ToArray() ); } } \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj index c78ccdc9..a443fa16 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/Asp.Versioning.OData.ApiExplorer.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>8.1.0</VersionPrefix> - <AssemblyVersion>8.1.0.0</AssemblyVersion> + <VersionPrefix>8.2.0</VersionPrefix> + <AssemblyVersion>8.2.0.0</AssemblyVersion> <TargetFramework>$(DefaultTargetFramework)</TargetFramework> <RootNamespace>Asp.Versioning</RootNamespace> <AssemblyTitle>ASP.NET Core API Versioning API Explorer for OData v4.0</AssemblyTitle> diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj index cec744b0..1e87e465 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <VersionPrefix>8.1.0</VersionPrefix> - <AssemblyVersion>8.1.0.0</AssemblyVersion> + <VersionPrefix>8.2.0</VersionPrefix> + <AssemblyVersion>8.2.0.0</AssemblyVersion> <TargetFramework>$(DefaultTargetFramework)</TargetFramework> <RootNamespace>Asp.Versioning</RootNamespace> <AssemblyTitle>ASP.NET Core API Versioning with OData v4.0</AssemblyTitle> @@ -15,7 +15,7 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.OData" Version="[8.0.2,9.0.0)" /> + <PackageReference Include="Microsoft.AspNetCore.OData" Version="[8.0.2,9.3.0)" /> </ItemGroup> <ItemGroup> diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs index 402da34d..9ef71b7b 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/OData/ODataApplicationModelProvider.cs @@ -141,7 +141,7 @@ private static return (metadataControllers, supported, deprecated); } - private static ControllerModel? SelectBestMetadataController( IReadOnlyList<ControllerModel> controllers ) + private static ControllerModel? SelectBestMetadataController( List<ControllerModel> controllers ) { // note: there should be at least 2 metadata controllers, but there could be 3+ // if a developer defines their own custom controller. ultimately, there can be diff --git a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/ApiExplorer/ODataApiDescriptionProviderTest.cs b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/ApiExplorer/ODataApiDescriptionProviderTest.cs index 511513b4..532abb00 100644 --- a/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/ApiExplorer/ODataApiDescriptionProviderTest.cs +++ b/src/AspNetCore/OData/test/Asp.Versioning.OData.ApiExplorer.Tests/ApiExplorer/ODataApiDescriptionProviderTest.cs @@ -256,9 +256,9 @@ private static void AssertQueryOptionWithoutOData( ApiDescription description, s parameter.ModelMetadata.Description.Should().EndWith( suffix + '.' ); } - private void PrintGroup( IReadOnlyList<ApiDescription> items ) + private void PrintGroup( ApiDescription[] items ) { - for ( var i = 0; i < items.Count; i++ ) + for ( var i = 0; i < items.Length; i++ ) { var item = items[i]; console.WriteLine( $"[{item.GroupName}] {item.HttpMethod} {item.RelativePath}" ); diff --git a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplicationModels/DefaultApiControllerFilter.cs b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplicationModels/DefaultApiControllerFilter.cs index e8d851be..a8a1d46e 100644 --- a/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplicationModels/DefaultApiControllerFilter.cs +++ b/src/AspNetCore/WebApi/src/Asp.Versioning.Mvc/ApplicationModels/DefaultApiControllerFilter.cs @@ -10,7 +10,7 @@ namespace Asp.Versioning.ApplicationModels; [CLSCompliant( false )] public sealed class DefaultApiControllerFilter : IApiControllerFilter { - private readonly IReadOnlyList<IApiControllerSpecification> specifications; + private readonly List<IApiControllerSpecification> specifications; /// <summary> /// Initializes a new instance of the <see cref="DefaultApiControllerFilter"/> class. @@ -19,7 +19,7 @@ public sealed class DefaultApiControllerFilter : IApiControllerFilter /// <see cref="IApiControllerSpecification">specifications</see> used by the filter /// to identify API controllers.</param> public DefaultApiControllerFilter( IEnumerable<IApiControllerSpecification> specifications ) => - this.specifications = specifications.ToArray(); + this.specifications = specifications.ToList(); /// <inheritdoc /> public IList<ControllerModel> Apply( IList<ControllerModel> controllers ) diff --git a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs index 7cb50224..fd973fe6 100644 --- a/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs +++ b/src/Common/src/Common.OData.ApiExplorer/Conventions/ODataAttributeVisitor.cs @@ -76,11 +76,11 @@ private void VisitModel( IEdmStructuredType modelType ) VisitMaxTop( querySettings ); } - private void VisitEnableQuery( IReadOnlyList<EnableQueryAttribute> attributes ) + private void VisitEnableQuery( EnableQueryAttribute[] attributes ) { var @default = new EnableQueryAttribute(); - for ( var i = 0; i < attributes.Count; i++ ) + for ( var i = 0; i < attributes.Length; i++ ) { var attribute = attributes[i]; From a1990bda30cb5767696bb4a8e799290dd6676aaa Mon Sep 17 00:00:00 2001 From: Xavier <xavierjohn@hotmail.com> Date: Fri, 11 Apr 2025 10:44:48 -0700 Subject: [PATCH 130/131] Remove direct reference to Odata 9.2.1 --- .../OData/ODataAdvancedExample/ODataAdvancedExample.csproj | 4 ---- .../OData/ODataBasicExample/ODataBasicExample.csproj | 4 ---- .../ODataConventionsExample/ODataConventionsExample.csproj | 4 ---- .../OData/ODataOpenApiExample/ODataOpenApiExample.csproj | 4 ---- .../SomeODataOpenApiExample/SomeODataOpenApiExample.csproj | 4 ---- 5 files changed, 20 deletions(-) diff --git a/examples/AspNetCore/OData/ODataAdvancedExample/ODataAdvancedExample.csproj b/examples/AspNetCore/OData/ODataAdvancedExample/ODataAdvancedExample.csproj index f990c588..ecccb3a9 100644 --- a/examples/AspNetCore/OData/ODataAdvancedExample/ODataAdvancedExample.csproj +++ b/examples/AspNetCore/OData/ODataAdvancedExample/ODataAdvancedExample.csproj @@ -4,8 +4,4 @@ <ProjectReference Include="..\..\..\..\src\AspNetCore\OData\src\Asp.Versioning.OData\Asp.Versioning.OData.csproj" /> </ItemGroup> - <ItemGroup> - <PackageReference Update="Microsoft.AspNetCore.OData" Version="9.2.1" /> - </ItemGroup> - </Project> \ No newline at end of file diff --git a/examples/AspNetCore/OData/ODataBasicExample/ODataBasicExample.csproj b/examples/AspNetCore/OData/ODataBasicExample/ODataBasicExample.csproj index 389b3dc6..6bdc8cef 100644 --- a/examples/AspNetCore/OData/ODataBasicExample/ODataBasicExample.csproj +++ b/examples/AspNetCore/OData/ODataBasicExample/ODataBasicExample.csproj @@ -4,8 +4,4 @@ <ProjectReference Include="..\..\..\..\src\AspNetCore\OData\src\Asp.Versioning.OData\Asp.Versioning.OData.csproj" /> </ItemGroup> - <ItemGroup> - <PackageReference Update="Microsoft.AspNetCore.OData" Version="9.2.1" /> - </ItemGroup> - </Project> \ No newline at end of file diff --git a/examples/AspNetCore/OData/ODataConventionsExample/ODataConventionsExample.csproj b/examples/AspNetCore/OData/ODataConventionsExample/ODataConventionsExample.csproj index f990c588..ecccb3a9 100644 --- a/examples/AspNetCore/OData/ODataConventionsExample/ODataConventionsExample.csproj +++ b/examples/AspNetCore/OData/ODataConventionsExample/ODataConventionsExample.csproj @@ -4,8 +4,4 @@ <ProjectReference Include="..\..\..\..\src\AspNetCore\OData\src\Asp.Versioning.OData\Asp.Versioning.OData.csproj" /> </ItemGroup> - <ItemGroup> - <PackageReference Update="Microsoft.AspNetCore.OData" Version="9.2.1" /> - </ItemGroup> - </Project> \ No newline at end of file diff --git a/examples/AspNetCore/OData/ODataOpenApiExample/ODataOpenApiExample.csproj b/examples/AspNetCore/OData/ODataOpenApiExample/ODataOpenApiExample.csproj index af705328..3bffcf5e 100644 --- a/examples/AspNetCore/OData/ODataOpenApiExample/ODataOpenApiExample.csproj +++ b/examples/AspNetCore/OData/ODataOpenApiExample/ODataOpenApiExample.csproj @@ -12,8 +12,4 @@ <ProjectReference Include="..\..\..\..\src\AspNetCore\OData\src\Asp.Versioning.OData.ApiExplorer\Asp.Versioning.OData.ApiExplorer.csproj" /> </ItemGroup> - <ItemGroup> - <PackageReference Update="Microsoft.AspNetCore.OData" Version="9.2.1" /> - </ItemGroup> - </Project> \ No newline at end of file diff --git a/examples/AspNetCore/OData/SomeODataOpenApiExample/SomeODataOpenApiExample.csproj b/examples/AspNetCore/OData/SomeODataOpenApiExample/SomeODataOpenApiExample.csproj index af705328..3bffcf5e 100644 --- a/examples/AspNetCore/OData/SomeODataOpenApiExample/SomeODataOpenApiExample.csproj +++ b/examples/AspNetCore/OData/SomeODataOpenApiExample/SomeODataOpenApiExample.csproj @@ -12,8 +12,4 @@ <ProjectReference Include="..\..\..\..\src\AspNetCore\OData\src\Asp.Versioning.OData.ApiExplorer\Asp.Versioning.OData.ApiExplorer.csproj" /> </ItemGroup> - <ItemGroup> - <PackageReference Update="Microsoft.AspNetCore.OData" Version="9.2.1" /> - </ItemGroup> - </Project> \ No newline at end of file From 39cfc87fd96972a8db19170d004ec89211e7161d Mon Sep 17 00:00:00 2001 From: Xavier <xavierjohn@hotmail.com> Date: Fri, 11 Apr 2025 13:29:45 -0700 Subject: [PATCH 131/131] Update the readme text. --- examples/AspNetCore/OData/Directory.Build.props | 2 +- .../OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt | 2 +- .../OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj | 2 +- src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/AspNetCore/OData/Directory.Build.props b/examples/AspNetCore/OData/Directory.Build.props index 302bad37..0c6cf639 100644 --- a/examples/AspNetCore/OData/Directory.Build.props +++ b/examples/AspNetCore/OData/Directory.Build.props @@ -4,7 +4,7 @@ <Import Project="$([MSBuild]::GetPathOfFileAbove('$(MSBuildThisFile)','$(MSBuildThisFileDirectory)../'))" /> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.OData" Version="8.2.3" /> + <PackageReference Include="Microsoft.AspNetCore.OData" Version="9.2.1" /> </ItemGroup> </Project> \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt index 5f282702..b5318606 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData.ApiExplorer/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +Support OData 9.0 ([#1103](https://github.com/dotnet/aspnet-api-versioning/issues/1103)) \ No newline at end of file diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj index 1e87e465..e69c80d7 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/Asp.Versioning.OData.csproj @@ -15,7 +15,7 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.OData" Version="[8.0.2,9.3.0)" /> + <PackageReference Include="Microsoft.AspNetCore.OData" Version="[8.0.2,10.0.0)" /> </ItemGroup> <ItemGroup> diff --git a/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt b/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt index 5f282702..b5318606 100644 --- a/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt +++ b/src/AspNetCore/OData/src/Asp.Versioning.OData/ReleaseNotes.txt @@ -1 +1 @@ - \ No newline at end of file +Support OData 9.0 ([#1103](https://github.com/dotnet/aspnet-api-versioning/issues/1103)) \ No newline at end of file