22
33namespace Asp . Versioning . ApiExplorer ;
44
5- using Microsoft . AspNetCore . Mvc . Abstractions ;
6- using Microsoft . AspNetCore . Mvc . Infrastructure ;
5+ using Microsoft . AspNetCore . Http ;
6+ using Microsoft . AspNetCore . Routing ;
77using Microsoft . Extensions . Options ;
8+ using Microsoft . Extensions . Primitives ;
89using static Asp . Versioning . ApiVersionMapping ;
910using static System . Globalization . CultureInfo ;
1011
@@ -14,23 +15,22 @@ namespace Asp.Versioning.ApiExplorer;
1415[ CLSCompliant ( false ) ]
1516public class DefaultApiVersionDescriptionProvider : IApiVersionDescriptionProvider
1617{
17- private readonly Lazy < IReadOnlyList < ApiVersionDescription > > apiVersionDescriptions ;
18+ private readonly ApiVersionDescriptionCollection collection ;
1819 private readonly IOptions < ApiExplorerOptions > options ;
1920
2021 /// <summary>
2122 /// Initializes a new instance of the <see cref="DefaultApiVersionDescriptionProvider"/> class.
2223 /// </summary>
23- /// <param name="actionDescriptorCollectionProvider">The <see cref="IActionDescriptorCollectionProvider">provider</see>
24- /// used to enumerate the actions within an application.</param>
24+ /// <param name="endpointDataSource">The <see cref="EndpointDataSource">data source</see> for <see cref="Endpoint">endpoints</see>.</param>
2525 /// <param name="sunsetPolicyManager">The <see cref="ISunsetPolicyManager">manager</see> used to resolve sunset policies.</param>
2626 /// <param name="apiExplorerOptions">The <see cref="IOptions{TOptions}">container</see> of configured
2727 /// <see cref="ApiExplorerOptions">API explorer options</see>.</param>
2828 public DefaultApiVersionDescriptionProvider (
29- IActionDescriptorCollectionProvider actionDescriptorCollectionProvider ,
29+ EndpointDataSource endpointDataSource ,
3030 ISunsetPolicyManager sunsetPolicyManager ,
3131 IOptions < ApiExplorerOptions > apiExplorerOptions )
3232 {
33- apiVersionDescriptions = LazyApiVersionDescriptions . Create ( this , actionDescriptorCollectionProvider ) ;
33+ collection = new ( this , endpointDataSource ) ;
3434 SunsetPolicyManager = sunsetPolicyManager ;
3535 options = apiExplorerOptions ;
3636 }
@@ -48,41 +48,46 @@ public DefaultApiVersionDescriptionProvider(
4848 protected ApiExplorerOptions Options => options . Value ;
4949
5050 /// <inheritdoc />
51- public IReadOnlyList < ApiVersionDescription > ApiVersionDescriptions => apiVersionDescriptions . Value ;
51+ public IReadOnlyList < ApiVersionDescription > ApiVersionDescriptions => collection . Items ;
5252
5353 /// <summary>
5454 /// Enumerates all API versions within an application.
5555 /// </summary>
56- /// <param name="actionDescriptorCollectionProvider ">The <see cref="IActionDescriptorCollectionProvider">provider </see> used to enumerate the actions within an application.</param>
56+ /// <param name="endpointDataSource ">The <see cref="EndpointDataSource">data source </see> used to enumerate the endpoints within an application.</param>
5757 /// <returns>A <see cref="IReadOnlyList{T}">read-only list</see> of <see cref="ApiVersionDescription">API version descriptions</see>.</returns>
58- protected virtual IReadOnlyList < ApiVersionDescription > EnumerateApiVersions ( IActionDescriptorCollectionProvider actionDescriptorCollectionProvider )
58+ protected virtual IReadOnlyList < ApiVersionDescription > EnumerateApiVersions ( EndpointDataSource endpointDataSource )
5959 {
60- if ( actionDescriptorCollectionProvider == null )
60+ if ( endpointDataSource == null )
6161 {
62- throw new ArgumentNullException ( nameof ( actionDescriptorCollectionProvider ) ) ;
62+ throw new ArgumentNullException ( nameof ( endpointDataSource ) ) ;
6363 }
6464
65- var actions = actionDescriptorCollectionProvider . ActionDescriptors . Items ;
66- var descriptions = new List < ApiVersionDescription > ( capacity : actions . Count ) ;
65+ var endpoints = endpointDataSource . Endpoints ;
66+ var descriptions = new List < ApiVersionDescription > ( capacity : endpoints . Count ) ;
6767 var supported = new HashSet < ApiVersion > ( ) ;
6868 var deprecated = new HashSet < ApiVersion > ( ) ;
6969
70- BucketizeApiVersions ( actions , supported , deprecated ) ;
70+ BucketizeApiVersions ( endpoints , supported , deprecated ) ;
7171 AppendDescriptions ( descriptions , supported , deprecated : false ) ;
7272 AppendDescriptions ( descriptions , deprecated , deprecated : true ) ;
7373
7474 return descriptions . OrderBy ( d => d . ApiVersion ) . ToArray ( ) ;
7575 }
7676
77- private void BucketizeApiVersions ( IReadOnlyList < ActionDescriptor > actions , ISet < ApiVersion > supported , ISet < ApiVersion > deprecated )
77+ private void BucketizeApiVersions ( IReadOnlyList < Endpoint > endpoints , ISet < ApiVersion > supported , ISet < ApiVersion > deprecated )
7878 {
7979 var declared = new HashSet < ApiVersion > ( ) ;
8080 var advertisedSupported = new HashSet < ApiVersion > ( ) ;
8181 var advertisedDeprecated = new HashSet < ApiVersion > ( ) ;
8282
83- for ( var i = 0 ; i < actions . Count ; i ++ )
83+ for ( var i = 0 ; i < endpoints . Count ; i ++ )
8484 {
85- var model = actions [ i ] . GetApiVersionMetadata ( ) . Map ( Explicit | Implicit ) ;
85+ if ( endpoints [ i ] . Metadata . GetMetadata < ApiVersionMetadata > ( ) is not ApiVersionMetadata metadata )
86+ {
87+ continue ;
88+ }
89+
90+ var model = metadata . Map ( Explicit | Implicit ) ;
8691 var versions = model . DeclaredApiVersions ;
8792
8893 for ( var j = 0 ; j < versions . Count ; j ++ )
@@ -130,28 +135,51 @@ private void AppendDescriptions( ICollection<ApiVersionDescription> descriptions
130135 }
131136 }
132137
133- private sealed class LazyApiVersionDescriptions : Lazy < IReadOnlyList < ApiVersionDescription > >
138+ private sealed class ApiVersionDescriptionCollection
134139 {
140+ private readonly object syncRoot = new ( ) ;
141+ private readonly EndpointDataSource endpointDataSource ;
135142 private readonly DefaultApiVersionDescriptionProvider apiVersionDescriptionProvider ;
136- private readonly IActionDescriptorCollectionProvider actionDescriptorCollectionProvider ;
143+ private IReadOnlyList < ApiVersionDescription > ? items ;
137144
138- private LazyApiVersionDescriptions (
145+ public ApiVersionDescriptionCollection (
139146 DefaultApiVersionDescriptionProvider apiVersionDescriptionProvider ,
140- IActionDescriptorCollectionProvider actionDescriptorCollectionProvider )
147+ EndpointDataSource endpointDataSource )
141148 {
142149 this . apiVersionDescriptionProvider = apiVersionDescriptionProvider ;
143- this . actionDescriptorCollectionProvider = actionDescriptorCollectionProvider ;
150+ this . endpointDataSource = endpointDataSource ?? throw new ArgumentNullException ( nameof ( endpointDataSource ) ) ;
151+ ChangeToken . OnChange ( endpointDataSource . GetChangeToken , UpdateItems ) ;
144152 }
145153
146- internal static Lazy < IReadOnlyList < ApiVersionDescription > > Create (
147- DefaultApiVersionDescriptionProvider apiVersionDescriptionProvider ,
148- IActionDescriptorCollectionProvider actionDescriptorCollectionProvider )
154+ public IReadOnlyList < ApiVersionDescription > Items
149155 {
150- var descriptions = new LazyApiVersionDescriptions ( apiVersionDescriptionProvider , actionDescriptorCollectionProvider ) ;
151- return new ( descriptions . EnumerateApiVersions ) ;
156+ get
157+ {
158+ Initialize ( ) ;
159+ return items ! ;
160+ }
152161 }
153162
154- private IReadOnlyList < ApiVersionDescription > EnumerateApiVersions ( ) =>
155- apiVersionDescriptionProvider . EnumerateApiVersions ( actionDescriptorCollectionProvider ) ;
163+ private void Initialize ( )
164+ {
165+ if ( items == null )
166+ {
167+ lock ( syncRoot )
168+ {
169+ if ( items == null )
170+ {
171+ UpdateItems ( ) ;
172+ }
173+ }
174+ }
175+ }
176+
177+ private void UpdateItems ( )
178+ {
179+ lock ( syncRoot )
180+ {
181+ items = apiVersionDescriptionProvider . EnumerateApiVersions ( endpointDataSource ) ;
182+ }
183+ }
156184 }
157185}
0 commit comments