Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Refactor IApiVersionDescriptionProviderFactory Registration #1077

Merged
merged 5 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ 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>
Expand Down Expand Up @@ -141,9 +139,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>() );
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,27 @@
// 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
{
private readonly ISunsetPolicyManager sunsetPolicyManager;
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;
Expand All @@ -43,6 +37,6 @@ public IApiVersionDescriptionProvider Create( EndpointDataSource endpointDataSou

collators.AddRange( providers );

return activator( collators, sunsetPolicyManager, options );
return new DefaultApiVersionDescriptionProvider( collators, sunsetPolicyManager, options );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"/>.
Expand Down Expand Up @@ -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>() );
Expand Down
Loading