2
2
3
3
namespace Asp . Versioning . ApiExplorer ;
4
4
5
- using Microsoft . AspNetCore . Mvc . Abstractions ;
6
- using Microsoft . AspNetCore . Mvc . Infrastructure ;
5
+ using Microsoft . AspNetCore . Http ;
6
+ using Microsoft . AspNetCore . Routing ;
7
7
using Microsoft . Extensions . Options ;
8
+ using Microsoft . Extensions . Primitives ;
8
9
using static Asp . Versioning . ApiVersionMapping ;
9
10
using static System . Globalization . CultureInfo ;
10
11
@@ -14,23 +15,22 @@ namespace Asp.Versioning.ApiExplorer;
14
15
[ CLSCompliant ( false ) ]
15
16
public class DefaultApiVersionDescriptionProvider : IApiVersionDescriptionProvider
16
17
{
17
- private readonly Lazy < IReadOnlyList < ApiVersionDescription > > apiVersionDescriptions ;
18
+ private readonly ApiVersionDescriptionCollection collection ;
18
19
private readonly IOptions < ApiExplorerOptions > options ;
19
20
20
21
/// <summary>
21
22
/// Initializes a new instance of the <see cref="DefaultApiVersionDescriptionProvider"/> class.
22
23
/// </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>
25
25
/// <param name="sunsetPolicyManager">The <see cref="ISunsetPolicyManager">manager</see> used to resolve sunset policies.</param>
26
26
/// <param name="apiExplorerOptions">The <see cref="IOptions{TOptions}">container</see> of configured
27
27
/// <see cref="ApiExplorerOptions">API explorer options</see>.</param>
28
28
public DefaultApiVersionDescriptionProvider (
29
- IActionDescriptorCollectionProvider actionDescriptorCollectionProvider ,
29
+ EndpointDataSource endpointDataSource ,
30
30
ISunsetPolicyManager sunsetPolicyManager ,
31
31
IOptions < ApiExplorerOptions > apiExplorerOptions )
32
32
{
33
- apiVersionDescriptions = LazyApiVersionDescriptions . Create ( this , actionDescriptorCollectionProvider ) ;
33
+ collection = new ( this , endpointDataSource ) ;
34
34
SunsetPolicyManager = sunsetPolicyManager ;
35
35
options = apiExplorerOptions ;
36
36
}
@@ -48,41 +48,46 @@ public DefaultApiVersionDescriptionProvider(
48
48
protected ApiExplorerOptions Options => options . Value ;
49
49
50
50
/// <inheritdoc />
51
- public IReadOnlyList < ApiVersionDescription > ApiVersionDescriptions => apiVersionDescriptions . Value ;
51
+ public IReadOnlyList < ApiVersionDescription > ApiVersionDescriptions => collection . Items ;
52
52
53
53
/// <summary>
54
54
/// Enumerates all API versions within an application.
55
55
/// </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>
57
57
/// <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 )
59
59
{
60
- if ( actionDescriptorCollectionProvider == null )
60
+ if ( endpointDataSource == null )
61
61
{
62
- throw new ArgumentNullException ( nameof ( actionDescriptorCollectionProvider ) ) ;
62
+ throw new ArgumentNullException ( nameof ( endpointDataSource ) ) ;
63
63
}
64
64
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 ) ;
67
67
var supported = new HashSet < ApiVersion > ( ) ;
68
68
var deprecated = new HashSet < ApiVersion > ( ) ;
69
69
70
- BucketizeApiVersions ( actions , supported , deprecated ) ;
70
+ BucketizeApiVersions ( endpoints , supported , deprecated ) ;
71
71
AppendDescriptions ( descriptions , supported , deprecated : false ) ;
72
72
AppendDescriptions ( descriptions , deprecated , deprecated : true ) ;
73
73
74
74
return descriptions . OrderBy ( d => d . ApiVersion ) . ToArray ( ) ;
75
75
}
76
76
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 )
78
78
{
79
79
var declared = new HashSet < ApiVersion > ( ) ;
80
80
var advertisedSupported = new HashSet < ApiVersion > ( ) ;
81
81
var advertisedDeprecated = new HashSet < ApiVersion > ( ) ;
82
82
83
- for ( var i = 0 ; i < actions . Count ; i ++ )
83
+ for ( var i = 0 ; i < endpoints . Count ; i ++ )
84
84
{
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 ) ;
86
91
var versions = model . DeclaredApiVersions ;
87
92
88
93
for ( var j = 0 ; j < versions . Count ; j ++ )
@@ -130,28 +135,51 @@ private void AppendDescriptions( ICollection<ApiVersionDescription> descriptions
130
135
}
131
136
}
132
137
133
- private sealed class LazyApiVersionDescriptions : Lazy < IReadOnlyList < ApiVersionDescription > >
138
+ private sealed class ApiVersionDescriptionCollection
134
139
{
140
+ private readonly object syncRoot = new ( ) ;
141
+ private readonly EndpointDataSource endpointDataSource ;
135
142
private readonly DefaultApiVersionDescriptionProvider apiVersionDescriptionProvider ;
136
- private readonly IActionDescriptorCollectionProvider actionDescriptorCollectionProvider ;
143
+ private IReadOnlyList < ApiVersionDescription > ? items ;
137
144
138
- private LazyApiVersionDescriptions (
145
+ public ApiVersionDescriptionCollection (
139
146
DefaultApiVersionDescriptionProvider apiVersionDescriptionProvider ,
140
- IActionDescriptorCollectionProvider actionDescriptorCollectionProvider )
147
+ EndpointDataSource endpointDataSource )
141
148
{
142
149
this . apiVersionDescriptionProvider = apiVersionDescriptionProvider ;
143
- this . actionDescriptorCollectionProvider = actionDescriptorCollectionProvider ;
150
+ this . endpointDataSource = endpointDataSource ?? throw new ArgumentNullException ( nameof ( endpointDataSource ) ) ;
151
+ ChangeToken . OnChange ( endpointDataSource . GetChangeToken , UpdateItems ) ;
144
152
}
145
153
146
- internal static Lazy < IReadOnlyList < ApiVersionDescription > > Create (
147
- DefaultApiVersionDescriptionProvider apiVersionDescriptionProvider ,
148
- IActionDescriptorCollectionProvider actionDescriptorCollectionProvider )
154
+ public IReadOnlyList < ApiVersionDescription > Items
149
155
{
150
- var descriptions = new LazyApiVersionDescriptions ( apiVersionDescriptionProvider , actionDescriptorCollectionProvider ) ;
151
- return new ( descriptions . EnumerateApiVersions ) ;
156
+ get
157
+ {
158
+ Initialize ( ) ;
159
+ return items ! ;
160
+ }
152
161
}
153
162
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
+ }
156
184
}
157
185
}
0 commit comments