diff --git a/eng/Versions.props b/eng/Versions.props index 5fd5f835..6e1a40b8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -14,12 +14,13 @@ 6.3.0-preview8-19405-04 3.0.0-preview8.19405.11 3.0.0-preview8.19405.4 - 2018.3.0 + 2019.1.3 12.0.2 1.0.1 6.2.0-preview-60806030202 1.5.0 - 2.7.0 + 4.0.0-preview.6.build.801 + 2.9.0 4.7.63 5.2.2 4.0.0 diff --git a/src/OpenIddict.Abstractions/Caches/IOpenIddictApplicationCache.cs b/src/OpenIddict.Abstractions/Caches/IOpenIddictApplicationCache.cs index 023d9d04..9d354295 100644 --- a/src/OpenIddict.Abstractions/Caches/IOpenIddictApplicationCache.cs +++ b/src/OpenIddict.Abstractions/Caches/IOpenIddictApplicationCache.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -22,10 +23,8 @@ namespace OpenIddict.Abstractions /// /// The application to add to the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task AddAsync([NotNull] TApplication application, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask AddAsync([NotNull] TApplication application, CancellationToken cancellationToken); /// /// Retrieves an application using its client identifier. @@ -54,11 +53,8 @@ namespace OpenIddict.Abstractions /// /// The redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the client applications corresponding to the specified redirect_uri. - /// - ValueTask> FindByPostLogoutRedirectUriAsync( + /// The client applications corresponding to the specified redirect_uri. + IAsyncEnumerable FindByPostLogoutRedirectUriAsync( [NotNull] string address, CancellationToken cancellationToken); /// @@ -66,11 +62,8 @@ namespace OpenIddict.Abstractions /// /// The redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the client applications corresponding to the specified redirect_uri. - /// - ValueTask> FindByRedirectUriAsync( + /// The client applications corresponding to the specified redirect_uri. + IAsyncEnumerable FindByRedirectUriAsync( [NotNull] string address, CancellationToken cancellationToken); /// @@ -78,9 +71,7 @@ namespace OpenIddict.Abstractions /// /// The application to remove from the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task RemoveAsync([NotNull] TApplication application, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask RemoveAsync([NotNull] TApplication application, CancellationToken cancellationToken); } } diff --git a/src/OpenIddict.Abstractions/Caches/IOpenIddictAuthorizationCache.cs b/src/OpenIddict.Abstractions/Caches/IOpenIddictAuthorizationCache.cs index 779fccd3..e25fd5a7 100644 --- a/src/OpenIddict.Abstractions/Caches/IOpenIddictAuthorizationCache.cs +++ b/src/OpenIddict.Abstractions/Caches/IOpenIddictAuthorizationCache.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -22,10 +23,8 @@ namespace OpenIddict.Abstractions /// /// The authorization to add to the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task AddAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask AddAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); /// /// Retrieves the authorizations corresponding to the specified @@ -34,11 +33,8 @@ namespace OpenIddict.Abstractions /// The subject associated with the authorization. /// The client associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the subject/client. - /// - ValueTask> FindAsync( + /// The authorizations corresponding to the subject/client. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken); /// @@ -48,11 +44,8 @@ namespace OpenIddict.Abstractions /// The client associated with the authorization. /// The authorization status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - ValueTask> FindAsync( + /// The authorizations corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken); /// @@ -63,11 +56,8 @@ namespace OpenIddict.Abstractions /// The authorization status. /// The authorization type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - ValueTask> FindAsync( + /// The authorizations corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken); @@ -80,11 +70,8 @@ namespace OpenIddict.Abstractions /// The authorization type. /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - ValueTask> FindAsync( + /// The authorizations corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken); @@ -93,11 +80,8 @@ namespace OpenIddict.Abstractions /// /// The application identifier associated with the authorizations. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified application. - /// - ValueTask> FindByApplicationIdAsync( + /// The authorizations corresponding to the specified application. + IAsyncEnumerable FindByApplicationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken); /// @@ -116,20 +100,15 @@ namespace OpenIddict.Abstractions /// /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified subject. - /// - ValueTask> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken); + /// The authorizations corresponding to the specified subject. + IAsyncEnumerable FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken); /// /// Removes the specified authorization from the cache. /// /// The authorization to remove from the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task RemoveAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask RemoveAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); } } diff --git a/src/OpenIddict.Abstractions/Caches/IOpenIddictScopeCache.cs b/src/OpenIddict.Abstractions/Caches/IOpenIddictScopeCache.cs index 44dc3cfe..7cf3eb4d 100644 --- a/src/OpenIddict.Abstractions/Caches/IOpenIddictScopeCache.cs +++ b/src/OpenIddict.Abstractions/Caches/IOpenIddictScopeCache.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -22,10 +23,8 @@ namespace OpenIddict.Abstractions /// /// The scope to add to the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task AddAsync([NotNull] TScope scope, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask AddAsync([NotNull] TScope scope, CancellationToken cancellationToken); /// /// Retrieves a scope using its unique identifier. @@ -54,31 +53,23 @@ namespace OpenIddict.Abstractions /// /// The names associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes corresponding to the specified names. - /// - ValueTask> FindByNamesAsync(ImmutableArray names, CancellationToken cancellationToken); + /// The scopes corresponding to the specified names. + IAsyncEnumerable FindByNamesAsync(ImmutableArray names, CancellationToken cancellationToken); /// /// Retrieves all the scopes that contain the specified resource. /// /// The resource associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes associated with the specified resource. - /// - ValueTask> FindByResourceAsync([NotNull] string resource, CancellationToken cancellationToken); + /// The scopes associated with the specified resource. + IAsyncEnumerable FindByResourceAsync([NotNull] string resource, CancellationToken cancellationToken); /// /// Removes the specified scope from the cache. /// /// The scope to remove from the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task RemoveAsync([NotNull] TScope scope, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask RemoveAsync([NotNull] TScope scope, CancellationToken cancellationToken); } } diff --git a/src/OpenIddict.Abstractions/Caches/IOpenIddictTokenCache.cs b/src/OpenIddict.Abstractions/Caches/IOpenIddictTokenCache.cs index 2b4aef2c..0c87e302 100644 --- a/src/OpenIddict.Abstractions/Caches/IOpenIddictTokenCache.cs +++ b/src/OpenIddict.Abstractions/Caches/IOpenIddictTokenCache.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -22,10 +23,8 @@ namespace OpenIddict.Abstractions /// /// The token to add to the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task AddAsync([NotNull] TToken token, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask AddAsync([NotNull] TToken token, CancellationToken cancellationToken); /// /// Retrieves the tokens corresponding to the specified @@ -34,11 +33,8 @@ namespace OpenIddict.Abstractions /// The subject associated with the token. /// The client associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the subject/client. - /// - ValueTask> FindAsync([NotNull] string subject, + /// The tokens corresponding to the subject/client. + IAsyncEnumerable FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken); /// @@ -48,11 +44,8 @@ namespace OpenIddict.Abstractions /// The client associated with the token. /// The token status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - ValueTask> FindAsync( + /// The tokens corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken); @@ -64,11 +57,8 @@ namespace OpenIddict.Abstractions /// The token status. /// The token type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - ValueTask> FindAsync( + /// The tokens corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken); @@ -77,22 +67,16 @@ namespace OpenIddict.Abstractions /// /// The application identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified application. - /// - ValueTask> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + /// The tokens corresponding to the specified application. + IAsyncEnumerable FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// /// Retrieves the list of tokens corresponding to the specified authorization identifier. /// /// The authorization identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified authorization. - /// - ValueTask> FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + /// The tokens corresponding to the specified authorization. + IAsyncEnumerable FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// /// Retrieves a token using its unique identifier. @@ -122,20 +106,15 @@ namespace OpenIddict.Abstractions /// /// The subject associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified subject. - /// - ValueTask> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken); + /// The tokens corresponding to the specified subject. + IAsyncEnumerable FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken); /// /// Removes the specified token from the cache. /// /// The token to remove from the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task RemoveAsync([NotNull] TToken token, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask RemoveAsync([NotNull] TToken token, CancellationToken cancellationToken); } } diff --git a/src/OpenIddict.Abstractions/Managers/IOpenIddictApplicationManager.cs b/src/OpenIddict.Abstractions/Managers/IOpenIddictApplicationManager.cs index 0436b149..b5d45d2b 100644 --- a/src/OpenIddict.Abstractions/Managers/IOpenIddictApplicationManager.cs +++ b/src/OpenIddict.Abstractions/Managers/IOpenIddictApplicationManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -23,10 +24,10 @@ namespace OpenIddict.Abstractions /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications in the database. /// - Task CountAsync(CancellationToken cancellationToken = default); + ValueTask CountAsync(CancellationToken cancellationToken = default); /// /// Determines the number of applications that match the specified query. @@ -35,10 +36,10 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications that match the specified query. /// - Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); + ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); /// /// Creates a new application based on the specified descriptor. @@ -48,10 +49,10 @@ namespace OpenIddict.Abstractions /// The application descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the unique identifier associated with the application. /// - Task CreateAsync([NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default); + ValueTask CreateAsync([NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default); /// /// Creates a new application. @@ -59,9 +60,9 @@ namespace OpenIddict.Abstractions /// The application to create. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task CreateAsync([NotNull] object application, CancellationToken cancellationToken = default); + ValueTask CreateAsync([NotNull] object application, CancellationToken cancellationToken = default); /// /// Creates a new application. @@ -72,9 +73,9 @@ namespace OpenIddict.Abstractions /// The client secret associated with the application, if applicable. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task CreateAsync([NotNull] object application, [CanBeNull] string secret, CancellationToken cancellationToken = default); + ValueTask CreateAsync([NotNull] object application, [CanBeNull] string secret, CancellationToken cancellationToken = default); /// /// Removes an existing application. @@ -82,9 +83,9 @@ namespace OpenIddict.Abstractions /// The application to delete. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task DeleteAsync([NotNull] object application, CancellationToken cancellationToken = default); + ValueTask DeleteAsync([NotNull] object application, CancellationToken cancellationToken = default); /// /// Retrieves an application using its client identifier. @@ -92,10 +93,10 @@ namespace OpenIddict.Abstractions /// The client identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - Task FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); + ValueTask FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); /// /// Retrieves an application using its unique identifier. @@ -103,32 +104,26 @@ namespace OpenIddict.Abstractions /// The unique identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); + ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); /// /// Retrieves all the applications associated with the specified post_logout_redirect_uri. /// /// The post_logout_redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified post_logout_redirect_uri. - /// - Task> FindByPostLogoutRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken = default); + /// The client applications corresponding to the specified post_logout_redirect_uri. + IAsyncEnumerable FindByPostLogoutRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken = default); /// /// Retrieves all the applications associated with the specified redirect_uri. /// /// The redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified redirect_uri. - /// - Task> FindByRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken = default); + /// The client applications corresponding to the specified redirect_uri. + IAsyncEnumerable FindByRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken = default); /// /// Executes the specified query and returns the first element. @@ -137,10 +132,10 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - Task GetAsync( + ValueTask GetAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); /// @@ -152,10 +147,10 @@ namespace OpenIddict.Abstractions /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - Task GetAsync( + ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default); @@ -254,7 +249,7 @@ namespace OpenIddict.Abstractions /// The permission. /// The that can be used to abort the operation. /// true if the application has been granted the specified permission, false otherwise. - Task HasPermissionAsync([NotNull] object application, [NotNull] string permission, CancellationToken cancellationToken = default); + ValueTask HasPermissionAsync([NotNull] object application, [NotNull] string permission, CancellationToken cancellationToken = default); /// /// Determines whether an application is a confidential client. @@ -262,7 +257,7 @@ namespace OpenIddict.Abstractions /// The application. /// The that can be used to abort the operation. /// true if the application is a confidential client, false otherwise. - Task IsConfidentialAsync([NotNull] object application, CancellationToken cancellationToken = default); + ValueTask IsConfidentialAsync([NotNull] object application, CancellationToken cancellationToken = default); /// /// Determines whether an application is a hybrid client. @@ -270,7 +265,7 @@ namespace OpenIddict.Abstractions /// The application. /// The that can be used to abort the operation. /// true if the application is a hybrid client, false otherwise. - Task IsHybridAsync([NotNull] object application, CancellationToken cancellationToken = default); + ValueTask IsHybridAsync([NotNull] object application, CancellationToken cancellationToken = default); /// /// Determines whether an application is a public client. @@ -278,7 +273,7 @@ namespace OpenIddict.Abstractions /// The application. /// The that can be used to abort the operation. /// true if the application is a public client, false otherwise. - Task IsPublicAsync([NotNull] object application, CancellationToken cancellationToken = default); + ValueTask IsPublicAsync([NotNull] object application, CancellationToken cancellationToken = default); /// /// Executes the specified query and returns all the corresponding elements. @@ -286,11 +281,8 @@ namespace OpenIddict.Abstractions /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [CanBeNull] int? count = null, [CanBeNull] int? offset = null, CancellationToken cancellationToken = default); /// @@ -299,11 +291,8 @@ namespace OpenIddict.Abstractions /// The result type. /// The query to execute. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); /// @@ -314,11 +303,8 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default); @@ -329,9 +315,9 @@ namespace OpenIddict.Abstractions /// The application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task PopulateAsync([NotNull] OpenIddictApplicationDescriptor descriptor, [NotNull] object application, CancellationToken cancellationToken = default); + ValueTask PopulateAsync([NotNull] OpenIddictApplicationDescriptor descriptor, [NotNull] object application, CancellationToken cancellationToken = default); /// /// Populates the application using the specified descriptor. @@ -340,9 +326,9 @@ namespace OpenIddict.Abstractions /// The descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task PopulateAsync([NotNull] object application, [NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default); + ValueTask PopulateAsync([NotNull] object application, [NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default); /// /// Updates an existing application. @@ -350,9 +336,9 @@ namespace OpenIddict.Abstractions /// The application to update. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task UpdateAsync([NotNull] object application, CancellationToken cancellationToken = default); + ValueTask UpdateAsync([NotNull] object application, CancellationToken cancellationToken = default); /// /// Updates an existing application. @@ -361,9 +347,9 @@ namespace OpenIddict.Abstractions /// The descriptor used to update the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task UpdateAsync([NotNull] object application, [NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default); + ValueTask UpdateAsync([NotNull] object application, [NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default); /// /// Updates an existing application and replaces the existing secret. @@ -374,20 +360,17 @@ namespace OpenIddict.Abstractions /// The client secret associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task UpdateAsync([NotNull] object application, [CanBeNull] string secret, CancellationToken cancellationToken = default); + ValueTask UpdateAsync([NotNull] object application, [CanBeNull] string secret, CancellationToken cancellationToken = default); /// /// Validates the application to ensure it's in a consistent state. /// /// The application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the validation error encountered when validating the application. - /// - Task> ValidateAsync([NotNull] object application, CancellationToken cancellationToken = default); + /// The validation error encountered when validating the application. + IAsyncEnumerable ValidateAsync([NotNull] object application, CancellationToken cancellationToken = default); /// /// Validates the client_secret associated with an application. @@ -395,12 +378,12 @@ namespace OpenIddict.Abstractions /// The application. /// The secret that should be compared to the client_secret stored in the database. /// The that can be used to abort the operation. - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns a boolean indicating whether the client secret was valid. /// - Task ValidateClientSecretAsync([NotNull] object application, string secret, CancellationToken cancellationToken = default); + ValueTask ValidateClientSecretAsync([NotNull] object application, string secret, CancellationToken cancellationToken = default); /// /// Validates the redirect_uri to ensure it's associated with an application. @@ -409,9 +392,9 @@ namespace OpenIddict.Abstractions /// The address that should be compared to one of the redirect_uri stored in the database. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns a boolean indicating whether the redirect_uri was valid. /// - Task ValidateRedirectUriAsync([NotNull] object application, [NotNull] string address, CancellationToken cancellationToken = default); + ValueTask ValidateRedirectUriAsync([NotNull] object application, [NotNull] string address, CancellationToken cancellationToken = default); } } diff --git a/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs b/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs index 91ee6d57..4ffa9861 100644 --- a/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs +++ b/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -23,10 +24,10 @@ namespace OpenIddict.Abstractions /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations in the database. /// - Task CountAsync(CancellationToken cancellationToken = default); + ValueTask CountAsync(CancellationToken cancellationToken = default); /// /// Determines the number of authorizations that match the specified query. @@ -35,10 +36,10 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations that match the specified query. /// - Task CountAsync( + ValueTask CountAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); /// @@ -51,9 +52,9 @@ namespace OpenIddict.Abstractions /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, whose result returns the authorization. + /// A that can be used to monitor the asynchronous operation, whose result returns the authorization. /// - Task CreateAsync( + ValueTask CreateAsync( [NotNull] ImmutableDictionary claims, [NotNull] string subject, [NotNull] string client, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken = default); @@ -63,9 +64,9 @@ namespace OpenIddict.Abstractions /// The authorization descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, whose result returns the authorization. + /// A that can be used to monitor the asynchronous operation, whose result returns the authorization. /// - Task CreateAsync([NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default); + ValueTask CreateAsync([NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default); /// /// Creates a new authorization. @@ -73,9 +74,9 @@ namespace OpenIddict.Abstractions /// The application to create. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task CreateAsync([NotNull] object authorization, CancellationToken cancellationToken = default); + ValueTask CreateAsync([NotNull] object authorization, CancellationToken cancellationToken = default); /// /// Removes an existing authorization. @@ -83,9 +84,9 @@ namespace OpenIddict.Abstractions /// The authorization to delete. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task DeleteAsync([NotNull] object authorization, CancellationToken cancellationToken = default); + ValueTask DeleteAsync([NotNull] object authorization, CancellationToken cancellationToken = default); /// /// Retrieves the authorizations corresponding to the specified @@ -94,11 +95,8 @@ namespace OpenIddict.Abstractions /// The subject associated with the authorization. /// The client associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the subject/client. - /// - Task> FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken = default); + /// The authorizations corresponding to the subject/client. + IAsyncEnumerable FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken = default); /// /// Retrieves the authorizations matching the specified parameters. @@ -107,11 +105,8 @@ namespace OpenIddict.Abstractions /// The client associated with the authorization. /// The authorization status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - Task> FindAsync( + /// The authorizations corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken = default); @@ -123,11 +118,8 @@ namespace OpenIddict.Abstractions /// The authorization status. /// The authorization type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - Task> FindAsync( + /// The authorizations corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken = default); @@ -140,11 +132,8 @@ namespace OpenIddict.Abstractions /// The authorization type. /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - Task> FindAsync( + /// The authorizations corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken = default); @@ -153,11 +142,8 @@ namespace OpenIddict.Abstractions /// /// The application identifier associated with the authorizations. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified application. - /// - Task> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); + /// The authorizations corresponding to the specified application. + IAsyncEnumerable FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); /// /// Retrieves an authorization using its unique identifier. @@ -165,21 +151,18 @@ namespace OpenIddict.Abstractions /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the authorization corresponding to the identifier. /// - Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); + ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); /// /// Retrieves all the authorizations corresponding to the specified subject. /// /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified subject. - /// - Task> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken = default); + /// The authorizations corresponding to the specified subject. + IAsyncEnumerable FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken = default); /// /// Retrieves the optional application identifier associated with an authorization. @@ -199,10 +182,10 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - Task GetAsync( + ValueTask GetAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); /// @@ -214,10 +197,10 @@ namespace OpenIddict.Abstractions /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - Task GetAsync( + ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default); @@ -283,7 +266,7 @@ namespace OpenIddict.Abstractions /// The scopes. /// The that can be used to abort the operation. /// true if the scopes are included in the authorization, false otherwise. - Task HasScopesAsync([NotNull] object authorization, ImmutableArray scopes, CancellationToken cancellationToken = default); + ValueTask HasScopesAsync([NotNull] object authorization, ImmutableArray scopes, CancellationToken cancellationToken = default); /// /// Determines whether a given authorization is ad hoc. @@ -291,7 +274,7 @@ namespace OpenIddict.Abstractions /// The authorization. /// The that can be used to abort the operation. /// true if the authorization is ad hoc, false otherwise. - Task IsAdHocAsync([NotNull] object authorization, CancellationToken cancellationToken = default); + ValueTask IsAdHocAsync([NotNull] object authorization, CancellationToken cancellationToken = default); /// /// Determines whether a given authorization is permanent. @@ -299,7 +282,7 @@ namespace OpenIddict.Abstractions /// The authorization. /// The that can be used to abort the operation. /// true if the authorization is permanent, false otherwise. - Task IsPermanentAsync([NotNull] object authorization, CancellationToken cancellationToken = default); + ValueTask IsPermanentAsync([NotNull] object authorization, CancellationToken cancellationToken = default); /// /// Determines whether a given authorization has been revoked. @@ -307,7 +290,7 @@ namespace OpenIddict.Abstractions /// The authorization. /// The that can be used to abort the operation. /// true if the authorization has been revoked, false otherwise. - Task IsRevokedAsync([NotNull] object authorization, CancellationToken cancellationToken = default); + ValueTask IsRevokedAsync([NotNull] object authorization, CancellationToken cancellationToken = default); /// /// Determines whether a given authorization is valid. @@ -315,7 +298,7 @@ namespace OpenIddict.Abstractions /// The authorization. /// The that can be used to abort the operation. /// true if the authorization is valid, false otherwise. - Task IsValidAsync([NotNull] object authorization, CancellationToken cancellationToken = default); + ValueTask IsValidAsync([NotNull] object authorization, CancellationToken cancellationToken = default); /// /// Executes the specified query and returns all the corresponding elements. @@ -323,11 +306,8 @@ namespace OpenIddict.Abstractions /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [CanBeNull] int? count = null, [CanBeNull] int? offset = null, CancellationToken cancellationToken = default); /// @@ -336,11 +316,8 @@ namespace OpenIddict.Abstractions /// The result type. /// The query to execute. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); /// @@ -351,11 +328,8 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default); @@ -366,9 +340,9 @@ namespace OpenIddict.Abstractions /// The authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task PopulateAsync([NotNull] OpenIddictAuthorizationDescriptor descriptor, [NotNull] object authorization, CancellationToken cancellationToken = default); + ValueTask PopulateAsync([NotNull] OpenIddictAuthorizationDescriptor descriptor, [NotNull] object authorization, CancellationToken cancellationToken = default); /// /// Populates the authorization using the specified descriptor. @@ -377,26 +351,26 @@ namespace OpenIddict.Abstractions /// The descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task PopulateAsync([NotNull] object authorization, [NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default); + ValueTask PopulateAsync([NotNull] object authorization, [NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default); /// /// Removes the authorizations that are marked as invalid and the ad-hoc ones that have no valid/nonexpired token attached. /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task PruneAsync(CancellationToken cancellationToken = default); + ValueTask PruneAsync(CancellationToken cancellationToken = default); /// /// Revokes an authorization. /// /// The authorization to revoke. /// The that can be used to abort the operation. - /// A that can be used to monitor the asynchronous operation. - Task RevokeAsync([NotNull] object authorization, CancellationToken cancellationToken = default); + /// A that can be used to monitor the asynchronous operation. + ValueTask RevokeAsync([NotNull] object authorization, CancellationToken cancellationToken = default); /// /// Sets the application identifier associated with an authorization. @@ -405,9 +379,9 @@ namespace OpenIddict.Abstractions /// The unique identifier associated with the client application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task SetApplicationIdAsync([NotNull] object authorization, [CanBeNull] string identifier, CancellationToken cancellationToken = default); + ValueTask SetApplicationIdAsync([NotNull] object authorization, [CanBeNull] string identifier, CancellationToken cancellationToken = default); /// /// Updates an existing authorization. @@ -415,9 +389,9 @@ namespace OpenIddict.Abstractions /// The authorization to update. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task UpdateAsync([NotNull] object authorization, CancellationToken cancellationToken = default); + ValueTask UpdateAsync([NotNull] object authorization, CancellationToken cancellationToken = default); /// /// Updates an existing authorization. @@ -426,19 +400,16 @@ namespace OpenIddict.Abstractions /// The descriptor used to update the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task UpdateAsync([NotNull] object authorization, [NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default); + ValueTask UpdateAsync([NotNull] object authorization, [NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default); /// /// Validates the authorization to ensure it's in a consistent state. /// /// The authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the validation error encountered when validating the authorization. - /// - Task> ValidateAsync([NotNull] object authorization, CancellationToken cancellationToken = default); + /// The validation error encountered when validating the authorization. + IAsyncEnumerable ValidateAsync([NotNull] object authorization, CancellationToken cancellationToken = default); } } diff --git a/src/OpenIddict.Abstractions/Managers/IOpenIddictScopeManager.cs b/src/OpenIddict.Abstractions/Managers/IOpenIddictScopeManager.cs index fd3dd2fa..7f6f8b03 100644 --- a/src/OpenIddict.Abstractions/Managers/IOpenIddictScopeManager.cs +++ b/src/OpenIddict.Abstractions/Managers/IOpenIddictScopeManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -23,10 +24,10 @@ namespace OpenIddict.Abstractions /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes in the database. /// - Task CountAsync(CancellationToken cancellationToken = default); + ValueTask CountAsync(CancellationToken cancellationToken = default); /// /// Determines the number of scopes that match the specified query. @@ -35,10 +36,10 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes that match the specified query. /// - Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); + ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); /// /// Creates a new scope based on the specified descriptor. @@ -46,9 +47,9 @@ namespace OpenIddict.Abstractions /// The scope descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, whose result returns the scope. + /// A that can be used to monitor the asynchronous operation, whose result returns the scope. /// - Task CreateAsync([NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default); + ValueTask CreateAsync([NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default); /// /// Creates a new scope. @@ -56,9 +57,9 @@ namespace OpenIddict.Abstractions /// The scope to create. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task CreateAsync([NotNull] object scope, CancellationToken cancellationToken = default); + ValueTask CreateAsync([NotNull] object scope, CancellationToken cancellationToken = default); /// /// Removes an existing scope. @@ -66,9 +67,9 @@ namespace OpenIddict.Abstractions /// The scope to delete. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task DeleteAsync([NotNull] object scope, CancellationToken cancellationToken = default); + ValueTask DeleteAsync([NotNull] object scope, CancellationToken cancellationToken = default); /// /// Retrieves a scope using its unique identifier. @@ -76,10 +77,10 @@ namespace OpenIddict.Abstractions /// The unique identifier associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the identifier. /// - Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); + ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); /// /// Retrieves a scope using its name. @@ -87,32 +88,26 @@ namespace OpenIddict.Abstractions /// The name associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the specified name. /// - Task FindByNameAsync([NotNull] string name, CancellationToken cancellationToken = default); + ValueTask FindByNameAsync([NotNull] string name, CancellationToken cancellationToken = default); /// /// Retrieves a list of scopes using their name. /// /// The names associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes corresponding to the specified names. - /// - Task> FindByNamesAsync(ImmutableArray names, CancellationToken cancellationToken = default); + /// The scopes corresponding to the specified names. + IAsyncEnumerable FindByNamesAsync(ImmutableArray names, CancellationToken cancellationToken = default); /// /// Retrieves all the scopes that contain the specified resource. /// /// The resource associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes associated with the specified resource. - /// - Task> FindByResourceAsync([NotNull] string resource, CancellationToken cancellationToken = default); + /// The scopes associated with the specified resource. + IAsyncEnumerable FindByResourceAsync([NotNull] string resource, CancellationToken cancellationToken = default); /// /// Executes the specified query and returns the first element. @@ -121,10 +116,10 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - Task GetAsync( + ValueTask GetAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); /// @@ -136,10 +131,10 @@ namespace OpenIddict.Abstractions /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - Task GetAsync( + ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default); @@ -204,11 +199,8 @@ namespace OpenIddict.Abstractions /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [CanBeNull] int? count = null, [CanBeNull] int? offset = null, CancellationToken cancellationToken = default); /// @@ -217,11 +209,8 @@ namespace OpenIddict.Abstractions /// The result type. /// The query to execute. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); /// @@ -232,11 +221,8 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default); @@ -245,11 +231,8 @@ namespace OpenIddict.Abstractions /// /// The scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the resources associated with the specified scopes. - /// - Task> ListResourcesAsync(ImmutableArray scopes, CancellationToken cancellationToken = default); + /// All the resources associated with the specified scopes. + IAsyncEnumerable ListResourcesAsync(ImmutableArray scopes, CancellationToken cancellationToken = default); /// /// Populates the specified descriptor using the properties exposed by the scope. @@ -258,9 +241,9 @@ namespace OpenIddict.Abstractions /// The scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task PopulateAsync([NotNull] OpenIddictScopeDescriptor descriptor, [NotNull] object scope, CancellationToken cancellationToken = default); + ValueTask PopulateAsync([NotNull] OpenIddictScopeDescriptor descriptor, [NotNull] object scope, CancellationToken cancellationToken = default); /// /// Populates the scope using the specified descriptor. @@ -269,9 +252,9 @@ namespace OpenIddict.Abstractions /// The descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task PopulateAsync([NotNull] object scope, [NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default); + ValueTask PopulateAsync([NotNull] object scope, [NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default); /// /// Updates an existing scope. @@ -279,9 +262,9 @@ namespace OpenIddict.Abstractions /// The scope to update. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task UpdateAsync([NotNull] object scope, CancellationToken cancellationToken = default); + ValueTask UpdateAsync([NotNull] object scope, CancellationToken cancellationToken = default); /// /// Updates an existing scope. @@ -290,19 +273,16 @@ namespace OpenIddict.Abstractions /// The descriptor used to update the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task UpdateAsync([NotNull] object scope, [NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default); + ValueTask UpdateAsync([NotNull] object scope, [NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default); /// /// Validates the scope to ensure it's in a consistent state. /// /// The scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the validation error encountered when validating the scope. - /// - Task> ValidateAsync([NotNull] object scope, CancellationToken cancellationToken = default); + /// The validation error encountered when validating the scope. + IAsyncEnumerable ValidateAsync([NotNull] object scope, CancellationToken cancellationToken = default); } } diff --git a/src/OpenIddict.Abstractions/Managers/IOpenIddictTokenManager.cs b/src/OpenIddict.Abstractions/Managers/IOpenIddictTokenManager.cs index 37421f0c..f9e9868d 100644 --- a/src/OpenIddict.Abstractions/Managers/IOpenIddictTokenManager.cs +++ b/src/OpenIddict.Abstractions/Managers/IOpenIddictTokenManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -23,10 +24,10 @@ namespace OpenIddict.Abstractions /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of tokens in the database. /// - Task CountAsync(CancellationToken cancellationToken = default); + ValueTask CountAsync(CancellationToken cancellationToken = default); /// /// Determines the number of tokens that match the specified query. @@ -35,10 +36,10 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of tokens that match the specified query. /// - Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); + ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); /// /// Creates a new token based on the specified descriptor. @@ -46,9 +47,9 @@ namespace OpenIddict.Abstractions /// The token descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, whose result returns the token. + /// A that can be used to monitor the asynchronous operation, whose result returns the token. /// - Task CreateAsync([NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default); + ValueTask CreateAsync([NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default); /// /// Creates a new token. @@ -56,9 +57,9 @@ namespace OpenIddict.Abstractions /// The token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task CreateAsync([NotNull] object token, CancellationToken cancellationToken = default); + ValueTask CreateAsync([NotNull] object token, CancellationToken cancellationToken = default); /// /// Removes an existing token. @@ -66,9 +67,9 @@ namespace OpenIddict.Abstractions /// The token to delete. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task DeleteAsync([NotNull] object token, CancellationToken cancellationToken = default); + ValueTask DeleteAsync([NotNull] object token, CancellationToken cancellationToken = default); /// /// Extends the specified token by replacing its expiration date. @@ -77,9 +78,9 @@ namespace OpenIddict.Abstractions /// The date on which the token will no longer be considered valid. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task ExtendAsync([NotNull] object token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken = default); + ValueTask ExtendAsync([NotNull] object token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken = default); /// /// Retrieves the tokens corresponding to the specified @@ -88,11 +89,8 @@ namespace OpenIddict.Abstractions /// The subject associated with the token. /// The client associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the subject/client. - /// - Task> FindAsync([NotNull] string subject, + /// The tokens corresponding to the subject/client. + IAsyncEnumerable FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken = default); /// @@ -102,11 +100,8 @@ namespace OpenIddict.Abstractions /// The client associated with the token. /// The token status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - Task> FindAsync( + /// The tokens corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken = default); @@ -118,11 +113,8 @@ namespace OpenIddict.Abstractions /// The token status. /// The token type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - Task> FindAsync( + /// The tokens corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken = default); @@ -131,22 +123,16 @@ namespace OpenIddict.Abstractions /// /// The application identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified application. - /// - Task> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); + /// The tokens corresponding to the specified application. + IAsyncEnumerable FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); /// /// Retrieves the list of tokens corresponding to the specified authorization identifier. /// /// The authorization identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified authorization. - /// - Task> FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); + /// The tokens corresponding to the specified authorization. + IAsyncEnumerable FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); /// /// Retrieves a token using its unique identifier. @@ -154,10 +140,10 @@ namespace OpenIddict.Abstractions /// The unique identifier associated with the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the token corresponding to the unique identifier. /// - Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); + ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); /// /// Retrieves the list of tokens corresponding to the specified reference identifier. @@ -166,21 +152,18 @@ namespace OpenIddict.Abstractions /// The reference identifier associated with the tokens. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the tokens corresponding to the specified reference identifier. /// - Task FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); + ValueTask FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); /// /// Retrieves the list of tokens corresponding to the specified subject. /// /// The subject associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified subject. - /// - Task> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken = default); + /// The tokens corresponding to the specified subject. + IAsyncEnumerable FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken = default); /// /// Retrieves the optional application identifier associated with a token. @@ -200,10 +183,10 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - Task GetAsync( + ValueTask GetAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); /// @@ -215,10 +198,10 @@ namespace OpenIddict.Abstractions /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - Task GetAsync( + ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default); @@ -329,7 +312,7 @@ namespace OpenIddict.Abstractions /// The token. /// The that can be used to abort the operation. /// true if the token has already been redemeed, false otherwise. - Task IsRedeemedAsync([NotNull] object token, CancellationToken cancellationToken = default); + ValueTask IsRedeemedAsync([NotNull] object token, CancellationToken cancellationToken = default); /// /// Determines whether a given token has been revoked. @@ -337,7 +320,7 @@ namespace OpenIddict.Abstractions /// The token. /// The that can be used to abort the operation. /// true if the token has been revoked, false otherwise. - Task IsRevokedAsync([NotNull] object token, CancellationToken cancellationToken = default); + ValueTask IsRevokedAsync([NotNull] object token, CancellationToken cancellationToken = default); /// /// Determines whether a given token is valid. @@ -345,7 +328,7 @@ namespace OpenIddict.Abstractions /// The token. /// The that can be used to abort the operation. /// true if the token is valid, false otherwise. - Task IsValidAsync([NotNull] object token, CancellationToken cancellationToken = default); + ValueTask IsValidAsync([NotNull] object token, CancellationToken cancellationToken = default); /// /// Executes the specified query and returns all the corresponding elements. @@ -353,11 +336,8 @@ namespace OpenIddict.Abstractions /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [CanBeNull] int? count = null, [CanBeNull] int? offset = null, CancellationToken cancellationToken = default); /// @@ -366,11 +346,8 @@ namespace OpenIddict.Abstractions /// The result type. /// The query to execute. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default); /// @@ -381,11 +358,8 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default); @@ -396,9 +370,9 @@ namespace OpenIddict.Abstractions /// The token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task PopulateAsync([NotNull] OpenIddictTokenDescriptor descriptor, [NotNull] object token, CancellationToken cancellationToken = default); + ValueTask PopulateAsync([NotNull] OpenIddictTokenDescriptor descriptor, [NotNull] object token, CancellationToken cancellationToken = default); /// /// Populates the token using the specified descriptor. @@ -407,34 +381,34 @@ namespace OpenIddict.Abstractions /// The descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task PopulateAsync([NotNull] object token, [NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default); + ValueTask PopulateAsync([NotNull] object token, [NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default); /// /// Removes the tokens that are marked as expired or invalid. /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task PruneAsync(CancellationToken cancellationToken = default); + ValueTask PruneAsync(CancellationToken cancellationToken = default); /// /// Redeems a token. /// /// The token to redeem. /// The that can be used to abort the operation. - /// A that can be used to monitor the asynchronous operation. - Task RedeemAsync([NotNull] object token, CancellationToken cancellationToken = default); + /// A that can be used to monitor the asynchronous operation. + ValueTask RedeemAsync([NotNull] object token, CancellationToken cancellationToken = default); /// /// Revokes a token. /// /// The token to revoke. /// The that can be used to abort the operation. - /// A that can be used to monitor the asynchronous operation. - Task RevokeAsync([NotNull] object token, CancellationToken cancellationToken = default); + /// A that can be used to monitor the asynchronous operation. + ValueTask RevokeAsync([NotNull] object token, CancellationToken cancellationToken = default); /// /// Sets the application identifier associated with a token. @@ -443,9 +417,9 @@ namespace OpenIddict.Abstractions /// The unique identifier associated with the client application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task SetApplicationIdAsync([NotNull] object token, [CanBeNull] string identifier, CancellationToken cancellationToken = default); + ValueTask SetApplicationIdAsync([NotNull] object token, [CanBeNull] string identifier, CancellationToken cancellationToken = default); /// /// Sets the authorization identifier associated with a token. @@ -454,9 +428,9 @@ namespace OpenIddict.Abstractions /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task SetAuthorizationIdAsync([NotNull] object token, [CanBeNull] string identifier, CancellationToken cancellationToken = default); + ValueTask SetAuthorizationIdAsync([NotNull] object token, [CanBeNull] string identifier, CancellationToken cancellationToken = default); /// /// Updates an existing token. @@ -464,9 +438,9 @@ namespace OpenIddict.Abstractions /// The token to update. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task UpdateAsync([NotNull] object token, CancellationToken cancellationToken = default); + ValueTask UpdateAsync([NotNull] object token, CancellationToken cancellationToken = default); /// /// Updates an existing token. @@ -475,19 +449,16 @@ namespace OpenIddict.Abstractions /// The descriptor used to update the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - Task UpdateAsync([NotNull] object token, [NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default); + ValueTask UpdateAsync([NotNull] object token, [NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default); /// /// Validates the token to ensure it's in a consistent state. /// /// The token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the validation error encountered when validating the token. - /// - Task> ValidateAsync([NotNull] object token, CancellationToken cancellationToken = default); + /// The validation error encountered when validating the token. + IAsyncEnumerable ValidateAsync([NotNull] object token, CancellationToken cancellationToken = default); } } diff --git a/src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs index 11b5ed90..982c1ff1 100644 --- a/src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs +++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs @@ -5,6 +5,7 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -25,10 +26,10 @@ namespace OpenIddict.Abstractions /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications in the database. /// - Task CountAsync(CancellationToken cancellationToken); + ValueTask CountAsync(CancellationToken cancellationToken); /// /// Determines the number of applications that match the specified query. @@ -37,30 +38,26 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications that match the specified query. /// - Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken); + ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken); /// /// Creates a new application. /// /// The application to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken); /// /// Removes an existing application. /// /// The application to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken); /// /// Retrieves an application using its unique identifier. @@ -68,10 +65,10 @@ namespace OpenIddict.Abstractions /// The unique identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// /// Retrieves an application using its client identifier. @@ -79,32 +76,26 @@ namespace OpenIddict.Abstractions /// The client identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - Task FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + ValueTask FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// /// Retrieves all the applications associated with the specified post_logout_redirect_uri. /// /// The post_logout_redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified post_logout_redirect_uri. - /// - Task> FindByPostLogoutRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken); + /// The client applications corresponding to the specified post_logout_redirect_uri. + IAsyncEnumerable FindByPostLogoutRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken); /// /// Retrieves all the applications associated with the specified redirect_uri. /// /// The redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified redirect_uri. - /// - Task> FindByRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken); + /// The client applications corresponding to the specified redirect_uri. + IAsyncEnumerable FindByRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken); /// /// Executes the specified query and returns the first element. @@ -115,10 +106,10 @@ namespace OpenIddict.Abstractions /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - Task GetAsync( + ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken); @@ -250,11 +241,8 @@ namespace OpenIddict.Abstractions /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken); + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken); /// /// Executes the specified query and returns all the corresponding elements. @@ -264,11 +252,8 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken); @@ -278,10 +263,8 @@ namespace OpenIddict.Abstractions /// The application. /// The client identifier associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetClientIdAsync([NotNull] TApplication application, [CanBeNull] string identifier, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetClientIdAsync([NotNull] TApplication application, [CanBeNull] string identifier, CancellationToken cancellationToken); /// /// Sets the client secret associated with an application. @@ -291,10 +274,8 @@ namespace OpenIddict.Abstractions /// The application. /// The client secret associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetClientSecretAsync([NotNull] TApplication application, [CanBeNull] string secret, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetClientSecretAsync([NotNull] TApplication application, [CanBeNull] string secret, CancellationToken cancellationToken); /// /// Sets the client type associated with an application. @@ -302,10 +283,8 @@ namespace OpenIddict.Abstractions /// The application. /// The client type associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetClientTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetClientTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken); /// /// Sets the consent type associated with an application. @@ -313,10 +292,8 @@ namespace OpenIddict.Abstractions /// The application. /// The consent type associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetConsentTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetConsentTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken); /// /// Sets the display name associated with an application. @@ -324,10 +301,8 @@ namespace OpenIddict.Abstractions /// The application. /// The display name associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetDisplayNameAsync([NotNull] TApplication application, [CanBeNull] string name, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetDisplayNameAsync([NotNull] TApplication application, [CanBeNull] string name, CancellationToken cancellationToken); /// /// Sets the permissions associated with an application. @@ -335,10 +310,8 @@ namespace OpenIddict.Abstractions /// The application. /// The permissions associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetPermissionsAsync([NotNull] TApplication application, ImmutableArray permissions, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetPermissionsAsync([NotNull] TApplication application, ImmutableArray permissions, CancellationToken cancellationToken); /// /// Sets the logout callback addresses associated with an application. @@ -346,10 +319,8 @@ namespace OpenIddict.Abstractions /// The application. /// The logout callback addresses associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetPostLogoutRedirectUrisAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + ValueTask SetPostLogoutRedirectUrisAsync([NotNull] TApplication application, ImmutableArray addresses, CancellationToken cancellationToken); /// @@ -358,10 +329,8 @@ namespace OpenIddict.Abstractions /// The application. /// The additional properties associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken); /// /// Sets the callback addresses associated with an application. @@ -369,10 +338,8 @@ namespace OpenIddict.Abstractions /// The application. /// The callback addresses associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetRedirectUrisAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + ValueTask SetRedirectUrisAsync([NotNull] TApplication application, ImmutableArray addresses, CancellationToken cancellationToken); /// @@ -380,9 +347,7 @@ namespace OpenIddict.Abstractions /// /// The application to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs index 211936a3..eecb897c 100644 --- a/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs @@ -5,6 +5,7 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -25,10 +26,10 @@ namespace OpenIddict.Abstractions /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations in the database. /// - Task CountAsync(CancellationToken cancellationToken); + ValueTask CountAsync(CancellationToken cancellationToken); /// /// Determines the number of authorizations that match the specified query. @@ -37,30 +38,26 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations that match the specified query. /// - Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken); + ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken); /// /// Creates a new authorization. /// /// The authorization to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); /// /// Removes an existing authorization. /// /// The authorization to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); /// /// Retrieves the authorizations corresponding to the specified @@ -69,12 +66,8 @@ namespace OpenIddict.Abstractions /// The subject associated with the authorization. /// The client associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the subject/client. - /// - Task> FindAsync( - [NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken); + /// The authorizations corresponding to the subject/client. + IAsyncEnumerable FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken); /// /// Retrieves the authorizations matching the specified parameters. @@ -83,11 +76,8 @@ namespace OpenIddict.Abstractions /// The client associated with the authorization. /// The authorization status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - Task> FindAsync( + /// The authorizations corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken); @@ -99,11 +89,8 @@ namespace OpenIddict.Abstractions /// The authorization status. /// The authorization type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - Task> FindAsync( + /// The authorizations corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken); @@ -116,11 +103,8 @@ namespace OpenIddict.Abstractions /// The authorization type. /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - Task> FindAsync( + /// The authorizations corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken); @@ -130,11 +114,8 @@ namespace OpenIddict.Abstractions /// /// The application identifier associated with the authorizations. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified application. - /// - Task> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + /// The authorizations corresponding to the specified application. + IAsyncEnumerable FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// /// Retrieves an authorization using its unique identifier. @@ -142,21 +123,18 @@ namespace OpenIddict.Abstractions /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the authorization corresponding to the identifier. /// - Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// /// Retrieves all the authorizations corresponding to the specified subject. /// /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified subject. - /// - Task> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken); + /// The authorizations corresponding to the specified subject. + IAsyncEnumerable FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken); /// /// Retrieves the optional application identifier associated with an authorization. @@ -178,10 +156,10 @@ namespace OpenIddict.Abstractions /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - Task GetAsync( + ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken); @@ -267,11 +245,8 @@ namespace OpenIddict.Abstractions /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken); + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken); /// /// Executes the specified query and returns all the corresponding elements. @@ -281,11 +256,8 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken); @@ -293,10 +265,8 @@ namespace OpenIddict.Abstractions /// Removes the authorizations that are marked as invalid and the ad-hoc ones that have no valid/nonexpired token attached. /// /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task PruneAsync(CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask PruneAsync(CancellationToken cancellationToken); /// /// Sets the application identifier associated with an authorization. @@ -304,10 +274,8 @@ namespace OpenIddict.Abstractions /// The authorization. /// The unique identifier associated with the client application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetApplicationIdAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + ValueTask SetApplicationIdAsync([NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken); /// @@ -316,10 +284,8 @@ namespace OpenIddict.Abstractions /// The authorization. /// The additional properties associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken); /// /// Sets the scopes associated with an authorization. @@ -327,10 +293,8 @@ namespace OpenIddict.Abstractions /// The authorization. /// The scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetScopesAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + ValueTask SetScopesAsync([NotNull] TAuthorization authorization, ImmutableArray scopes, CancellationToken cancellationToken); /// @@ -339,10 +303,8 @@ namespace OpenIddict.Abstractions /// The authorization. /// The status associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetStatusAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + ValueTask SetStatusAsync([NotNull] TAuthorization authorization, [CanBeNull] string status, CancellationToken cancellationToken); /// @@ -351,10 +313,8 @@ namespace OpenIddict.Abstractions /// The authorization. /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetSubjectAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + ValueTask SetSubjectAsync([NotNull] TAuthorization authorization, [CanBeNull] string subject, CancellationToken cancellationToken); /// @@ -363,10 +323,8 @@ namespace OpenIddict.Abstractions /// The authorization. /// The type associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetTypeAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + ValueTask SetTypeAsync([NotNull] TAuthorization authorization, [CanBeNull] string type, CancellationToken cancellationToken); /// @@ -374,9 +332,7 @@ namespace OpenIddict.Abstractions /// /// The authorization to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs index fd204f66..aee87eff 100644 --- a/src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs +++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs @@ -5,6 +5,7 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -25,10 +26,10 @@ namespace OpenIddict.Abstractions /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes in the database. /// - Task CountAsync(CancellationToken cancellationToken); + ValueTask CountAsync(CancellationToken cancellationToken); /// /// Determines the number of scopes that match the specified query. @@ -37,30 +38,26 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes that match the specified query. /// - Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken); + ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken); /// /// Creates a new scope. /// /// The scope to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken); /// /// Removes an existing scope. /// /// The scope to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken); /// /// Retrieves a scope using its unique identifier. @@ -68,10 +65,10 @@ namespace OpenIddict.Abstractions /// The unique identifier associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the identifier. /// - Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// /// Retrieves a scope using its name. @@ -79,32 +76,26 @@ namespace OpenIddict.Abstractions /// The name associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the specified name. /// - Task FindByNameAsync([NotNull] string name, CancellationToken cancellationToken); + ValueTask FindByNameAsync([NotNull] string name, CancellationToken cancellationToken); /// /// Retrieves a list of scopes using their name. /// /// The names associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes corresponding to the specified names. - /// - Task> FindByNamesAsync(ImmutableArray names, CancellationToken cancellationToken); + /// The scopes corresponding to the specified names. + IAsyncEnumerable FindByNamesAsync(ImmutableArray names, CancellationToken cancellationToken); /// /// Retrieves all the scopes that contain the specified resource. /// /// The resource associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes associated with the specified resource. - /// - Task> FindByResourceAsync([NotNull] string resource, CancellationToken cancellationToken); + /// The scopes associated with the specified resource. + IAsyncEnumerable FindByResourceAsync([NotNull] string resource, CancellationToken cancellationToken); /// /// Executes the specified query and returns the first element. @@ -115,10 +106,10 @@ namespace OpenIddict.Abstractions /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - Task GetAsync( + ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken); @@ -204,11 +195,8 @@ namespace OpenIddict.Abstractions /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken); + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken); /// /// Executes the specified query and returns all the corresponding elements. @@ -218,11 +206,8 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken); @@ -232,10 +217,8 @@ namespace OpenIddict.Abstractions /// The scope. /// The description associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken); /// /// Sets the display name associated with a scope. @@ -243,10 +226,8 @@ namespace OpenIddict.Abstractions /// The scope. /// The display name associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetDisplayNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetDisplayNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken); /// /// Sets the name associated with a scope. @@ -254,10 +235,8 @@ namespace OpenIddict.Abstractions /// The scope. /// The name associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken); /// /// Sets the additional properties associated with a scope. @@ -265,10 +244,8 @@ namespace OpenIddict.Abstractions /// The scope. /// The additional properties associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken); /// /// Sets the resources associated with a scope. @@ -276,19 +253,15 @@ namespace OpenIddict.Abstractions /// The scope. /// The resources associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetResourcesAsync([NotNull] TScope scope, ImmutableArray resources, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetResourcesAsync([NotNull] TScope scope, ImmutableArray resources, CancellationToken cancellationToken); /// /// Updates an existing scope. /// /// The scope to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs index 2b4cc317..6b79c69f 100644 --- a/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs +++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs @@ -5,6 +5,7 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -25,10 +26,10 @@ namespace OpenIddict.Abstractions /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications in the database. /// - Task CountAsync(CancellationToken cancellationToken); + ValueTask CountAsync(CancellationToken cancellationToken); /// /// Determines the number of tokens that match the specified query. @@ -37,28 +38,26 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of tokens that match the specified query. /// - Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken); + ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken); /// /// Creates a new token. /// /// The token to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task CreateAsync([NotNull] TToken token, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask CreateAsync([NotNull] TToken token, CancellationToken cancellationToken); /// /// Removes a token. /// /// The token to delete. /// The that can be used to abort the operation. - /// A that can be used to monitor the asynchronous operation. - Task DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken); /// /// Retrieves the tokens corresponding to the specified @@ -67,11 +66,8 @@ namespace OpenIddict.Abstractions /// The subject associated with the token. /// The client associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the subject/client. - /// - Task> FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken); + /// The tokens corresponding to the subject/client. + IAsyncEnumerable FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken); /// /// Retrieves the tokens matching the specified parameters. @@ -80,11 +76,8 @@ namespace OpenIddict.Abstractions /// The client associated with the token. /// The token status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - Task> FindAsync( + /// The tokens corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken); @@ -96,11 +89,8 @@ namespace OpenIddict.Abstractions /// The token status. /// The token type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - Task> FindAsync( + /// The tokens corresponding to the criteria. + IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken); @@ -109,22 +99,16 @@ namespace OpenIddict.Abstractions /// /// The application identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified application. - /// - Task> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + /// The tokens corresponding to the specified application. + IAsyncEnumerable FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// /// Retrieves the list of tokens corresponding to the specified authorization identifier. /// /// The authorization identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified authorization. - /// - Task> FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + /// The tokens corresponding to the specified authorization. + IAsyncEnumerable FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// /// Retrieves a token using its unique identifier. @@ -132,10 +116,10 @@ namespace OpenIddict.Abstractions /// The unique identifier associated with the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the token corresponding to the unique identifier. /// - Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// /// Retrieves the list of tokens corresponding to the specified reference identifier. @@ -144,21 +128,18 @@ namespace OpenIddict.Abstractions /// The reference identifier associated with the tokens. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the tokens corresponding to the specified reference identifier. /// - Task FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + ValueTask FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// /// Retrieves the list of tokens corresponding to the specified subject. /// /// The subject associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified subject. - /// - Task> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken); + /// The tokens corresponding to the specified subject. + IAsyncEnumerable FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken); /// /// Retrieves the optional application identifier associated with a token. @@ -180,10 +161,10 @@ namespace OpenIddict.Abstractions /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - Task GetAsync( + ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken); @@ -315,11 +296,8 @@ namespace OpenIddict.Abstractions /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken); + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken); /// /// Executes the specified query and returns all the corresponding elements. @@ -329,11 +307,8 @@ namespace OpenIddict.Abstractions /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - Task> ListAsync( + /// All the elements returned when executing the specified query. + IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken); @@ -341,10 +316,8 @@ namespace OpenIddict.Abstractions /// Removes the tokens that are marked as expired or invalid. /// /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task PruneAsync(CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask PruneAsync(CancellationToken cancellationToken); /// /// Sets the application identifier associated with a token. @@ -352,10 +325,8 @@ namespace OpenIddict.Abstractions /// The token. /// The unique identifier associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken); /// /// Sets the authorization identifier associated with a token. @@ -363,10 +334,8 @@ namespace OpenIddict.Abstractions /// The token. /// The unique identifier associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken); /// /// Sets the creation date associated with a token. @@ -374,10 +343,8 @@ namespace OpenIddict.Abstractions /// The token. /// The creation date. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetCreationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetCreationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken); /// /// Sets the expiration date associated with a token. @@ -385,10 +352,8 @@ namespace OpenIddict.Abstractions /// The token. /// The expiration date. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetExpirationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetExpirationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken); /// /// Sets the payload associated with a token. @@ -396,10 +361,8 @@ namespace OpenIddict.Abstractions /// The token. /// The payload associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken); /// /// Sets the additional properties associated with a token. @@ -407,10 +370,8 @@ namespace OpenIddict.Abstractions /// The token. /// The additional properties associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken); /// /// Sets the reference identifier associated with a token. @@ -420,10 +381,8 @@ namespace OpenIddict.Abstractions /// The token. /// The reference identifier associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken); /// /// Sets the status associated with a token. @@ -431,10 +390,8 @@ namespace OpenIddict.Abstractions /// The token. /// The status associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken); /// /// Sets the subject associated with a token. @@ -442,10 +399,8 @@ namespace OpenIddict.Abstractions /// The token. /// The subject associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken); /// /// Sets the token type associated with a token. @@ -453,19 +408,15 @@ namespace OpenIddict.Abstractions /// The token. /// The token type associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task SetTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask SetTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken); /// /// Updates an existing token. /// /// The token to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken); + /// A that can be used to monitor the asynchronous operation. + ValueTask UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/OpenIddict.Core/Caches/OpenIddictApplicationCache.cs b/src/OpenIddict.Core/Caches/OpenIddictApplicationCache.cs index 07dd3020..f4f11f5a 100644 --- a/src/OpenIddict.Core/Caches/OpenIddictApplicationCache.cs +++ b/src/OpenIddict.Core/Caches/OpenIddictApplicationCache.cs @@ -6,7 +6,9 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; @@ -45,10 +47,8 @@ namespace OpenIddict.Core /// /// The application to add to the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public async Task AddAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public async ValueTask AddAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { @@ -246,11 +246,8 @@ namespace OpenIddict.Core /// /// The post_logout_redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the client applications corresponding to the specified post_logout_redirect_uri. - /// - public ValueTask> FindByPostLogoutRedirectUriAsync( + /// The client applications corresponding to the specified post_logout_redirect_uri. + public IAsyncEnumerable FindByPostLogoutRedirectUriAsync( [NotNull] string address, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(address)) @@ -266,12 +263,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray applications)) { - return new ValueTask>(applications); + return applications.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var application in (applications = await _store.FindByPostLogoutRedirectUriAsync(address, cancellationToken))) + var applications = ImmutableArray.CreateRange(await _store.FindByPostLogoutRedirectUriAsync( + address, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var application in applications) { await AddAsync(application, cancellationToken); } @@ -293,10 +293,13 @@ namespace OpenIddict.Core entry.SetValue(applications); } - return applications; + foreach (var application in applications) + { + yield return application; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -304,11 +307,8 @@ namespace OpenIddict.Core /// /// The redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the client applications corresponding to the specified redirect_uri. - /// - public ValueTask> FindByRedirectUriAsync( + /// The client applications corresponding to the specified redirect_uri. + public IAsyncEnumerable FindByRedirectUriAsync( [NotNull] string address, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(address)) @@ -324,12 +324,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray applications)) { - return new ValueTask>(applications); + return applications.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var application in (applications = await _store.FindByRedirectUriAsync(address, cancellationToken))) + var applications = ImmutableArray.CreateRange(await _store.FindByRedirectUriAsync( + address, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var application in applications) { await AddAsync(application, cancellationToken); } @@ -351,10 +354,13 @@ namespace OpenIddict.Core entry.SetValue(applications); } - return applications; + foreach (var application in applications) + { + yield return application; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -362,10 +368,8 @@ namespace OpenIddict.Core /// /// The application to remove from the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public async Task RemoveAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public async ValueTask RemoveAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { @@ -391,10 +395,10 @@ namespace OpenIddict.Core /// The application associated with the expiration signal. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns an expiration signal for the specified application. /// - protected virtual async Task CreateExpirationSignalAsync( + protected virtual async ValueTask CreateExpirationSignalAsync( [NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) diff --git a/src/OpenIddict.Core/Caches/OpenIddictAuthorizationCache.cs b/src/OpenIddict.Core/Caches/OpenIddictAuthorizationCache.cs index 6509eedb..a8c68e19 100644 --- a/src/OpenIddict.Core/Caches/OpenIddictAuthorizationCache.cs +++ b/src/OpenIddict.Core/Caches/OpenIddictAuthorizationCache.cs @@ -6,7 +6,9 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; @@ -46,9 +48,9 @@ namespace OpenIddict.Core /// The authorization to add to the cache. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public async Task AddAsync(TAuthorization authorization, CancellationToken cancellationToken) + public async ValueTask AddAsync(TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { @@ -135,11 +137,8 @@ namespace OpenIddict.Core /// The subject associated with the authorization. /// The client associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the subject/client. - /// - public ValueTask> FindAsync( + /// The authorizations corresponding to the subject/client. + public IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -161,12 +160,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray authorizations)) { - return new ValueTask>(authorizations); + return authorizations.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var authorization in (authorizations = await _store.FindAsync(subject, client, cancellationToken))) + var authorizations = ImmutableArray.CreateRange(await _store.FindAsync( + subject, client, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var authorization in authorizations) { await AddAsync(authorization, cancellationToken); } @@ -188,10 +190,13 @@ namespace OpenIddict.Core entry.SetValue(authorizations); } - return authorizations; + foreach (var authorization in authorizations) + { + yield return authorization; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -201,11 +206,8 @@ namespace OpenIddict.Core /// The client associated with the authorization. /// The authorization status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public ValueTask> FindAsync( + /// The authorizations corresponding to the criteria. + public IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken) { @@ -234,12 +236,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray authorizations)) { - return new ValueTask>(authorizations); + return authorizations.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var authorization in (authorizations = await _store.FindAsync(subject, client, status, cancellationToken))) + var authorizations = ImmutableArray.CreateRange(await _store.FindAsync( + subject, client, status, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var authorization in authorizations) { await AddAsync(authorization, cancellationToken); } @@ -261,10 +266,13 @@ namespace OpenIddict.Core entry.SetValue(authorizations); } - return authorizations; + foreach (var authorization in authorizations) + { + yield return authorization; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -275,11 +283,8 @@ namespace OpenIddict.Core /// The authorization status. /// The authorization type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public ValueTask> FindAsync( + /// The authorizations corresponding to the criteria. + public IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken) { @@ -314,12 +319,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray authorizations)) { - return new ValueTask>(authorizations); + return authorizations.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var authorization in (authorizations = await _store.FindAsync(subject, client, status, type, cancellationToken))) + var authorizations = ImmutableArray.CreateRange(await _store.FindAsync( + subject, client, status, type, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var authorization in authorizations) { await AddAsync(authorization, cancellationToken); } @@ -341,10 +349,13 @@ namespace OpenIddict.Core entry.SetValue(authorizations); } - return authorizations; + foreach (var authorization in authorizations) + { + yield return authorization; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -356,11 +367,8 @@ namespace OpenIddict.Core /// The authorization type. /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public ValueTask> FindAsync( + /// The authorizations corresponding to the criteria. + public IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken) @@ -387,19 +395,17 @@ namespace OpenIddict.Core // Note: this method is only partially cached. - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - var authorizations = await _store.FindAsync(subject, client, status, type, scopes, cancellationToken); - - foreach (var authorization in authorizations) + await foreach (var authorization in _store.FindAsync(subject, client, status, type, scopes, cancellationToken)) { await AddAsync(authorization, cancellationToken); - } - return authorizations; + yield return authorization; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -407,11 +413,8 @@ namespace OpenIddict.Core /// /// The application identifier associated with the authorizations. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified application. - /// - public ValueTask> FindByApplicationIdAsync( + /// The authorizations corresponding to the specified application. + public IAsyncEnumerable FindByApplicationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) @@ -427,12 +430,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray authorizations)) { - return new ValueTask>(authorizations); + return authorizations.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var authorization in (authorizations = await _store.FindByApplicationIdAsync(identifier, cancellationToken))) + var authorizations = ImmutableArray.CreateRange(await _store.FindByApplicationIdAsync( + identifier, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var authorization in authorizations) { await AddAsync(authorization, cancellationToken); } @@ -454,10 +460,13 @@ namespace OpenIddict.Core entry.SetValue(authorizations); } - return authorizations; + foreach (var authorization in authorizations) + { + yield return authorization; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -522,11 +531,8 @@ namespace OpenIddict.Core /// /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified subject. - /// - public ValueTask> FindBySubjectAsync( + /// The authorizations corresponding to the specified subject. + public IAsyncEnumerable FindBySubjectAsync( [NotNull] string subject, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -542,12 +548,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray authorizations)) { - return new ValueTask>(authorizations); + return authorizations.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var authorization in (authorizations = await _store.FindBySubjectAsync(subject, cancellationToken))) + var authorizations = ImmutableArray.CreateRange(await _store.FindBySubjectAsync( + subject, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var authorization in authorizations) { await AddAsync(authorization, cancellationToken); } @@ -569,10 +578,13 @@ namespace OpenIddict.Core entry.SetValue(authorizations); } - return authorizations; + foreach (var authorization in authorizations) + { + yield return authorization; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -580,10 +592,8 @@ namespace OpenIddict.Core /// /// The authorization to remove from the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public async Task RemoveAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public async ValueTask RemoveAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { @@ -609,10 +619,10 @@ namespace OpenIddict.Core /// The authorization associated with the expiration signal. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns an expiration signal for the specified authorization. /// - protected virtual async Task CreateExpirationSignalAsync( + protected virtual async ValueTask CreateExpirationSignalAsync( [NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) diff --git a/src/OpenIddict.Core/Caches/OpenIddictScopeCache.cs b/src/OpenIddict.Core/Caches/OpenIddictScopeCache.cs index 710ce8e6..56d205f2 100644 --- a/src/OpenIddict.Core/Caches/OpenIddictScopeCache.cs +++ b/src/OpenIddict.Core/Caches/OpenIddictScopeCache.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -46,10 +47,8 @@ namespace OpenIddict.Core /// /// The scope to add to the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public async Task AddAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public async ValueTask AddAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { @@ -238,15 +237,12 @@ namespace OpenIddict.Core /// /// The names associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes corresponding to the specified names. - /// - public ValueTask> FindByNamesAsync(ImmutableArray names, CancellationToken cancellationToken) + /// The scopes corresponding to the specified names. + public IAsyncEnumerable FindByNamesAsync(ImmutableArray names, CancellationToken cancellationToken) { if (names.IsDefaultOrEmpty) { - return new ValueTask>(ImmutableArray.Create()); + return AsyncEnumerable.Empty(); } if (names.Any(name => string.IsNullOrEmpty(name))) @@ -256,19 +252,17 @@ namespace OpenIddict.Core // Note: this method is only partially cached. - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - var scopes = await _store.FindByNamesAsync(names, cancellationToken); - - foreach (var scope in scopes) + await foreach (var scope in _store.FindByNamesAsync(names, cancellationToken)) { await AddAsync(scope, cancellationToken); - } - return scopes; + yield return scope; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -276,11 +270,8 @@ namespace OpenIddict.Core /// /// The resource associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes associated with the specified resource. - /// - public ValueTask> FindByResourceAsync([NotNull] string resource, CancellationToken cancellationToken) + /// The scopes associated with the specified resource. + public IAsyncEnumerable FindByResourceAsync([NotNull] string resource, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(resource)) { @@ -295,12 +286,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray scopes)) { - return new ValueTask>(scopes); + return scopes.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var scope in (scopes = await _store.FindByResourceAsync(resource, cancellationToken))) + var scopes = ImmutableArray.CreateRange(await _store.FindByResourceAsync( + resource, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var scope in scopes) { await AddAsync(scope, cancellationToken); } @@ -322,10 +316,13 @@ namespace OpenIddict.Core entry.SetValue(scopes); } - return scopes; + foreach (var scope in scopes) + { + yield return scope; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -333,10 +330,8 @@ namespace OpenIddict.Core /// /// The scope to remove from the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public async Task RemoveAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public async ValueTask RemoveAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { @@ -362,10 +357,10 @@ namespace OpenIddict.Core /// The scope associated with the expiration signal. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns an expiration signal for the specified scope. /// - protected virtual async Task CreateExpirationSignalAsync([NotNull] TScope scope, CancellationToken cancellationToken) + protected virtual async ValueTask CreateExpirationSignalAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { diff --git a/src/OpenIddict.Core/Caches/OpenIddictTokenCache.cs b/src/OpenIddict.Core/Caches/OpenIddictTokenCache.cs index 1caef84e..dfffc62a 100644 --- a/src/OpenIddict.Core/Caches/OpenIddictTokenCache.cs +++ b/src/OpenIddict.Core/Caches/OpenIddictTokenCache.cs @@ -6,7 +6,9 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; @@ -45,10 +47,8 @@ namespace OpenIddict.Core /// /// The token to add to the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public async Task AddAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public async ValueTask AddAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { @@ -158,11 +158,8 @@ namespace OpenIddict.Core /// The subject associated with the token. /// The client associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the subject/client. - /// - public ValueTask> FindAsync([NotNull] string subject, + /// The tokens corresponding to the subject/client. + public IAsyncEnumerable FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -184,12 +181,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray tokens)) { - return new ValueTask>(tokens); + return tokens.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var token in (tokens = await _store.FindAsync(subject, client, cancellationToken))) + var tokens = ImmutableArray.CreateRange(await _store.FindAsync( + subject, client, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var token in tokens) { await AddAsync(token, cancellationToken); } @@ -211,10 +211,13 @@ namespace OpenIddict.Core entry.SetValue(tokens); } - return tokens; + foreach (var token in tokens) + { + yield return token; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -224,11 +227,8 @@ namespace OpenIddict.Core /// The client associated with the token. /// The token status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - public ValueTask> FindAsync( + /// The tokens corresponding to the criteria. + public IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken) { @@ -257,12 +257,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray tokens)) { - return new ValueTask>(tokens); + return tokens.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var token in (tokens = await _store.FindAsync(subject, client, status, cancellationToken))) + var tokens = ImmutableArray.CreateRange(await _store.FindAsync( + subject, client, status, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var token in tokens) { await AddAsync(token, cancellationToken); } @@ -284,10 +287,13 @@ namespace OpenIddict.Core entry.SetValue(tokens); } - return tokens; + foreach (var token in tokens) + { + yield return token; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -298,11 +304,8 @@ namespace OpenIddict.Core /// The token status. /// The token type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - public ValueTask> FindAsync( + /// The tokens corresponding to the criteria. + public IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken) { @@ -337,12 +340,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray tokens)) { - return new ValueTask>(tokens); + return tokens.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var token in (tokens = await _store.FindAsync(subject, client, status, type, cancellationToken))) + var tokens = ImmutableArray.CreateRange(await _store.FindAsync( + subject, client, status, type, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var token in tokens) { await AddAsync(token, cancellationToken); } @@ -364,10 +370,13 @@ namespace OpenIddict.Core entry.SetValue(tokens); } - return tokens; + foreach (var token in tokens) + { + yield return token; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -375,11 +384,8 @@ namespace OpenIddict.Core /// /// The application identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified application. - /// - public ValueTask> FindByApplicationIdAsync( + /// The tokens corresponding to the specified application. + public IAsyncEnumerable FindByApplicationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) @@ -395,12 +401,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray tokens)) { - return new ValueTask>(tokens); + return tokens.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var token in (tokens = await _store.FindByApplicationIdAsync(identifier, cancellationToken))) + var tokens = ImmutableArray.CreateRange(await _store.FindByApplicationIdAsync( + identifier, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var token in tokens) { await AddAsync(token, cancellationToken); } @@ -422,10 +431,13 @@ namespace OpenIddict.Core entry.SetValue(tokens); } - return tokens; + foreach (var token in tokens) + { + yield return token; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -433,11 +445,8 @@ namespace OpenIddict.Core /// /// The authorization identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified authorization. - /// - public ValueTask> FindByAuthorizationIdAsync( + /// The tokens corresponding to the specified authorization. + public IAsyncEnumerable FindByAuthorizationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) @@ -453,12 +462,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray tokens)) { - return new ValueTask>(tokens); + return tokens.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var token in (tokens = await _store.FindByAuthorizationIdAsync(identifier, cancellationToken))) + var tokens = ImmutableArray.CreateRange(await _store.FindByAuthorizationIdAsync( + identifier, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var token in tokens) { await AddAsync(token, cancellationToken); } @@ -480,10 +492,13 @@ namespace OpenIddict.Core entry.SetValue(tokens); } - return tokens; + foreach (var token in tokens) + { + yield return token; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -606,11 +621,8 @@ namespace OpenIddict.Core /// /// The subject associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified subject. - /// - public ValueTask> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken) + /// The tokens corresponding to the specified subject. + public IAsyncEnumerable FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) { @@ -625,12 +637,15 @@ namespace OpenIddict.Core if (_cache.TryGetValue(parameters, out ImmutableArray tokens)) { - return new ValueTask>(tokens); + return tokens.ToAsyncEnumerable(); } - async Task> ExecuteAsync() + async IAsyncEnumerable ExecuteAsync() { - foreach (var token in (tokens = await _store.FindBySubjectAsync(subject, cancellationToken))) + var tokens = ImmutableArray.CreateRange(await _store.FindBySubjectAsync( + subject, cancellationToken).ToListAsync(cancellationToken)); + + foreach (var token in tokens) { await AddAsync(token, cancellationToken); } @@ -652,10 +667,13 @@ namespace OpenIddict.Core entry.SetValue(tokens); } - return tokens; + foreach (var token in tokens) + { + yield return token; + } } - return new ValueTask>(ExecuteAsync()); + return ExecuteAsync(); } /// @@ -663,10 +681,8 @@ namespace OpenIddict.Core /// /// The token to remove from the cache. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public async Task RemoveAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public async ValueTask RemoveAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { @@ -692,10 +708,10 @@ namespace OpenIddict.Core /// The token associated with the expiration signal. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns an expiration signal for the specified token. /// - protected virtual async Task CreateExpirationSignalAsync([NotNull] TToken token, CancellationToken cancellationToken) + protected virtual async ValueTask CreateExpirationSignalAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { diff --git a/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs index 9437635a..89d63edd 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs @@ -5,9 +5,11 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -62,10 +64,10 @@ namespace OpenIddict.Core /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications in the database. /// - public virtual Task CountAsync(CancellationToken cancellationToken = default) + public virtual ValueTask CountAsync(CancellationToken cancellationToken = default) => Store.CountAsync(cancellationToken); /// @@ -75,10 +77,10 @@ namespace OpenIddict.Core /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications that match the specified query. /// - public virtual Task CountAsync( + public virtual ValueTask CountAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default) { if (query == null) @@ -95,9 +97,9 @@ namespace OpenIddict.Core /// The application to create. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken = default) + public virtual ValueTask CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken = default) => CreateAsync(application, secret: null, cancellationToken); /// @@ -109,9 +111,9 @@ namespace OpenIddict.Core /// The client secret associated with the application, if applicable. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task CreateAsync( + public virtual async ValueTask CreateAsync( [NotNull] TApplication application, [CanBeNull] string secret, CancellationToken cancellationToken = default) { @@ -141,7 +143,7 @@ namespace OpenIddict.Core await Store.SetClientSecretAsync(application, secret, cancellationToken); } - var results = await ValidateAsync(application, cancellationToken); + var results = await ValidateAsync(application, cancellationToken).ToListAsync(cancellationToken); if (results.Any(result => result != ValidationResult.Success)) { var builder = new StringBuilder(); @@ -153,7 +155,7 @@ namespace OpenIddict.Core builder.AppendLine(result.ErrorMessage); } - throw new OpenIddictExceptions.ValidationException(builder.ToString(), results); + throw new OpenIddictExceptions.ValidationException(builder.ToString(), results.ToImmutableArray()); } await Store.CreateAsync(application, cancellationToken); @@ -172,10 +174,10 @@ namespace OpenIddict.Core /// The application descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the unique identifier associated with the application. /// - public virtual async Task CreateAsync( + public virtual async ValueTask CreateAsync( [NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default) { if (descriptor == null) @@ -211,9 +213,9 @@ namespace OpenIddict.Core /// The application to delete. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken = default) + public virtual async ValueTask DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken = default) { if (application == null) { @@ -234,10 +236,10 @@ namespace OpenIddict.Core /// The client identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - public virtual async Task FindByClientIdAsync( + public virtual async ValueTask FindByClientIdAsync( [NotNull] string identifier, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(identifier)) @@ -272,10 +274,10 @@ namespace OpenIddict.Core /// The unique identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - public virtual async Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default) + public virtual async ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(identifier)) { @@ -308,11 +310,8 @@ namespace OpenIddict.Core /// /// The post_logout_redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified post_logout_redirect_uri. - /// - public virtual async Task> FindByPostLogoutRedirectUriAsync( + /// The client applications corresponding to the specified post_logout_redirect_uri. + public virtual IAsyncEnumerable FindByPostLogoutRedirectUriAsync( [NotNull] string address, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(address)) @@ -321,13 +320,8 @@ namespace OpenIddict.Core } var applications = Options.CurrentValue.DisableEntityCaching ? - await Store.FindByPostLogoutRedirectUriAsync(address, cancellationToken) : - await Cache.FindByPostLogoutRedirectUriAsync(address, cancellationToken); - - if (applications.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindByPostLogoutRedirectUriAsync(address, cancellationToken) : + Cache.FindByPostLogoutRedirectUriAsync(address, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -338,23 +332,8 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(applications.Length); - - foreach (var application in applications) - { - foreach (var uri in await Store.GetPostLogoutRedirectUrisAsync(application, cancellationToken)) - { - // Note: the post_logout_redirect_uri must be compared using case-sensitive "Simple String Comparison". - if (string.Equals(uri, address, StringComparison.Ordinal)) - { - builder.Add(application); - } - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return applications.WhereAwait(async application => + (await Store.GetPostLogoutRedirectUrisAsync(application, cancellationToken)).Contains(address, StringComparer.Ordinal)); } /// @@ -362,11 +341,8 @@ namespace OpenIddict.Core /// /// The redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified redirect_uri. - /// - public virtual async Task> FindByRedirectUriAsync( + /// The client applications corresponding to the specified redirect_uri. + public virtual IAsyncEnumerable FindByRedirectUriAsync( [NotNull] string address, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(address)) @@ -375,13 +351,8 @@ namespace OpenIddict.Core } var applications = Options.CurrentValue.DisableEntityCaching ? - await Store.FindByRedirectUriAsync(address, cancellationToken) : - await Cache.FindByRedirectUriAsync(address, cancellationToken); - - if (applications.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindByRedirectUriAsync(address, cancellationToken) : + Cache.FindByRedirectUriAsync(address, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -392,23 +363,8 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(applications.Length); - - foreach (var application in applications) - { - foreach (var uri in await Store.GetRedirectUrisAsync(application, cancellationToken)) - { - // Note: the post_logout_redirect_uri must be compared using case-sensitive "Simple String Comparison". - if (string.Equals(uri, address, StringComparison.Ordinal)) - { - builder.Add(application); - } - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return applications.WhereAwait(async application => + (await Store.GetRedirectUrisAsync(application, cancellationToken)).Contains(address, StringComparer.Ordinal)); } /// @@ -418,10 +374,10 @@ namespace OpenIddict.Core /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default) { if (query == null) @@ -441,10 +397,10 @@ namespace OpenIddict.Core /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default) { @@ -628,7 +584,7 @@ namespace OpenIddict.Core /// The permission. /// The that can be used to abort the operation. /// true if the application has been granted the specified permission, false otherwise. - public virtual async Task HasPermissionAsync( + public virtual async ValueTask HasPermissionAsync( [NotNull] TApplication application, [NotNull] string permission, CancellationToken cancellationToken = default) { if (application == null) @@ -641,7 +597,7 @@ namespace OpenIddict.Core throw new ArgumentException("The permission name cannot be null or empty.", nameof(permission)); } - return (await GetPermissionsAsync(application, cancellationToken)).Contains(permission); + return (await GetPermissionsAsync(application, cancellationToken)).Contains(permission, StringComparer.OrdinalIgnoreCase); } /// @@ -650,7 +606,7 @@ namespace OpenIddict.Core /// The application. /// The that can be used to abort the operation. /// true if the application is a confidential client, false otherwise. - public async Task IsConfidentialAsync([NotNull] TApplication application, CancellationToken cancellationToken = default) + public async ValueTask IsConfidentialAsync([NotNull] TApplication application, CancellationToken cancellationToken = default) { if (application == null) { @@ -672,7 +628,7 @@ namespace OpenIddict.Core /// The application. /// The that can be used to abort the operation. /// true if the application is a hybrid client, false otherwise. - public async Task IsHybridAsync([NotNull] TApplication application, CancellationToken cancellationToken = default) + public async ValueTask IsHybridAsync([NotNull] TApplication application, CancellationToken cancellationToken = default) { if (application == null) { @@ -694,7 +650,7 @@ namespace OpenIddict.Core /// The application. /// The that can be used to abort the operation. /// true if the application is a public client, false otherwise. - public async Task IsPublicAsync([NotNull] TApplication application, CancellationToken cancellationToken = default) + public async ValueTask IsPublicAsync([NotNull] TApplication application, CancellationToken cancellationToken = default) { if (application == null) { @@ -717,15 +673,10 @@ namespace OpenIddict.Core /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [CanBeNull] int? count = null, [CanBeNull] int? offset = null, CancellationToken cancellationToken = default) - { - return Store.ListAsync(count, offset, cancellationToken); - } + => Store.ListAsync(count, offset, cancellationToken); /// /// Executes the specified query and returns all the corresponding elements. @@ -733,11 +684,8 @@ namespace OpenIddict.Core /// The result type. /// The query to execute. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default) { if (query == null) @@ -756,11 +704,8 @@ namespace OpenIddict.Core /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default) { @@ -779,9 +724,9 @@ namespace OpenIddict.Core /// The descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task PopulateAsync([NotNull] TApplication application, + public virtual async ValueTask PopulateAsync([NotNull] TApplication application, [NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default) { if (application == null) @@ -813,9 +758,9 @@ namespace OpenIddict.Core /// The application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task PopulateAsync( + public virtual async ValueTask PopulateAsync( [NotNull] OpenIddictApplicationDescriptor descriptor, [NotNull] TApplication application, CancellationToken cancellationToken = default) { @@ -880,16 +825,16 @@ namespace OpenIddict.Core /// The application to update. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken = default) + public virtual async ValueTask UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken = default) { if (application == null) { throw new ArgumentNullException(nameof(application)); } - var results = await ValidateAsync(application, cancellationToken); + var results = await ValidateAsync(application, cancellationToken).ToListAsync(cancellationToken); if (results.Any(result => result != ValidationResult.Success)) { var builder = new StringBuilder(); @@ -901,7 +846,7 @@ namespace OpenIddict.Core builder.AppendLine(result.ErrorMessage); } - throw new OpenIddictExceptions.ValidationException(builder.ToString(), results); + throw new OpenIddictExceptions.ValidationException(builder.ToString(), results.ToImmutableArray()); } await Store.UpdateAsync(application, cancellationToken); @@ -922,9 +867,9 @@ namespace OpenIddict.Core /// The client secret associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task UpdateAsync([NotNull] TApplication application, + public virtual async ValueTask UpdateAsync([NotNull] TApplication application, [CanBeNull] string secret, CancellationToken cancellationToken = default) { if (application == null) @@ -953,9 +898,9 @@ namespace OpenIddict.Core /// The descriptor used to update the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task UpdateAsync([NotNull] TApplication application, + public virtual async ValueTask UpdateAsync([NotNull] TApplication application, [NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default) { if (application == null) @@ -989,25 +934,20 @@ namespace OpenIddict.Core /// /// The application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the validation error encountered when validating the application. - /// - public virtual async Task> ValidateAsync( - [NotNull] TApplication application, CancellationToken cancellationToken = default) + /// The validation error encountered when validating the application. + public virtual async IAsyncEnumerable ValidateAsync( + [NotNull] TApplication application, [EnumeratorCancellation] CancellationToken cancellationToken = default) { if (application == null) { throw new ArgumentNullException(nameof(application)); } - var builder = ImmutableArray.CreateBuilder(); - // Ensure the client_id is not null or empty and is not already used for a different application. var identifier = await Store.GetClientIdAsync(application, cancellationToken); if (string.IsNullOrEmpty(identifier)) { - builder.Add(new ValidationResult("The client identifier cannot be null or empty.")); + yield return new ValidationResult("The client identifier cannot be null or empty."); } else @@ -1021,14 +961,14 @@ namespace OpenIddict.Core await Store.GetIdAsync(other, cancellationToken), await Store.GetIdAsync(application, cancellationToken), StringComparison.Ordinal)) { - builder.Add(new ValidationResult("An application with the same client identifier already exists.")); + yield return new ValidationResult("An application with the same client identifier already exists."); } } var type = await Store.GetClientTypeAsync(application, cancellationToken); if (string.IsNullOrEmpty(type)) { - builder.Add(new ValidationResult("The client type cannot be null or empty.")); + yield return new ValidationResult("The client type cannot be null or empty."); } else @@ -1038,8 +978,8 @@ namespace OpenIddict.Core !string.Equals(type, OpenIddictConstants.ClientTypes.Hybrid, StringComparison.OrdinalIgnoreCase) && !string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { - builder.Add(new ValidationResult("Only 'confidential', 'hybrid' or 'public' applications are " + - "supported by the default application manager.")); + yield return new ValidationResult("Only 'confidential', 'hybrid' or 'public' applications are " + + "supported by the default application manager."); } // Ensure a client secret was specified if the client is a confidential application. @@ -1047,14 +987,14 @@ namespace OpenIddict.Core if (string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase)) { - builder.Add(new ValidationResult("The client secret cannot be null or empty for a confidential application.")); + yield return new ValidationResult("The client secret cannot be null or empty for a confidential application."); } // Ensure no client secret was specified if the client is a public application. else if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { - builder.Add(new ValidationResult("A client secret cannot be associated with a public application.")); + yield return new ValidationResult("A client secret cannot be associated with a public application."); } } @@ -1067,7 +1007,7 @@ namespace OpenIddict.Core // Ensure the address is not null or empty. if (string.IsNullOrEmpty(address)) { - builder.Add(new ValidationResult("Callback URLs cannot be null or empty.")); + yield return new ValidationResult("Callback URLs cannot be null or empty."); break; } @@ -1075,7 +1015,7 @@ namespace OpenIddict.Core // Ensure the address is a valid absolute URL. if (!Uri.TryCreate(address, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString()) { - builder.Add(new ValidationResult("Callback URLs must be valid absolute URLs.")); + yield return new ValidationResult("Callback URLs must be valid absolute URLs."); break; } @@ -1083,15 +1023,11 @@ namespace OpenIddict.Core // Ensure the address doesn't contain a fragment. if (!string.IsNullOrEmpty(uri.Fragment)) { - builder.Add(new ValidationResult("Callback URLs cannot contain a fragment.")); + yield return new ValidationResult("Callback URLs cannot contain a fragment."); break; } } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); } /// @@ -1100,12 +1036,12 @@ namespace OpenIddict.Core /// The application. /// The secret that should be compared to the client_secret stored in the database. /// The that can be used to abort the operation. - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns a boolean indicating whether the client secret was valid. /// - public virtual async Task ValidateClientSecretAsync( + public virtual async ValueTask ValidateClientSecretAsync( [NotNull] TApplication application, string secret, CancellationToken cancellationToken = default) { if (application == null) @@ -1147,10 +1083,10 @@ namespace OpenIddict.Core /// The address that should be compared to one of the redirect_uri stored in the database. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns a boolean indicating whether the redirect_uri was valid. /// - public virtual async Task ValidateRedirectUriAsync( + public virtual async ValueTask ValidateRedirectUriAsync( [NotNull] TApplication application, [NotNull] string address, CancellationToken cancellationToken = default) { if (application == null) @@ -1186,16 +1122,16 @@ namespace OpenIddict.Core /// The client secret. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - protected virtual Task ObfuscateClientSecretAsync([NotNull] string secret, CancellationToken cancellationToken = default) + protected virtual ValueTask ObfuscateClientSecretAsync([NotNull] string secret, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(secret)) { throw new ArgumentException("The secret cannot be null or empty.", nameof(secret)); } - return Task.FromResult(Crypto.HashPassword(secret)); + return new ValueTask(Crypto.HashPassword(secret)); } /// @@ -1206,10 +1142,10 @@ namespace OpenIddict.Core /// The value stored in the database, which is usually a hashed representation of the secret. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns a boolean indicating whether the specified value was valid. /// - protected virtual Task ValidateClientSecretAsync( + protected virtual ValueTask ValidateClientSecretAsync( [NotNull] string secret, [NotNull] string comparand, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(secret)) @@ -1224,7 +1160,7 @@ namespace OpenIddict.Core try { - return Task.FromResult(Crypto.VerifyHashedPassword(comparand, secret)); + return new ValueTask(Crypto.VerifyHashedPassword(comparand, secret)); } catch (Exception exception) @@ -1232,44 +1168,44 @@ namespace OpenIddict.Core Logger.LogWarning(exception, "An error occurred while trying to verify a client secret. " + "This may indicate that the hashed entry is corrupted or malformed."); - return Task.FromResult(false); + return new ValueTask(false); } } - Task IOpenIddictApplicationManager.CountAsync(CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.CountAsync(CancellationToken cancellationToken) => CountAsync(cancellationToken); - Task IOpenIddictApplicationManager.CountAsync(Func, IQueryable> query, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.CountAsync(Func, IQueryable> query, CancellationToken cancellationToken) => CountAsync(query, cancellationToken); - async Task IOpenIddictApplicationManager.CreateAsync(OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken) + async ValueTask IOpenIddictApplicationManager.CreateAsync(OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken) => await CreateAsync(descriptor, cancellationToken); - Task IOpenIddictApplicationManager.CreateAsync(object application, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.CreateAsync(object application, CancellationToken cancellationToken) => CreateAsync((TApplication) application, cancellationToken); - Task IOpenIddictApplicationManager.CreateAsync(object application, string secret, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.CreateAsync(object application, string secret, CancellationToken cancellationToken) => CreateAsync((TApplication) application, secret, cancellationToken); - Task IOpenIddictApplicationManager.DeleteAsync(object application, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.DeleteAsync(object application, CancellationToken cancellationToken) => DeleteAsync((TApplication) application, cancellationToken); - async Task IOpenIddictApplicationManager.FindByClientIdAsync(string identifier, CancellationToken cancellationToken) + async ValueTask IOpenIddictApplicationManager.FindByClientIdAsync(string identifier, CancellationToken cancellationToken) => await FindByClientIdAsync(identifier, cancellationToken); - async Task IOpenIddictApplicationManager.FindByIdAsync(string identifier, CancellationToken cancellationToken) + async ValueTask IOpenIddictApplicationManager.FindByIdAsync(string identifier, CancellationToken cancellationToken) => await FindByIdAsync(identifier, cancellationToken); - async Task> IOpenIddictApplicationManager.FindByPostLogoutRedirectUriAsync(string address, CancellationToken cancellationToken) - => (await FindByPostLogoutRedirectUriAsync(address, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictApplicationManager.FindByPostLogoutRedirectUriAsync(string address, CancellationToken cancellationToken) + => FindByPostLogoutRedirectUriAsync(address, cancellationToken).OfType(); - async Task> IOpenIddictApplicationManager.FindByRedirectUriAsync(string address, CancellationToken cancellationToken) - => (await FindByRedirectUriAsync(address, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictApplicationManager.FindByRedirectUriAsync(string address, CancellationToken cancellationToken) + => FindByRedirectUriAsync(address, cancellationToken).OfType(); - Task IOpenIddictApplicationManager.GetAsync(Func, IQueryable> query, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.GetAsync(Func, IQueryable> query, CancellationToken cancellationToken) => GetAsync(query, cancellationToken); - Task IOpenIddictApplicationManager.GetAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.GetAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) => GetAsync(query, state, cancellationToken); ValueTask IOpenIddictApplicationManager.GetClientIdAsync(object application, CancellationToken cancellationToken) @@ -1296,49 +1232,49 @@ namespace OpenIddict.Core ValueTask> IOpenIddictApplicationManager.GetRedirectUrisAsync(object application, CancellationToken cancellationToken) => GetRedirectUrisAsync((TApplication) application, cancellationToken); - Task IOpenIddictApplicationManager.HasPermissionAsync(object application, string permission, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.HasPermissionAsync(object application, string permission, CancellationToken cancellationToken) => HasPermissionAsync((TApplication) application, permission, cancellationToken); - Task IOpenIddictApplicationManager.IsConfidentialAsync(object application, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.IsConfidentialAsync(object application, CancellationToken cancellationToken) => IsConfidentialAsync((TApplication) application, cancellationToken); - Task IOpenIddictApplicationManager.IsHybridAsync(object application, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.IsHybridAsync(object application, CancellationToken cancellationToken) => IsHybridAsync((TApplication) application, cancellationToken); - Task IOpenIddictApplicationManager.IsPublicAsync(object application, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.IsPublicAsync(object application, CancellationToken cancellationToken) => IsPublicAsync((TApplication) application, cancellationToken); - async Task> IOpenIddictApplicationManager.ListAsync(int? count, int? offset, CancellationToken cancellationToken) - => (await ListAsync(count, offset, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictApplicationManager.ListAsync(int? count, int? offset, CancellationToken cancellationToken) + => ListAsync(count, offset, cancellationToken).OfType(); - Task> IOpenIddictApplicationManager.ListAsync(Func, IQueryable> query, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictApplicationManager.ListAsync(Func, IQueryable> query, CancellationToken cancellationToken) => ListAsync(query, cancellationToken); - Task> IOpenIddictApplicationManager.ListAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictApplicationManager.ListAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) => ListAsync(query, state, cancellationToken); - Task IOpenIddictApplicationManager.PopulateAsync(OpenIddictApplicationDescriptor descriptor, object application, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.PopulateAsync(OpenIddictApplicationDescriptor descriptor, object application, CancellationToken cancellationToken) => PopulateAsync(descriptor, (TApplication) application, cancellationToken); - Task IOpenIddictApplicationManager.PopulateAsync(object application, OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.PopulateAsync(object application, OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken) => PopulateAsync((TApplication) application, descriptor, cancellationToken); - Task IOpenIddictApplicationManager.UpdateAsync(object application, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.UpdateAsync(object application, CancellationToken cancellationToken) => UpdateAsync((TApplication) application, cancellationToken); - Task IOpenIddictApplicationManager.UpdateAsync(object application, OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.UpdateAsync(object application, OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken) => UpdateAsync((TApplication) application, descriptor, cancellationToken); - Task IOpenIddictApplicationManager.UpdateAsync(object application, string secret, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.UpdateAsync(object application, string secret, CancellationToken cancellationToken) => UpdateAsync((TApplication) application, secret, cancellationToken); - Task> IOpenIddictApplicationManager.ValidateAsync(object application, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictApplicationManager.ValidateAsync(object application, CancellationToken cancellationToken) => ValidateAsync((TApplication) application, cancellationToken); - Task IOpenIddictApplicationManager.ValidateClientSecretAsync(object application, string secret, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.ValidateClientSecretAsync(object application, string secret, CancellationToken cancellationToken) => ValidateClientSecretAsync((TApplication) application, secret, cancellationToken); - Task IOpenIddictApplicationManager.ValidateRedirectUriAsync(object application, string address, CancellationToken cancellationToken) + ValueTask IOpenIddictApplicationManager.ValidateRedirectUriAsync(object application, string address, CancellationToken cancellationToken) => ValidateRedirectUriAsync((TApplication) application, address, cancellationToken); } } \ No newline at end of file diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs index 7d64c392..f20ef182 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs @@ -5,9 +5,11 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -61,10 +63,10 @@ namespace OpenIddict.Core /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations in the database. /// - public virtual Task CountAsync(CancellationToken cancellationToken = default) + public virtual ValueTask CountAsync(CancellationToken cancellationToken = default) => Store.CountAsync(cancellationToken); /// @@ -74,10 +76,10 @@ namespace OpenIddict.Core /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations that match the specified query. /// - public virtual Task CountAsync( + public virtual ValueTask CountAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default) { if (query == null) @@ -94,9 +96,9 @@ namespace OpenIddict.Core /// The application to create. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) + public virtual async ValueTask CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) { if (authorization == null) { @@ -109,7 +111,7 @@ namespace OpenIddict.Core await Store.SetStatusAsync(authorization, OpenIddictConstants.Statuses.Valid, cancellationToken); } - var results = await ValidateAsync(authorization, cancellationToken); + var results = await ValidateAsync(authorization, cancellationToken).ToListAsync(cancellationToken); if (results.Any(result => result != ValidationResult.Success)) { var builder = new StringBuilder(); @@ -121,7 +123,7 @@ namespace OpenIddict.Core builder.AppendLine(result.ErrorMessage); } - throw new OpenIddictExceptions.ValidationException(builder.ToString(), results); + throw new OpenIddictExceptions.ValidationException(builder.ToString(), results.ToImmutableArray()); } await Store.CreateAsync(authorization, cancellationToken); @@ -138,9 +140,9 @@ namespace OpenIddict.Core /// The authorization descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, whose result returns the authorization. + /// A that can be used to monitor the asynchronous operation, whose result returns the authorization. /// - public virtual async Task CreateAsync( + public virtual async ValueTask CreateAsync( [NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default) { if (descriptor == null) @@ -170,9 +172,9 @@ namespace OpenIddict.Core /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, whose result returns the authorization. + /// A that can be used to monitor the asynchronous operation, whose result returns the authorization. /// - public virtual Task CreateAsync( + public virtual ValueTask CreateAsync( [NotNull] ImmutableDictionary claims, [NotNull] string subject, [NotNull] string client, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken = default) { @@ -220,9 +222,9 @@ namespace OpenIddict.Core /// The authorization to delete. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) + public virtual async ValueTask DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) { if (authorization == null) { @@ -244,11 +246,8 @@ namespace OpenIddict.Core /// The subject associated with the authorization. /// The client associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the subject/client. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the subject/client. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(subject)) @@ -262,13 +261,8 @@ namespace OpenIddict.Core } var authorizations = Options.CurrentValue.DisableEntityCaching ? - await Store.FindAsync(subject, client, cancellationToken) : - await Cache.FindAsync(subject, client, cancellationToken); - - if (authorizations.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindAsync(subject, client, cancellationToken) : + Cache.FindAsync(subject, client, cancellationToken); // SQL engines like Microsoft SQL Server or MySQL are known to use case-insensitive lookups by default. // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation @@ -279,19 +273,8 @@ namespace OpenIddict.Core return authorizations; } - var builder = ImmutableArray.CreateBuilder(authorizations.Length); - - foreach (var authorization in authorizations) - { - if (string.Equals(await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)) - { - builder.Add(authorization); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return authorizations.WhereAwait(async authorization => string.Equals( + await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)); } /// @@ -301,11 +284,8 @@ namespace OpenIddict.Core /// The client associated with the authorization. /// The authorization status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken = default) { @@ -325,13 +305,8 @@ namespace OpenIddict.Core } var authorizations = Options.CurrentValue.DisableEntityCaching ? - await Store.FindAsync(subject, client, status, cancellationToken) : - await Cache.FindAsync(subject, client, status, cancellationToken); - - if (authorizations.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindAsync(subject, client, status, cancellationToken) : + Cache.FindAsync(subject, client, status, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -342,19 +317,8 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(authorizations.Length); - - foreach (var authorization in authorizations) - { - if (string.Equals(await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)) - { - builder.Add(authorization); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return authorizations.WhereAwait(async authorization => string.Equals( + await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)); } /// @@ -365,11 +329,8 @@ namespace OpenIddict.Core /// The authorization status. /// The authorization type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken = default) { @@ -394,36 +355,16 @@ namespace OpenIddict.Core } var authorizations = Options.CurrentValue.DisableEntityCaching ? - await Store.FindAsync(subject, client, status, type, cancellationToken) : - await Cache.FindAsync(subject, client, status, type, cancellationToken); - - if (authorizations.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindAsync(subject, client, status, type, cancellationToken) : + Cache.FindAsync(subject, client, status, type, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { return authorizations; } - // SQL engines like Microsoft SQL Server or MySQL are known to use case-insensitive lookups by default. - // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation - // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - - var builder = ImmutableArray.CreateBuilder(authorizations.Length); - - foreach (var authorization in authorizations) - { - if (string.Equals(await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)) - { - builder.Add(authorization); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return authorizations.WhereAwait(async authorization => string.Equals( + await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)); } /// @@ -435,11 +376,8 @@ namespace OpenIddict.Core /// The authorization type. /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken = default) @@ -465,13 +403,8 @@ namespace OpenIddict.Core } var authorizations = Options.CurrentValue.DisableEntityCaching ? - await Store.FindAsync(subject, client, status, type, scopes, cancellationToken) : - await Cache.FindAsync(subject, client, status, type, scopes, cancellationToken); - - if (authorizations.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindAsync(subject, client, status, type, scopes, cancellationToken) : + Cache.FindAsync(subject, client, status, type, scopes, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -482,20 +415,9 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(authorizations.Length); - - foreach (var authorization in authorizations) - { - if (string.Equals(await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal) - && await HasScopesAsync(authorization, scopes, cancellationToken)) - { - builder.Add(authorization); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return authorizations.WhereAwait(async authorization => string.Equals( + await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal) && + await HasScopesAsync(authorization, scopes, cancellationToken)); } /// @@ -503,11 +425,8 @@ namespace OpenIddict.Core /// /// The application identifier associated with the authorizations. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified application. - /// - public virtual async Task> FindByApplicationIdAsync( + /// The authorizations corresponding to the specified application. + public virtual IAsyncEnumerable FindByApplicationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(identifier)) @@ -516,13 +435,8 @@ namespace OpenIddict.Core } var authorizations = Options.CurrentValue.DisableEntityCaching ? - await Store.FindByApplicationIdAsync(identifier, cancellationToken) : - await Cache.FindByApplicationIdAsync(identifier, cancellationToken); - - if (authorizations.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindByApplicationIdAsync(identifier, cancellationToken) : + Cache.FindByApplicationIdAsync(identifier, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -533,19 +447,8 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(authorizations.Length); - - foreach (var authorization in authorizations) - { - if (string.Equals(await Store.GetApplicationIdAsync(authorization, cancellationToken), identifier, StringComparison.Ordinal)) - { - builder.Add(authorization); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return authorizations.WhereAwait(async authorization => string.Equals( + await Store.GetApplicationIdAsync(authorization, cancellationToken), identifier, StringComparison.Ordinal)); } /// @@ -554,10 +457,10 @@ namespace OpenIddict.Core /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the authorization corresponding to the identifier. /// - public virtual async Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default) + public virtual async ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(identifier)) { @@ -590,11 +493,8 @@ namespace OpenIddict.Core /// /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified subject. - /// - public virtual async Task> FindBySubjectAsync( + /// The authorizations corresponding to the specified subject. + public virtual IAsyncEnumerable FindBySubjectAsync( [NotNull] string subject, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(subject)) @@ -603,13 +503,8 @@ namespace OpenIddict.Core } var authorizations = Options.CurrentValue.DisableEntityCaching ? - await Store.FindBySubjectAsync(subject, cancellationToken) : - await Cache.FindBySubjectAsync(subject, cancellationToken); - - if (authorizations.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindBySubjectAsync(subject, cancellationToken) : + Cache.FindBySubjectAsync(subject, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -620,19 +515,8 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(authorizations.Length); - - foreach (var authorization in authorizations) - { - if (string.Equals(await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)) - { - builder.Add(authorization); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return authorizations.WhereAwait(async authorization => string.Equals( + await Store.GetSubjectAsync(authorization, cancellationToken), subject, StringComparison.Ordinal)); } /// @@ -662,10 +546,10 @@ namespace OpenIddict.Core /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default) { if (query == null) @@ -685,10 +569,10 @@ namespace OpenIddict.Core /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default) { @@ -806,7 +690,7 @@ namespace OpenIddict.Core /// The scopes. /// The that can be used to abort the operation. /// true if the scopes are included in the authorization, false otherwise. - public virtual async Task HasScopesAsync([NotNull] TAuthorization authorization, + public virtual async ValueTask HasScopesAsync([NotNull] TAuthorization authorization, ImmutableArray scopes, CancellationToken cancellationToken = default) { if (authorization == null) @@ -825,7 +709,7 @@ namespace OpenIddict.Core /// The authorization. /// The that can be used to abort the operation. /// true if the authorization is ad hoc, false otherwise. - public async Task IsAdHocAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) + public async ValueTask IsAdHocAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) { if (authorization == null) { @@ -847,7 +731,7 @@ namespace OpenIddict.Core /// The authorization. /// The that can be used to abort the operation. /// true if the authorization is permanent, false otherwise. - public async Task IsPermanentAsync( + public async ValueTask IsPermanentAsync( [NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) { if (authorization == null) @@ -870,7 +754,7 @@ namespace OpenIddict.Core /// The authorization. /// The that can be used to abort the operation. /// true if the authorization has been revoked, false otherwise. - public virtual async Task IsRevokedAsync( + public virtual async ValueTask IsRevokedAsync( [NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) { if (authorization == null) @@ -893,7 +777,7 @@ namespace OpenIddict.Core /// The authorization. /// The that can be used to abort the operation. /// true if the authorization is valid, false otherwise. - public virtual async Task IsValidAsync( + public virtual async ValueTask IsValidAsync( [NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) { if (authorization == null) @@ -916,15 +800,10 @@ namespace OpenIddict.Core /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [CanBeNull] int? count = null, [CanBeNull] int? offset = null, CancellationToken cancellationToken = default) - { - return Store.ListAsync(count, offset, cancellationToken); - } + => Store.ListAsync(count, offset, cancellationToken); /// /// Executes the specified query and returns all the corresponding elements. @@ -932,11 +811,8 @@ namespace OpenIddict.Core /// The result type. /// The query to execute. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default) { if (query == null) @@ -955,11 +831,8 @@ namespace OpenIddict.Core /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default) { @@ -978,9 +851,9 @@ namespace OpenIddict.Core /// The descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task PopulateAsync([NotNull] TAuthorization authorization, + public virtual async ValueTask PopulateAsync([NotNull] TAuthorization authorization, [NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default) { if (authorization == null) @@ -1007,9 +880,9 @@ namespace OpenIddict.Core /// The authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task PopulateAsync( + public virtual async ValueTask PopulateAsync( [NotNull] OpenIddictAuthorizationDescriptor descriptor, [NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) { @@ -1036,9 +909,9 @@ namespace OpenIddict.Core /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task PruneAsync(CancellationToken cancellationToken = default) + public virtual ValueTask PruneAsync(CancellationToken cancellationToken = default) => Store.PruneAsync(cancellationToken); /// @@ -1046,8 +919,8 @@ namespace OpenIddict.Core /// /// The authorization to revoke. /// The that can be used to abort the operation. - /// A that can be used to monitor the asynchronous operation. - public virtual async Task RevokeAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask RevokeAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) { if (authorization == null) { @@ -1069,9 +942,9 @@ namespace OpenIddict.Core /// The unique identifier associated with the client application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task SetApplicationIdAsync( + public virtual async ValueTask SetApplicationIdAsync( [NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken = default) { if (authorization == null) @@ -1089,16 +962,16 @@ namespace OpenIddict.Core /// The authorization to update. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) + public virtual async ValueTask UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) { if (authorization == null) { throw new ArgumentNullException(nameof(authorization)); } - var results = await ValidateAsync(authorization, cancellationToken); + var results = await ValidateAsync(authorization, cancellationToken).ToListAsync(cancellationToken); if (results.Any(result => result != ValidationResult.Success)) { var builder = new StringBuilder(); @@ -1110,7 +983,7 @@ namespace OpenIddict.Core builder.AppendLine(result.ErrorMessage); } - throw new OpenIddictExceptions.ValidationException(builder.ToString(), results); + throw new OpenIddictExceptions.ValidationException(builder.ToString(), results.ToImmutableArray()); } await Store.UpdateAsync(authorization, cancellationToken); @@ -1129,9 +1002,9 @@ namespace OpenIddict.Core /// The descriptor used to update the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task UpdateAsync([NotNull] TAuthorization authorization, + public virtual async ValueTask UpdateAsync([NotNull] TAuthorization authorization, [NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default) { if (authorization == null) @@ -1153,12 +1026,9 @@ namespace OpenIddict.Core /// /// The authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the validation error encountered when validating the authorization. - /// - public virtual async Task> ValidateAsync( - [NotNull] TAuthorization authorization, CancellationToken cancellationToken = default) + /// The validation error encountered when validating the authorization. + public virtual async IAsyncEnumerable ValidateAsync( + [NotNull] TAuthorization authorization, [EnumeratorCancellation] CancellationToken cancellationToken = default) { if (authorization == null) { @@ -1170,23 +1040,23 @@ namespace OpenIddict.Core var type = await Store.GetTypeAsync(authorization, cancellationToken); if (string.IsNullOrEmpty(type)) { - builder.Add(new ValidationResult("The authorization type cannot be null or empty.")); + yield return new ValidationResult("The authorization type cannot be null or empty."); } else if (!string.Equals(type, OpenIddictConstants.AuthorizationTypes.AdHoc, StringComparison.OrdinalIgnoreCase) && !string.Equals(type, OpenIddictConstants.AuthorizationTypes.Permanent, StringComparison.OrdinalIgnoreCase)) { - builder.Add(new ValidationResult("The specified authorization type is not supported by the default token manager.")); + yield return new ValidationResult("The specified authorization type is not supported by the default token manager."); } if (string.IsNullOrEmpty(await Store.GetStatusAsync(authorization, cancellationToken))) { - builder.Add(new ValidationResult("The status cannot be null or empty.")); + yield return new ValidationResult("The status cannot be null or empty."); } if (string.IsNullOrEmpty(await Store.GetSubjectAsync(authorization, cancellationToken))) { - builder.Add(new ValidationResult("The subject cannot be null or empty.")); + yield return new ValidationResult("The subject cannot be null or empty."); } // Ensure that the scopes are not null or empty and do not contain spaces. @@ -1194,70 +1064,66 @@ namespace OpenIddict.Core { if (string.IsNullOrEmpty(scope)) { - builder.Add(new ValidationResult("Scopes cannot be null or empty.")); + yield return new ValidationResult("Scopes cannot be null or empty."); break; } if (scope.Contains(OpenIddictConstants.Separators.Space[0])) { - builder.Add(new ValidationResult("Scopes cannot contain spaces.")); + yield return new ValidationResult("Scopes cannot contain spaces."); break; } } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); } - Task IOpenIddictAuthorizationManager.CountAsync(CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.CountAsync(CancellationToken cancellationToken) => CountAsync(cancellationToken); - Task IOpenIddictAuthorizationManager.CountAsync(Func, IQueryable> query, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.CountAsync(Func, IQueryable> query, CancellationToken cancellationToken) => CountAsync(query, cancellationToken); - async Task IOpenIddictAuthorizationManager.CreateAsync(ImmutableDictionary claims, string subject, string client, string type, ImmutableArray scopes, CancellationToken cancellationToken) + async ValueTask IOpenIddictAuthorizationManager.CreateAsync(ImmutableDictionary claims, string subject, string client, string type, ImmutableArray scopes, CancellationToken cancellationToken) => await CreateAsync(claims, subject, client, type, scopes, cancellationToken); - async Task IOpenIddictAuthorizationManager.CreateAsync(OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken) + async ValueTask IOpenIddictAuthorizationManager.CreateAsync(OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken) => await CreateAsync(descriptor, cancellationToken); - Task IOpenIddictAuthorizationManager.CreateAsync(object authorization, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.CreateAsync(object authorization, CancellationToken cancellationToken) => CreateAsync((TAuthorization) authorization, cancellationToken); - Task IOpenIddictAuthorizationManager.DeleteAsync(object authorization, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.DeleteAsync(object authorization, CancellationToken cancellationToken) => DeleteAsync((TAuthorization) authorization, cancellationToken); - async Task> IOpenIddictAuthorizationManager.FindAsync(string subject, string client, CancellationToken cancellationToken) - => (await FindAsync(subject, client, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictAuthorizationManager.FindAsync(string subject, string client, CancellationToken cancellationToken) + => FindAsync(subject, client, cancellationToken).OfType(); - async Task> IOpenIddictAuthorizationManager.FindAsync(string subject, string client, string status, CancellationToken cancellationToken) - => (await FindAsync(subject, client, status, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictAuthorizationManager.FindAsync(string subject, string client, string status, CancellationToken cancellationToken) + => FindAsync(subject, client, status, cancellationToken).OfType(); - async Task> IOpenIddictAuthorizationManager.FindAsync(string subject, string client, string status, string type, CancellationToken cancellationToken) - => (await FindAsync(subject, client, status, type, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictAuthorizationManager.FindAsync(string subject, string client, string status, string type, CancellationToken cancellationToken) + => FindAsync(subject, client, status, type, cancellationToken).OfType(); - async Task> IOpenIddictAuthorizationManager.FindAsync(string subject, string client, string status, string type, ImmutableArray scopes, CancellationToken cancellationToken) - => (await FindAsync(subject, client, status, type, scopes, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictAuthorizationManager.FindAsync(string subject, string client, string status, string type, ImmutableArray scopes, CancellationToken cancellationToken) + => FindAsync(subject, client, status, type, scopes, cancellationToken).OfType(); - async Task> IOpenIddictAuthorizationManager.FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken) - => (await FindByApplicationIdAsync(identifier, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictAuthorizationManager.FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken) + => FindByApplicationIdAsync(identifier, cancellationToken).OfType(); - async Task IOpenIddictAuthorizationManager.FindByIdAsync(string identifier, CancellationToken cancellationToken) + async ValueTask IOpenIddictAuthorizationManager.FindByIdAsync(string identifier, CancellationToken cancellationToken) => await FindByIdAsync(identifier, cancellationToken); - async Task> IOpenIddictAuthorizationManager.FindBySubjectAsync(string subject, CancellationToken cancellationToken) - => (await FindBySubjectAsync(subject, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictAuthorizationManager.FindBySubjectAsync(string subject, CancellationToken cancellationToken) + => FindBySubjectAsync(subject, cancellationToken).OfType(); ValueTask IOpenIddictAuthorizationManager.GetApplicationIdAsync(object authorization, CancellationToken cancellationToken) => GetApplicationIdAsync((TAuthorization) authorization, cancellationToken); - Task IOpenIddictAuthorizationManager.GetAsync(Func, IQueryable> query, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.GetAsync(Func, IQueryable> query, CancellationToken cancellationToken) => GetAsync(query, cancellationToken); - Task IOpenIddictAuthorizationManager.GetAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.GetAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) => GetAsync(query, state, cancellationToken); ValueTask IOpenIddictAuthorizationManager.GetIdAsync(object authorization, CancellationToken cancellationToken) @@ -1275,52 +1141,52 @@ namespace OpenIddict.Core ValueTask IOpenIddictAuthorizationManager.GetTypeAsync(object authorization, CancellationToken cancellationToken) => GetTypeAsync((TAuthorization) authorization, cancellationToken); - Task IOpenIddictAuthorizationManager.HasScopesAsync(object authorization, ImmutableArray scopes, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.HasScopesAsync(object authorization, ImmutableArray scopes, CancellationToken cancellationToken) => HasScopesAsync((TAuthorization) authorization, scopes, cancellationToken); - Task IOpenIddictAuthorizationManager.IsAdHocAsync(object authorization, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.IsAdHocAsync(object authorization, CancellationToken cancellationToken) => IsAdHocAsync((TAuthorization) authorization, cancellationToken); - Task IOpenIddictAuthorizationManager.IsPermanentAsync(object authorization, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.IsPermanentAsync(object authorization, CancellationToken cancellationToken) => IsPermanentAsync((TAuthorization) authorization, cancellationToken); - Task IOpenIddictAuthorizationManager.IsRevokedAsync(object authorization, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.IsRevokedAsync(object authorization, CancellationToken cancellationToken) => IsRevokedAsync((TAuthorization) authorization, cancellationToken); - Task IOpenIddictAuthorizationManager.IsValidAsync(object authorization, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.IsValidAsync(object authorization, CancellationToken cancellationToken) => IsValidAsync((TAuthorization) authorization, cancellationToken); - async Task> IOpenIddictAuthorizationManager.ListAsync(int? count, int? offset, CancellationToken cancellationToken) - => (await ListAsync(count, offset, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictAuthorizationManager.ListAsync(int? count, int? offset, CancellationToken cancellationToken) + => ListAsync(count, offset, cancellationToken).OfType(); - Task> IOpenIddictAuthorizationManager.ListAsync(Func, IQueryable> query, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictAuthorizationManager.ListAsync(Func, IQueryable> query, CancellationToken cancellationToken) => ListAsync(query, cancellationToken); - Task> IOpenIddictAuthorizationManager.ListAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictAuthorizationManager.ListAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) => ListAsync(query, state, cancellationToken); - Task IOpenIddictAuthorizationManager.PopulateAsync(OpenIddictAuthorizationDescriptor descriptor, object authorization, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.PopulateAsync(OpenIddictAuthorizationDescriptor descriptor, object authorization, CancellationToken cancellationToken) => PopulateAsync(descriptor, (TAuthorization) authorization, cancellationToken); - Task IOpenIddictAuthorizationManager.PopulateAsync(object authorization, OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.PopulateAsync(object authorization, OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken) => PopulateAsync((TAuthorization) authorization, descriptor, cancellationToken); - Task IOpenIddictAuthorizationManager.PruneAsync(CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.PruneAsync(CancellationToken cancellationToken) => PruneAsync(cancellationToken); - Task IOpenIddictAuthorizationManager.RevokeAsync(object authorization, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.RevokeAsync(object authorization, CancellationToken cancellationToken) => RevokeAsync((TAuthorization) authorization, cancellationToken); - Task IOpenIddictAuthorizationManager.SetApplicationIdAsync(object authorization, string identifier, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.SetApplicationIdAsync(object authorization, string identifier, CancellationToken cancellationToken) => SetApplicationIdAsync((TAuthorization) authorization, identifier, cancellationToken); - Task IOpenIddictAuthorizationManager.UpdateAsync(object authorization, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.UpdateAsync(object authorization, CancellationToken cancellationToken) => UpdateAsync((TAuthorization) authorization, cancellationToken); - Task IOpenIddictAuthorizationManager.UpdateAsync(object authorization, OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken) + ValueTask IOpenIddictAuthorizationManager.UpdateAsync(object authorization, OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken) => UpdateAsync((TAuthorization) authorization, descriptor, cancellationToken); - Task> IOpenIddictAuthorizationManager.ValidateAsync(object authorization, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictAuthorizationManager.ValidateAsync(object authorization, CancellationToken cancellationToken) => ValidateAsync((TAuthorization) authorization, cancellationToken); } } \ No newline at end of file diff --git a/src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs b/src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs index ccb8e28f..2d2b17f8 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -62,10 +63,10 @@ namespace OpenIddict.Core /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes in the database. /// - public virtual Task CountAsync(CancellationToken cancellationToken = default) + public virtual ValueTask CountAsync(CancellationToken cancellationToken = default) => Store.CountAsync(cancellationToken); /// @@ -75,10 +76,10 @@ namespace OpenIddict.Core /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes that match the specified query. /// - public virtual Task CountAsync( + public virtual ValueTask CountAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default) { if (query == null) @@ -95,16 +96,16 @@ namespace OpenIddict.Core /// The scope to create. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken = default) + public virtual async ValueTask CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken = default) { if (scope == null) { throw new ArgumentNullException(nameof(scope)); } - var results = await ValidateAsync(scope, cancellationToken); + var results = await ValidateAsync(scope, cancellationToken).ToListAsync(cancellationToken); if (results.Any(result => result != ValidationResult.Success)) { var builder = new StringBuilder(); @@ -116,7 +117,7 @@ namespace OpenIddict.Core builder.AppendLine(result.ErrorMessage); } - throw new OpenIddictExceptions.ValidationException(builder.ToString(), results); + throw new OpenIddictExceptions.ValidationException(builder.ToString(), results.ToImmutableArray()); } await Store.CreateAsync(scope, cancellationToken); @@ -133,9 +134,9 @@ namespace OpenIddict.Core /// The scope descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, whose result returns the scope. + /// A that can be used to monitor the asynchronous operation, whose result returns the scope. /// - public virtual async Task CreateAsync( + public virtual async ValueTask CreateAsync( [NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default) { if (descriptor == null) @@ -161,9 +162,9 @@ namespace OpenIddict.Core /// The scope to delete. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken = default) + public virtual async ValueTask DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken = default) { if (scope == null) { @@ -184,10 +185,10 @@ namespace OpenIddict.Core /// The unique identifier associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the identifier. /// - public virtual async Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default) + public virtual async ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(identifier)) { @@ -221,10 +222,10 @@ namespace OpenIddict.Core /// The name associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the specified name. /// - public virtual async Task FindByNameAsync([NotNull] string name, CancellationToken cancellationToken = default) + public virtual async ValueTask FindByNameAsync([NotNull] string name, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(name)) { @@ -258,16 +259,13 @@ namespace OpenIddict.Core /// /// The names associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes corresponding to the specified names. - /// - public virtual async Task> FindByNamesAsync( + /// The scopes corresponding to the specified names. + public virtual IAsyncEnumerable FindByNamesAsync( ImmutableArray names, CancellationToken cancellationToken = default) { if (names.IsDefaultOrEmpty) { - return ImmutableArray.Create(); + return AsyncEnumerable.Empty(); } if (names.Any(name => string.IsNullOrEmpty(name))) @@ -276,13 +274,8 @@ namespace OpenIddict.Core } var scopes = Options.CurrentValue.DisableEntityCaching ? - await Store.FindByNamesAsync(names, cancellationToken) : - await Cache.FindByNamesAsync(names, cancellationToken); - - if (scopes.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindByNamesAsync(names, cancellationToken) : + Cache.FindByNamesAsync(names, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -293,19 +286,7 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(scopes.Length); - - foreach (var scope in scopes) - { - if (names.Contains(await Store.GetNameAsync(scope, cancellationToken))) - { - builder.Add(scope); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return scopes.WhereAwait(async scope => names.Contains(await Store.GetNameAsync(scope, cancellationToken), StringComparer.Ordinal)); } /// @@ -313,11 +294,8 @@ namespace OpenIddict.Core /// /// The resource associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes associated with the specified resource. - /// - public virtual async Task> FindByResourceAsync( + /// The scopes associated with the specified resource. + public virtual IAsyncEnumerable FindByResourceAsync( [NotNull] string resource, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(resource)) @@ -326,13 +304,8 @@ namespace OpenIddict.Core } var scopes = Options.CurrentValue.DisableEntityCaching ? - await Store.FindByResourceAsync(resource, cancellationToken) : - await Cache.FindByResourceAsync(resource, cancellationToken); - - if (scopes.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindByResourceAsync(resource, cancellationToken) : + Cache.FindByResourceAsync(resource, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -343,22 +316,8 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(scopes.Length); - - foreach (var scope in scopes) - { - foreach (var value in await Store.GetResourcesAsync(scope, cancellationToken)) - { - if (string.Equals(value, resource, StringComparison.Ordinal)) - { - builder.Add(scope); - } - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return scopes.WhereAwait(async scope => + (await Store.GetResourcesAsync(scope, cancellationToken)).Contains(resource, StringComparer.Ordinal)); } /// @@ -368,10 +327,10 @@ namespace OpenIddict.Core /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default) { if (query == null) @@ -391,10 +350,10 @@ namespace OpenIddict.Core /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default) { @@ -508,15 +467,10 @@ namespace OpenIddict.Core /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [CanBeNull] int? count = null, [CanBeNull] int? offset = null, CancellationToken cancellationToken = default) - { - return Store.ListAsync(count, offset, cancellationToken); - } + => Store.ListAsync(count, offset, cancellationToken); /// /// Executes the specified query and returns all the corresponding elements. @@ -524,11 +478,8 @@ namespace OpenIddict.Core /// The result type. /// The query to execute. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default) { if (query == null) @@ -547,11 +498,8 @@ namespace OpenIddict.Core /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default) { @@ -568,32 +516,31 @@ namespace OpenIddict.Core /// /// The scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the resources associated with the specified scopes. - /// - public virtual async Task> ListResourcesAsync( + /// All the resources associated with the specified scopes. + public virtual IAsyncEnumerable ListResourcesAsync( ImmutableArray scopes, CancellationToken cancellationToken = default) { if (scopes.IsDefaultOrEmpty) { - return ImmutableArray.Create(); + return AsyncEnumerable.Empty(); } - var set = new HashSet(StringComparer.Ordinal); + return ExecuteAsync(cancellationToken); - foreach (var scope in await FindByNamesAsync(scopes, cancellationToken)) + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) { - var resources = await GetResourcesAsync(scope, cancellationToken); - if (resources.IsDefaultOrEmpty) + var resources = new HashSet(StringComparer.Ordinal); + + await foreach (var scope in FindByNamesAsync(scopes, cancellationToken)) { - continue; + resources.UnionWith(await GetResourcesAsync(scope, cancellationToken)); } - set.UnionWith(resources); + foreach (var resource in resources) + { + yield return resource; + } } - - return set.ToImmutableArray(); } /// @@ -603,9 +550,9 @@ namespace OpenIddict.Core /// The descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task PopulateAsync([NotNull] TScope scope, + public virtual async ValueTask PopulateAsync([NotNull] TScope scope, [NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default) { if (scope == null) @@ -631,9 +578,9 @@ namespace OpenIddict.Core /// The scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task PopulateAsync( + public virtual async ValueTask PopulateAsync( [NotNull] OpenIddictScopeDescriptor descriptor, [NotNull] TScope scope, CancellationToken cancellationToken = default) { @@ -660,16 +607,16 @@ namespace OpenIddict.Core /// The scope to update. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken = default) + public virtual async ValueTask UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken = default) { if (scope == null) { throw new ArgumentNullException(nameof(scope)); } - var results = await ValidateAsync(scope, cancellationToken); + var results = await ValidateAsync(scope, cancellationToken).ToListAsync(cancellationToken); if (results.Any(result => result != ValidationResult.Success)) { var builder = new StringBuilder(); @@ -681,7 +628,7 @@ namespace OpenIddict.Core builder.AppendLine(result.ErrorMessage); } - throw new OpenIddictExceptions.ValidationException(builder.ToString(), results); + throw new OpenIddictExceptions.ValidationException(builder.ToString(), results.ToImmutableArray()); } await Store.UpdateAsync(scope, cancellationToken); @@ -700,9 +647,9 @@ namespace OpenIddict.Core /// The descriptor used to update the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task UpdateAsync([NotNull] TScope scope, + public virtual async ValueTask UpdateAsync([NotNull] TScope scope, [NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default) { if (scope == null) @@ -724,31 +671,26 @@ namespace OpenIddict.Core /// /// The scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the validation error encountered when validating the scope. - /// - public virtual async Task> ValidateAsync( - [NotNull] TScope scope, CancellationToken cancellationToken = default) + /// The validation error encountered when validating the scope. + public virtual async IAsyncEnumerable ValidateAsync( + [NotNull] TScope scope, [EnumeratorCancellation] CancellationToken cancellationToken = default) { if (scope == null) { throw new ArgumentNullException(nameof(scope)); } - var builder = ImmutableArray.CreateBuilder(); - // Ensure the name is not null or empty, does not contain a // space and is not already used for a different scope entity. var name = await Store.GetNameAsync(scope, cancellationToken); if (string.IsNullOrEmpty(name)) { - builder.Add(new ValidationResult("The scope name cannot be null or empty.")); + yield return new ValidationResult("The scope name cannot be null or empty."); } else if (name.Contains(OpenIddictConstants.Separators.Space[0])) { - builder.Add(new ValidationResult("The scope name cannot contain spaces.")); + yield return new ValidationResult("The scope name cannot contain spaces."); } else @@ -762,46 +704,42 @@ namespace OpenIddict.Core await Store.GetIdAsync(other, cancellationToken), await Store.GetIdAsync(scope, cancellationToken), StringComparison.Ordinal)) { - builder.Add(new ValidationResult("A scope with the same name already exists.")); + yield return new ValidationResult("A scope with the same name already exists."); } } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); } - Task IOpenIddictScopeManager.CountAsync(CancellationToken cancellationToken) + ValueTask IOpenIddictScopeManager.CountAsync(CancellationToken cancellationToken) => CountAsync(cancellationToken); - Task IOpenIddictScopeManager.CountAsync(Func, IQueryable> query, CancellationToken cancellationToken) + ValueTask IOpenIddictScopeManager.CountAsync(Func, IQueryable> query, CancellationToken cancellationToken) => CountAsync(query, cancellationToken); - async Task IOpenIddictScopeManager.CreateAsync(OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken) + async ValueTask IOpenIddictScopeManager.CreateAsync(OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken) => await CreateAsync(descriptor, cancellationToken); - Task IOpenIddictScopeManager.CreateAsync(object scope, CancellationToken cancellationToken) + ValueTask IOpenIddictScopeManager.CreateAsync(object scope, CancellationToken cancellationToken) => CreateAsync((TScope) scope, cancellationToken); - Task IOpenIddictScopeManager.DeleteAsync(object scope, CancellationToken cancellationToken) + ValueTask IOpenIddictScopeManager.DeleteAsync(object scope, CancellationToken cancellationToken) => DeleteAsync((TScope) scope, cancellationToken); - async Task IOpenIddictScopeManager.FindByIdAsync(string identifier, CancellationToken cancellationToken) + async ValueTask IOpenIddictScopeManager.FindByIdAsync(string identifier, CancellationToken cancellationToken) => await FindByIdAsync(identifier, cancellationToken); - async Task IOpenIddictScopeManager.FindByNameAsync(string name, CancellationToken cancellationToken) + async ValueTask IOpenIddictScopeManager.FindByNameAsync(string name, CancellationToken cancellationToken) => await FindByNameAsync(name, cancellationToken); - async Task> IOpenIddictScopeManager.FindByNamesAsync(ImmutableArray names, CancellationToken cancellationToken) - => (await FindByNamesAsync(names, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictScopeManager.FindByNamesAsync(ImmutableArray names, CancellationToken cancellationToken) + => FindByNamesAsync(names, cancellationToken).OfType(); - async Task> IOpenIddictScopeManager.FindByResourceAsync(string resource, CancellationToken cancellationToken) - => (await FindByResourceAsync(resource, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictScopeManager.FindByResourceAsync(string resource, CancellationToken cancellationToken) + => FindByResourceAsync(resource, cancellationToken).OfType(); - Task IOpenIddictScopeManager.GetAsync(Func, IQueryable> query, CancellationToken cancellationToken) + ValueTask IOpenIddictScopeManager.GetAsync(Func, IQueryable> query, CancellationToken cancellationToken) => GetAsync(query, cancellationToken); - Task IOpenIddictScopeManager.GetAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) + ValueTask IOpenIddictScopeManager.GetAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) => GetAsync(query, state, cancellationToken); ValueTask IOpenIddictScopeManager.GetDescriptionAsync(object scope, CancellationToken cancellationToken) @@ -819,31 +757,31 @@ namespace OpenIddict.Core ValueTask> IOpenIddictScopeManager.GetResourcesAsync(object scope, CancellationToken cancellationToken) => GetResourcesAsync((TScope) scope, cancellationToken); - async Task> IOpenIddictScopeManager.ListAsync(int? count, int? offset, CancellationToken cancellationToken) - => (await ListAsync(count, offset, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictScopeManager.ListAsync(int? count, int? offset, CancellationToken cancellationToken) + => ListAsync(count, offset, cancellationToken).OfType(); - Task> IOpenIddictScopeManager.ListAsync(Func, IQueryable> query, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictScopeManager.ListAsync(Func, IQueryable> query, CancellationToken cancellationToken) => ListAsync(query, cancellationToken); - Task> IOpenIddictScopeManager.ListAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictScopeManager.ListAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) => ListAsync(query, state, cancellationToken); - Task> IOpenIddictScopeManager.ListResourcesAsync(ImmutableArray scopes, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictScopeManager.ListResourcesAsync(ImmutableArray scopes, CancellationToken cancellationToken) => ListResourcesAsync(scopes, cancellationToken); - Task IOpenIddictScopeManager.PopulateAsync(OpenIddictScopeDescriptor descriptor, object scope, CancellationToken cancellationToken) + ValueTask IOpenIddictScopeManager.PopulateAsync(OpenIddictScopeDescriptor descriptor, object scope, CancellationToken cancellationToken) => PopulateAsync(descriptor, (TScope) scope, cancellationToken); - Task IOpenIddictScopeManager.PopulateAsync(object scope, OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken) + ValueTask IOpenIddictScopeManager.PopulateAsync(object scope, OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken) => PopulateAsync((TScope) scope, descriptor, cancellationToken); - Task IOpenIddictScopeManager.UpdateAsync(object scope, CancellationToken cancellationToken) + ValueTask IOpenIddictScopeManager.UpdateAsync(object scope, CancellationToken cancellationToken) => UpdateAsync((TScope) scope, cancellationToken); - Task IOpenIddictScopeManager.UpdateAsync(object scope, OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken) + ValueTask IOpenIddictScopeManager.UpdateAsync(object scope, OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken) => UpdateAsync((TScope) scope, descriptor, cancellationToken); - Task> IOpenIddictScopeManager.ValidateAsync(object scope, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictScopeManager.ValidateAsync(object scope, CancellationToken cancellationToken) => ValidateAsync((TScope) scope, cancellationToken); } } \ No newline at end of file diff --git a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs index f22fe6ac..02547677 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs @@ -5,9 +5,11 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; using System.Threading; @@ -62,10 +64,10 @@ namespace OpenIddict.Core /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of tokens in the database. /// - public virtual Task CountAsync(CancellationToken cancellationToken = default) + public virtual ValueTask CountAsync(CancellationToken cancellationToken = default) => Store.CountAsync(cancellationToken); /// @@ -75,10 +77,10 @@ namespace OpenIddict.Core /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of tokens that match the specified query. /// - public virtual Task CountAsync( + public virtual ValueTask CountAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default) { if (query == null) @@ -95,9 +97,9 @@ namespace OpenIddict.Core /// The token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task CreateAsync([NotNull] TToken token, CancellationToken cancellationToken = default) + public virtual async ValueTask CreateAsync([NotNull] TToken token, CancellationToken cancellationToken = default) { if (token == null) { @@ -118,7 +120,7 @@ namespace OpenIddict.Core await Store.SetReferenceIdAsync(token, identifier, cancellationToken); } - var results = await ValidateAsync(token, cancellationToken); + var results = await ValidateAsync(token, cancellationToken).ToListAsync(cancellationToken); if (results.Any(result => result != ValidationResult.Success)) { var builder = new StringBuilder(); @@ -130,7 +132,7 @@ namespace OpenIddict.Core builder.AppendLine(result.ErrorMessage); } - throw new OpenIddictExceptions.ValidationException(builder.ToString(), results); + throw new OpenIddictExceptions.ValidationException(builder.ToString(), results.ToImmutableArray()); } await Store.CreateAsync(token, cancellationToken); @@ -147,9 +149,9 @@ namespace OpenIddict.Core /// The token descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, whose result returns the token. + /// A that can be used to monitor the asynchronous operation, whose result returns the token. /// - public virtual async Task CreateAsync( + public virtual async ValueTask CreateAsync( [NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default) { if (descriptor == null) @@ -175,9 +177,9 @@ namespace OpenIddict.Core /// The token to delete. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken = default) + public virtual async ValueTask DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken = default) { if (token == null) { @@ -199,9 +201,9 @@ namespace OpenIddict.Core /// The date on which the token will no longer be considered valid. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task ExtendAsync([NotNull] TToken token, + public virtual async ValueTask ExtendAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken = default) { if (token == null) @@ -220,11 +222,8 @@ namespace OpenIddict.Core /// The subject associated with the token. /// The client associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the subject/client. - /// - public virtual async Task> FindAsync([NotNull] string subject, + /// The tokens corresponding to the subject/client. + public virtual IAsyncEnumerable FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(subject)) @@ -238,13 +237,8 @@ namespace OpenIddict.Core } var tokens = Options.CurrentValue.DisableEntityCaching ? - await Store.FindAsync(subject, client, cancellationToken) : - await Cache.FindAsync(subject, client, cancellationToken); - - if (tokens.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindAsync(subject, client, cancellationToken) : + Cache.FindAsync(subject, client, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -255,19 +249,8 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(tokens.Length); - - foreach (var token in tokens) - { - if (string.Equals(await Store.GetSubjectAsync(token, cancellationToken), subject, StringComparison.Ordinal)) - { - builder.Add(token); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return tokens.WhereAwait(async token => string.Equals(await Store.GetSubjectAsync( + token, cancellationToken), subject, StringComparison.Ordinal)); } /// @@ -277,11 +260,8 @@ namespace OpenIddict.Core /// The client associated with the token. /// The token status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The tokens corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken = default) { @@ -301,13 +281,8 @@ namespace OpenIddict.Core } var tokens = Options.CurrentValue.DisableEntityCaching ? - await Store.FindAsync(subject, client, status, cancellationToken) : - await Cache.FindAsync(subject, client, status, cancellationToken); - - if (tokens.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindAsync(subject, client, status, cancellationToken) : + Cache.FindAsync(subject, client, status, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -318,19 +293,8 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(tokens.Length); - - foreach (var token in tokens) - { - if (string.Equals(await Store.GetSubjectAsync(token, cancellationToken), subject, StringComparison.Ordinal)) - { - builder.Add(token); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return tokens.WhereAwait(async token => string.Equals(await Store.GetSubjectAsync( + token, cancellationToken), subject, StringComparison.Ordinal)); } /// @@ -341,11 +305,8 @@ namespace OpenIddict.Core /// The token status. /// The token type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// Tokens corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken = default) { @@ -370,13 +331,8 @@ namespace OpenIddict.Core } var tokens = Options.CurrentValue.DisableEntityCaching ? - await Store.FindAsync(subject, client, status, type, cancellationToken) : - await Cache.FindAsync(subject, client, status, type, cancellationToken); - - if (tokens.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindAsync(subject, client, status, type, cancellationToken) : + Cache.FindAsync(subject, client, status, type, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -387,19 +343,8 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(tokens.Length); - - foreach (var token in tokens) - { - if (string.Equals(await Store.GetSubjectAsync(token, cancellationToken), subject, StringComparison.Ordinal)) - { - builder.Add(token); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return tokens.WhereAwait(async token => string.Equals(await Store.GetSubjectAsync( + token, cancellationToken), subject, StringComparison.Ordinal)); } /// @@ -407,11 +352,8 @@ namespace OpenIddict.Core /// /// The application identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified application. - /// - public virtual async Task> FindByApplicationIdAsync( + /// The tokens corresponding to the specified application. + public virtual IAsyncEnumerable FindByApplicationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(identifier)) @@ -420,13 +362,8 @@ namespace OpenIddict.Core } var tokens = Options.CurrentValue.DisableEntityCaching ? - await Store.FindByApplicationIdAsync(identifier, cancellationToken) : - await Cache.FindByApplicationIdAsync(identifier, cancellationToken); - - if (tokens.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindByApplicationIdAsync(identifier, cancellationToken) : + Cache.FindByApplicationIdAsync(identifier, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -437,19 +374,8 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(tokens.Length); - - foreach (var token in tokens) - { - if (string.Equals(await Store.GetApplicationIdAsync(token, cancellationToken), identifier, StringComparison.Ordinal)) - { - builder.Add(token); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return tokens.WhereAwait(async token => string.Equals(await Store.GetApplicationIdAsync( + token, cancellationToken), identifier, StringComparison.Ordinal)); } /// @@ -457,11 +383,8 @@ namespace OpenIddict.Core /// /// The authorization identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified authorization. - /// - public virtual async Task> FindByAuthorizationIdAsync( + /// The tokens corresponding to the specified authorization. + public virtual IAsyncEnumerable FindByAuthorizationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(identifier)) @@ -470,13 +393,8 @@ namespace OpenIddict.Core } var tokens = Options.CurrentValue.DisableEntityCaching ? - await Store.FindByAuthorizationIdAsync(identifier, cancellationToken) : - await Cache.FindByAuthorizationIdAsync(identifier, cancellationToken); - - if (tokens.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindByAuthorizationIdAsync(identifier, cancellationToken) : + Cache.FindByAuthorizationIdAsync(identifier, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -487,19 +405,8 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(tokens.Length); - - foreach (var token in tokens) - { - if (string.Equals(await Store.GetAuthorizationIdAsync(token, cancellationToken), identifier, StringComparison.Ordinal)) - { - builder.Add(token); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return tokens.WhereAwait(async token => string.Equals(await Store.GetAuthorizationIdAsync( + token, cancellationToken), identifier, StringComparison.Ordinal)); } /// @@ -508,10 +415,10 @@ namespace OpenIddict.Core /// The unique identifier associated with the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the token corresponding to the unique identifier. /// - public virtual async Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default) + public virtual async ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(identifier)) { @@ -546,10 +453,10 @@ namespace OpenIddict.Core /// The reference identifier associated with the tokens. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the tokens corresponding to the specified reference identifier. /// - public virtual async Task FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default) + public virtual async ValueTask FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(identifier)) { @@ -585,11 +492,8 @@ namespace OpenIddict.Core /// /// The subject associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified subject. - /// - public virtual async Task> FindBySubjectAsync( + /// The tokens corresponding to the specified subject. + public virtual IAsyncEnumerable FindBySubjectAsync( [NotNull] string subject, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(subject)) @@ -598,13 +502,8 @@ namespace OpenIddict.Core } var tokens = Options.CurrentValue.DisableEntityCaching ? - await Store.FindBySubjectAsync(subject, cancellationToken) : - await Cache.FindBySubjectAsync(subject, cancellationToken); - - if (tokens.IsEmpty) - { - return ImmutableArray.Create(); - } + Store.FindBySubjectAsync(subject, cancellationToken) : + Cache.FindBySubjectAsync(subject, cancellationToken); if (Options.CurrentValue.DisableAdditionalFiltering) { @@ -615,19 +514,8 @@ namespace OpenIddict.Core // To ensure a case-sensitive comparison is enforced independently of the database/table/query collation // used by the store, a second pass using string.Equals(StringComparison.Ordinal) is manually made here. - var builder = ImmutableArray.CreateBuilder(tokens.Length); - - foreach (var token in tokens) - { - if (string.Equals(await Store.GetSubjectAsync(token, cancellationToken), subject, StringComparison.Ordinal)) - { - builder.Add(token); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return tokens.WhereAwait(async token => string.Equals(await Store.GetSubjectAsync( + token, cancellationToken), subject, StringComparison.Ordinal)); } /// @@ -656,10 +544,10 @@ namespace OpenIddict.Core /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default) { if (query == null) @@ -679,10 +567,10 @@ namespace OpenIddict.Core /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default) { @@ -873,7 +761,7 @@ namespace OpenIddict.Core /// The token. /// The that can be used to abort the operation. /// true if the token has already been redemeed, false otherwise. - public virtual async Task IsRedeemedAsync([NotNull] TToken token, CancellationToken cancellationToken = default) + public virtual async ValueTask IsRedeemedAsync([NotNull] TToken token, CancellationToken cancellationToken = default) { if (token == null) { @@ -895,7 +783,7 @@ namespace OpenIddict.Core /// The token. /// The that can be used to abort the operation. /// true if the token has been revoked, false otherwise. - public virtual async Task IsRevokedAsync([NotNull] TToken token, CancellationToken cancellationToken = default) + public virtual async ValueTask IsRevokedAsync([NotNull] TToken token, CancellationToken cancellationToken = default) { if (token == null) { @@ -917,7 +805,7 @@ namespace OpenIddict.Core /// The token. /// The that can be used to abort the operation. /// true if the token is valid, false otherwise. - public virtual async Task IsValidAsync([NotNull] TToken token, CancellationToken cancellationToken = default) + public virtual async ValueTask IsValidAsync([NotNull] TToken token, CancellationToken cancellationToken = default) { if (token == null) { @@ -939,15 +827,10 @@ namespace OpenIddict.Core /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [CanBeNull] int? count = null, [CanBeNull] int? offset = null, CancellationToken cancellationToken = default) - { - return Store.ListAsync(count, offset, cancellationToken); - } + => Store.ListAsync(count, offset, cancellationToken); /// /// Executes the specified query and returns all the corresponding elements. @@ -955,11 +838,8 @@ namespace OpenIddict.Core /// The result type. /// The query to execute. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken = default) { if (query == null) @@ -978,11 +858,8 @@ namespace OpenIddict.Core /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken = default) { @@ -1001,9 +878,9 @@ namespace OpenIddict.Core /// The descriptor. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task PopulateAsync([NotNull] TToken token, + public virtual async ValueTask PopulateAsync([NotNull] TToken token, [NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default) { if (token == null) @@ -1034,9 +911,9 @@ namespace OpenIddict.Core /// The token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task PopulateAsync( + public virtual async ValueTask PopulateAsync( [NotNull] OpenIddictTokenDescriptor descriptor, [NotNull] TToken token, CancellationToken cancellationToken = default) { @@ -1066,9 +943,9 @@ namespace OpenIddict.Core /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task PruneAsync(CancellationToken cancellationToken = default) + public virtual ValueTask PruneAsync(CancellationToken cancellationToken = default) => Store.PruneAsync(cancellationToken); /// @@ -1076,8 +953,8 @@ namespace OpenIddict.Core /// /// The token to redeem. /// The that can be used to abort the operation. - /// A that can be used to monitor the asynchronous operation. - public virtual async Task RedeemAsync([NotNull] TToken token, CancellationToken cancellationToken = default) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask RedeemAsync([NotNull] TToken token, CancellationToken cancellationToken = default) { if (token == null) { @@ -1097,8 +974,8 @@ namespace OpenIddict.Core /// /// The token to revoke. /// The that can be used to abort the operation. - /// A that can be used to monitor the asynchronous operation. - public virtual async Task RevokeAsync([NotNull] TToken token, CancellationToken cancellationToken = default) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask RevokeAsync([NotNull] TToken token, CancellationToken cancellationToken = default) { if (token == null) { @@ -1120,9 +997,9 @@ namespace OpenIddict.Core /// The unique identifier associated with the client application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task SetApplicationIdAsync([NotNull] TToken token, + public virtual async ValueTask SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken = default) { if (token == null) @@ -1141,9 +1018,9 @@ namespace OpenIddict.Core /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task SetAuthorizationIdAsync([NotNull] TToken token, + public virtual async ValueTask SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken = default) { if (token == null) @@ -1161,16 +1038,16 @@ namespace OpenIddict.Core /// The token to update. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken = default) + public virtual async ValueTask UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken = default) { if (token == null) { throw new ArgumentNullException(nameof(token)); } - var results = await ValidateAsync(token, cancellationToken); + var results = await ValidateAsync(token, cancellationToken).ToListAsync(cancellationToken); if (results.Any(result => result != ValidationResult.Success)) { var builder = new StringBuilder(); @@ -1182,7 +1059,7 @@ namespace OpenIddict.Core builder.AppendLine(result.ErrorMessage); } - throw new OpenIddictExceptions.ValidationException(builder.ToString(), results); + throw new OpenIddictExceptions.ValidationException(builder.ToString(), results.ToImmutableArray()); } await Store.UpdateAsync(token, cancellationToken); @@ -1201,9 +1078,9 @@ namespace OpenIddict.Core /// The descriptor used to update the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task UpdateAsync([NotNull] TToken token, + public virtual async ValueTask UpdateAsync([NotNull] TToken token, [NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default) { if (token == null) @@ -1236,20 +1113,15 @@ namespace OpenIddict.Core /// /// The token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the validation error encountered when validating the token. - /// - public virtual async Task> ValidateAsync( - [NotNull] TToken token, CancellationToken cancellationToken = default) + /// The validation error encountered when validating the token. + public virtual async IAsyncEnumerable ValidateAsync( + [NotNull] TToken token, [EnumeratorCancellation] CancellationToken cancellationToken = default) { if (token == null) { throw new ArgumentNullException(nameof(token)); } - var builder = ImmutableArray.CreateBuilder(); - // If a reference identifier was associated with the token, // ensure it's not already used for a different token. var identifier = await Store.GetReferenceIdAsync(token, cancellationToken); @@ -1264,36 +1136,32 @@ namespace OpenIddict.Core await Store.GetIdAsync(other, cancellationToken), await Store.GetIdAsync(token, cancellationToken), StringComparison.Ordinal)) { - builder.Add(new ValidationResult("A token with the same reference identifier already exists.")); + yield return new ValidationResult("A token with the same reference identifier already exists."); } } var type = await Store.GetTypeAsync(token, cancellationToken); if (string.IsNullOrEmpty(type)) { - builder.Add(new ValidationResult("The token type cannot be null or empty.")); + yield return new ValidationResult("The token type cannot be null or empty."); } else if (!string.Equals(type, OpenIddictConstants.TokenUsages.AccessToken, StringComparison.OrdinalIgnoreCase) && !string.Equals(type, OpenIddictConstants.TokenUsages.AuthorizationCode, StringComparison.OrdinalIgnoreCase) && !string.Equals(type, OpenIddictConstants.TokenUsages.RefreshToken, StringComparison.OrdinalIgnoreCase)) { - builder.Add(new ValidationResult("The specified token type is not supported by the default token manager.")); + yield return new ValidationResult("The specified token type is not supported by the default token manager."); } if (string.IsNullOrEmpty(await Store.GetStatusAsync(token, cancellationToken))) { - builder.Add(new ValidationResult("The status cannot be null or empty.")); + yield return new ValidationResult("The status cannot be null or empty."); } if (string.IsNullOrEmpty(await Store.GetSubjectAsync(token, cancellationToken))) { - builder.Add(new ValidationResult("The subject cannot be null or empty.")); + yield return new ValidationResult("The subject cannot be null or empty."); } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); } /// @@ -1303,72 +1171,70 @@ namespace OpenIddict.Core /// The client identifier. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - protected virtual Task ObfuscateReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default) + protected virtual ValueTask ObfuscateReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - using (var algorithm = SHA256.Create()) - { - // Compute the digest of the generated identifier and use it as the hashed identifier of the reference token. - // Doing that prevents token identifiers stolen from the database from being used as valid reference tokens. - return Task.FromResult(Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(identifier)))); - } + // Compute the digest of the generated identifier and use it as the hashed identifier of the reference token. + // Doing that prevents token identifiers stolen from the database from being used as valid reference tokens. + using var algorithm = SHA256.Create(); + return new ValueTask(Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(identifier)))); } - Task IOpenIddictTokenManager.CountAsync(CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.CountAsync(CancellationToken cancellationToken) => CountAsync(cancellationToken); - Task IOpenIddictTokenManager.CountAsync(Func, IQueryable> query, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.CountAsync(Func, IQueryable> query, CancellationToken cancellationToken) => CountAsync(query, cancellationToken); - async Task IOpenIddictTokenManager.CreateAsync(OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken) + async ValueTask IOpenIddictTokenManager.CreateAsync(OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken) => await CreateAsync(descriptor, cancellationToken); - Task IOpenIddictTokenManager.CreateAsync(object token, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.CreateAsync(object token, CancellationToken cancellationToken) => CreateAsync((TToken) token, cancellationToken); - Task IOpenIddictTokenManager.DeleteAsync(object token, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.DeleteAsync(object token, CancellationToken cancellationToken) => DeleteAsync((TToken) token, cancellationToken); - Task IOpenIddictTokenManager.ExtendAsync(object token, DateTimeOffset? date, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.ExtendAsync(object token, DateTimeOffset? date, CancellationToken cancellationToken) => ExtendAsync((TToken) token, date, cancellationToken); - async Task> IOpenIddictTokenManager.FindAsync(string subject, string client, CancellationToken cancellationToken) - => (await FindAsync(subject, client, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictTokenManager.FindAsync(string subject, string client, CancellationToken cancellationToken) + => FindAsync(subject, client, cancellationToken).OfType(); - async Task> IOpenIddictTokenManager.FindAsync(string subject, string client, string status, CancellationToken cancellationToken) - => (await FindAsync(subject, client, status, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictTokenManager.FindAsync(string subject, string client, string status, CancellationToken cancellationToken) + => FindAsync(subject, client, status, cancellationToken).OfType(); - async Task> IOpenIddictTokenManager.FindAsync(string subject, string client, string status, string type, CancellationToken cancellationToken) - => (await FindAsync(subject, client, status, type, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictTokenManager.FindAsync(string subject, string client, string status, string type, CancellationToken cancellationToken) + => FindAsync(subject, client, status, type, cancellationToken).OfType(); - async Task> IOpenIddictTokenManager.FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken) - => (await FindByApplicationIdAsync(identifier, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictTokenManager.FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken) + => FindByApplicationIdAsync(identifier, cancellationToken).OfType(); - async Task> IOpenIddictTokenManager.FindByAuthorizationIdAsync(string identifier, CancellationToken cancellationToken) - => (await FindByAuthorizationIdAsync(identifier, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictTokenManager.FindByAuthorizationIdAsync(string identifier, CancellationToken cancellationToken) + => FindByAuthorizationIdAsync(identifier, cancellationToken).OfType(); - async Task IOpenIddictTokenManager.FindByIdAsync(string identifier, CancellationToken cancellationToken) + async ValueTask IOpenIddictTokenManager.FindByIdAsync(string identifier, CancellationToken cancellationToken) => await FindByIdAsync(identifier, cancellationToken); - async Task IOpenIddictTokenManager.FindByReferenceIdAsync(string identifier, CancellationToken cancellationToken) + async ValueTask IOpenIddictTokenManager.FindByReferenceIdAsync(string identifier, CancellationToken cancellationToken) => await FindByReferenceIdAsync(identifier, cancellationToken); - async Task> IOpenIddictTokenManager.FindBySubjectAsync(string subject, CancellationToken cancellationToken) - => (await FindBySubjectAsync(subject, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictTokenManager.FindBySubjectAsync(string subject, CancellationToken cancellationToken) + => FindBySubjectAsync(subject, cancellationToken).OfType(); ValueTask IOpenIddictTokenManager.GetApplicationIdAsync(object token, CancellationToken cancellationToken) => GetApplicationIdAsync((TToken) token, cancellationToken); - Task IOpenIddictTokenManager.GetAsync(Func, IQueryable> query, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.GetAsync(Func, IQueryable> query, CancellationToken cancellationToken) => GetAsync(query, cancellationToken); - Task IOpenIddictTokenManager.GetAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.GetAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) => GetAsync(query, state, cancellationToken); ValueTask IOpenIddictTokenManager.GetAuthorizationIdAsync(object token, CancellationToken cancellationToken) @@ -1398,52 +1264,52 @@ namespace OpenIddict.Core ValueTask IOpenIddictTokenManager.GetTypeAsync(object token, CancellationToken cancellationToken) => GetTypeAsync((TToken) token, cancellationToken); - Task IOpenIddictTokenManager.IsRedeemedAsync(object token, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.IsRedeemedAsync(object token, CancellationToken cancellationToken) => IsRedeemedAsync((TToken) token, cancellationToken); - Task IOpenIddictTokenManager.IsRevokedAsync(object token, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.IsRevokedAsync(object token, CancellationToken cancellationToken) => IsRevokedAsync((TToken) token, cancellationToken); - Task IOpenIddictTokenManager.IsValidAsync(object token, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.IsValidAsync(object token, CancellationToken cancellationToken) => IsValidAsync((TToken) token, cancellationToken); - async Task> IOpenIddictTokenManager.ListAsync(int? count, int? offset, CancellationToken cancellationToken) - => (await ListAsync(count, offset, cancellationToken)).CastArray(); + IAsyncEnumerable IOpenIddictTokenManager.ListAsync(int? count, int? offset, CancellationToken cancellationToken) + => ListAsync(count, offset, cancellationToken).OfType(); - Task> IOpenIddictTokenManager.ListAsync(Func, IQueryable> query, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictTokenManager.ListAsync(Func, IQueryable> query, CancellationToken cancellationToken) => ListAsync(query, cancellationToken); - Task> IOpenIddictTokenManager.ListAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictTokenManager.ListAsync(Func, TState, IQueryable> query, TState state, CancellationToken cancellationToken) => ListAsync(query, state, cancellationToken); - Task IOpenIddictTokenManager.PopulateAsync(OpenIddictTokenDescriptor descriptor, object token, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.PopulateAsync(OpenIddictTokenDescriptor descriptor, object token, CancellationToken cancellationToken) => PopulateAsync(descriptor, (TToken) token, cancellationToken); - Task IOpenIddictTokenManager.PopulateAsync(object token, OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.PopulateAsync(object token, OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken) => PopulateAsync((TToken) token, descriptor, cancellationToken); - Task IOpenIddictTokenManager.PruneAsync(CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.PruneAsync(CancellationToken cancellationToken) => PruneAsync(cancellationToken); - Task IOpenIddictTokenManager.RedeemAsync(object token, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.RedeemAsync(object token, CancellationToken cancellationToken) => RedeemAsync((TToken) token, cancellationToken); - Task IOpenIddictTokenManager.RevokeAsync(object token, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.RevokeAsync(object token, CancellationToken cancellationToken) => RevokeAsync((TToken) token, cancellationToken); - Task IOpenIddictTokenManager.SetApplicationIdAsync(object token, string identifier, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.SetApplicationIdAsync(object token, string identifier, CancellationToken cancellationToken) => SetApplicationIdAsync((TToken) token, identifier, cancellationToken); - Task IOpenIddictTokenManager.SetAuthorizationIdAsync(object token, string identifier, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.SetAuthorizationIdAsync(object token, string identifier, CancellationToken cancellationToken) => SetAuthorizationIdAsync((TToken) token, identifier, cancellationToken); - Task IOpenIddictTokenManager.UpdateAsync(object token, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.UpdateAsync(object token, CancellationToken cancellationToken) => UpdateAsync((TToken) token, cancellationToken); - Task IOpenIddictTokenManager.UpdateAsync(object token, OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken) + ValueTask IOpenIddictTokenManager.UpdateAsync(object token, OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken) => UpdateAsync((TToken) token, descriptor, cancellationToken); - Task> IOpenIddictTokenManager.ValidateAsync(object token, CancellationToken cancellationToken) + IAsyncEnumerable IOpenIddictTokenManager.ValidateAsync(object token, CancellationToken cancellationToken) => ValidateAsync((TToken) token, cancellationToken); } } \ No newline at end of file diff --git a/src/OpenIddict.Core/OpenIddict.Core.csproj b/src/OpenIddict.Core/OpenIddict.Core.csproj index bcba8e06..b76172cb 100644 --- a/src/OpenIddict.Core/OpenIddict.Core.csproj +++ b/src/OpenIddict.Core/OpenIddict.Core.csproj @@ -19,6 +19,7 @@ + diff --git a/src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkHelpers.cs b/src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkHelpers.cs index 057e4fa7..55aa6f54 100644 --- a/src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkHelpers.cs +++ b/src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkHelpers.cs @@ -4,6 +4,9 @@ * the license and the contributors participating to this project. */ +using System.Collections.Generic; +using System.Linq; +using System.Threading; using JetBrains.Annotations; using OpenIddict.EntityFramework; using OpenIddict.EntityFramework.Models; @@ -11,7 +14,7 @@ using OpenIddict.EntityFramework.Models; namespace System.Data.Entity { /// - /// Exposes extensions allowing to register the OpenIddict Entity Framework 6.x entity sets. + /// Exposes extensions simplifying the integration between OpenIddict and Entity Framework 6.x. /// public static class OpenIddictEntityFrameworkHelpers { @@ -55,5 +58,30 @@ namespace System.Data.Entity return builder; } + + /// + /// Executes the query and returns the results as a non-streamed async enumeration. + /// + /// The type of the returned entities. + /// The query source. + /// The that can be used to abort the operation. + /// The non-streamed async enumeration containing the results. + internal static IAsyncEnumerable AsAsyncEnumerable([NotNull] this IQueryable source, CancellationToken cancellationToken) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + foreach (var element in await source.ToListAsync(cancellationToken)) + { + yield return element; + } + } + } } } \ No newline at end of file diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs index 3a1de64d..a83d134f 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs @@ -103,11 +103,11 @@ namespace OpenIddict.EntityFramework /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications in the database. /// - public virtual Task CountAsync(CancellationToken cancellationToken) - => Applications.LongCountAsync(); + public virtual ValueTask CountAsync(CancellationToken cancellationToken) + => new ValueTask(Applications.LongCountAsync()); /// /// Determines the number of applications that match the specified query. @@ -116,17 +116,17 @@ namespace OpenIddict.EntityFramework /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications that match the specified query. /// - public virtual Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + public virtual ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) { throw new ArgumentNullException(nameof(query)); } - return query(Applications).LongCountAsync(); + return new ValueTask(query(Applications).LongCountAsync()); } /// @@ -134,10 +134,8 @@ namespace OpenIddict.EntityFramework /// /// The application to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { @@ -146,7 +144,7 @@ namespace OpenIddict.EntityFramework Applications.Add(application); - return Context.SaveChangesAsync(cancellationToken); + return new ValueTask(Context.SaveChangesAsync(cancellationToken)); } /// @@ -154,10 +152,8 @@ namespace OpenIddict.EntityFramework /// /// The application to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { @@ -191,41 +187,40 @@ namespace OpenIddict.EntityFramework // To prevent an SQL exception from being thrown if a new associated entity is // created after the existing entries have been listed, the following logic is // executed in a serializable transaction, that will lock the affected tables. - using (var transaction = CreateTransaction()) - { - // Remove all the authorizations associated with the application and - // the tokens attached to these implicit or explicit authorizations. - foreach (var authorization in await ListAuthorizationsAsync()) - { - foreach (var token in authorization.Tokens) - { - Tokens.Remove(token); - } - - Authorizations.Remove(authorization); - } + using var transaction = CreateTransaction(); - // Remove all the tokens associated with the application. - foreach (var token in await ListTokensAsync()) + // Remove all the authorizations associated with the application and + // the tokens attached to these implicit or explicit authorizations. + foreach (var authorization in await ListAuthorizationsAsync()) + { + foreach (var token in authorization.Tokens) { Tokens.Remove(token); } - Applications.Remove(application); + Authorizations.Remove(authorization); + } - try - { - await Context.SaveChangesAsync(cancellationToken); - transaction?.Commit(); - } + // Remove all the tokens associated with the application. + foreach (var token in await ListTokensAsync()) + { + Tokens.Remove(token); + } - catch (DbUpdateConcurrencyException exception) - { - throw new OpenIddictExceptions.ConcurrencyException(new StringBuilder() - .AppendLine("The application was concurrently updated and cannot be persisted in its current state.") - .Append("Reload the application from the database and retry the operation.") - .ToString(), exception); - } + Applications.Remove(application); + + try + { + await Context.SaveChangesAsync(cancellationToken); + transaction?.Commit(); + } + + catch (DbUpdateConcurrencyException exception) + { + throw new OpenIddictExceptions.ConcurrencyException(new StringBuilder() + .AppendLine("The application was concurrently updated and cannot be persisted in its current state.") + .Append("Reload the application from the database and retry the operation.") + .ToString(), exception); } } @@ -235,10 +230,10 @@ namespace OpenIddict.EntityFramework /// The unique identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - public virtual Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -247,9 +242,9 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(identifier); - return (from application in Applications - where application.Id.Equals(key) - select application).FirstOrDefaultAsync(); + return new ValueTask((from application in Applications + where application.Id.Equals(key) + select application).FirstOrDefaultAsync()); } /// @@ -258,19 +253,19 @@ namespace OpenIddict.EntityFramework /// The client identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - public virtual Task FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual ValueTask FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - return (from application in Applications - where application.ClientId == identifier - select application).FirstOrDefaultAsync(); + return new ValueTask((from application in Applications + where application.ClientId == identifier + select application).FirstOrDefaultAsync()); } /// @@ -278,11 +273,8 @@ namespace OpenIddict.EntityFramework /// /// The post_logout_redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified post_logout_redirect_uri. - /// - public virtual async Task> FindByPostLogoutRedirectUriAsync( + /// The client applications corresponding to the specified post_logout_redirect_uri. + public virtual IAsyncEnumerable FindByPostLogoutRedirectUriAsync( [NotNull] string address, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(address)) @@ -295,30 +287,10 @@ namespace OpenIddict.EntityFramework // are retrieved, a second pass is made to ensure only valid elements are returned. // Implementers that use this method in a hot path may want to override this method // to use SQL Server 2016 functions like JSON_VALUE to make the query more efficient. - var applications = await (from application in Applications - where application.PostLogoutRedirectUris.Contains(address) - select application).ToListAsync(cancellationToken); - - var builder = ImmutableArray.CreateBuilder(applications.Count); - - foreach (var application in applications) - { - foreach (var uri in await GetPostLogoutRedirectUrisAsync(application, cancellationToken)) - { - // Note: the post_logout_redirect_uri must be compared - // using case-sensitive "Simple String Comparison". - if (string.Equals(uri, address, StringComparison.Ordinal)) - { - builder.Add(application); - - break; - } - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return Applications.Where(application => application.PostLogoutRedirectUris.Contains(address)) + .AsAsyncEnumerable(cancellationToken) + .WhereAwait(async application => (await GetPostLogoutRedirectUrisAsync(application, cancellationToken)) + .Contains(address, StringComparer.Ordinal)); } /// @@ -326,11 +298,8 @@ namespace OpenIddict.EntityFramework /// /// The redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified redirect_uri. - /// - public virtual async Task> FindByRedirectUriAsync( + /// The client applications corresponding to the specified redirect_uri. + public virtual IAsyncEnumerable FindByRedirectUriAsync( [NotNull] string address, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(address)) @@ -343,30 +312,10 @@ namespace OpenIddict.EntityFramework // are retrieved, a second pass is made to ensure only valid elements are returned. // Implementers that use this method in a hot path may want to override this method // to use SQL Server 2016 functions like JSON_VALUE to make the query more efficient. - var applications = await (from application in Applications - where application.RedirectUris.Contains(address) - select application).ToListAsync(cancellationToken); - - var builder = ImmutableArray.CreateBuilder(applications.Count); - - foreach (var application in applications) - { - foreach (var uri in await GetRedirectUrisAsync(application, cancellationToken)) - { - // Note: the redirect_uri must be compared using case-sensitive "Simple String Comparison". - // See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest for more information. - if (string.Equals(uri, address, StringComparison.Ordinal)) - { - builder.Add(application); - - break; - } - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); + return Applications.Where(application => application.RedirectUris.Contains(address)) + .AsAsyncEnumerable(cancellationToken) + .WhereAwait(async application => (await GetRedirectUrisAsync(application, cancellationToken)) + .Contains(address, StringComparer.Ordinal)); } /// @@ -378,10 +327,10 @@ namespace OpenIddict.EntityFramework /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -390,7 +339,7 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(query)); } - return query(Applications, state).FirstOrDefaultAsync(cancellationToken); + return new ValueTask(query(Applications, state).FirstOrDefaultAsync(cancellationToken)); } /// @@ -687,11 +636,8 @@ namespace OpenIddict.EntityFramework /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) { var query = Applications.OrderBy(application => application.Id).AsQueryable(); @@ -706,7 +652,7 @@ namespace OpenIddict.EntityFramework query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + return query.AsAsyncEnumerable(cancellationToken); } /// @@ -717,11 +663,8 @@ namespace OpenIddict.EntityFramework /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -730,7 +673,7 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(query)); } - return ImmutableArray.CreateRange(await query(Applications, state).ToListAsync(cancellationToken)); + return query(Applications, state).AsAsyncEnumerable(cancellationToken); } /// @@ -739,10 +682,8 @@ namespace OpenIddict.EntityFramework /// The application. /// The client identifier associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetClientIdAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetClientIdAsync([NotNull] TApplication application, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (application == null) @@ -752,7 +693,7 @@ namespace OpenIddict.EntityFramework application.ClientId = identifier; - return Task.CompletedTask; + return default; } /// @@ -763,10 +704,8 @@ namespace OpenIddict.EntityFramework /// The application. /// The client secret associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetClientSecretAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetClientSecretAsync([NotNull] TApplication application, [CanBeNull] string secret, CancellationToken cancellationToken) { if (application == null) @@ -776,7 +715,7 @@ namespace OpenIddict.EntityFramework application.ClientSecret = secret; - return Task.CompletedTask; + return default; } /// @@ -785,10 +724,8 @@ namespace OpenIddict.EntityFramework /// The application. /// The client type associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetClientTypeAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetClientTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken) { if (application == null) @@ -798,7 +735,7 @@ namespace OpenIddict.EntityFramework application.Type = type; - return Task.CompletedTask; + return default; } /// @@ -807,10 +744,8 @@ namespace OpenIddict.EntityFramework /// The application. /// The consent type associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetConsentTypeAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetConsentTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken) { if (application == null) @@ -820,7 +755,7 @@ namespace OpenIddict.EntityFramework application.ConsentType = type; - return Task.CompletedTask; + return default; } /// @@ -829,10 +764,8 @@ namespace OpenIddict.EntityFramework /// The application. /// The display name associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetDisplayNameAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetDisplayNameAsync([NotNull] TApplication application, [CanBeNull] string name, CancellationToken cancellationToken) { if (application == null) @@ -842,7 +775,7 @@ namespace OpenIddict.EntityFramework application.DisplayName = name; - return Task.CompletedTask; + return default; } /// @@ -851,10 +784,8 @@ namespace OpenIddict.EntityFramework /// The application. /// The permissions associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPermissionsAsync([NotNull] TApplication application, ImmutableArray permissions, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPermissionsAsync([NotNull] TApplication application, ImmutableArray permissions, CancellationToken cancellationToken) { if (application == null) { @@ -865,12 +796,12 @@ namespace OpenIddict.EntityFramework { application.Permissions = null; - return Task.CompletedTask; + return default; } application.Permissions = new JArray(permissions.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -879,10 +810,8 @@ namespace OpenIddict.EntityFramework /// The application. /// The logout callback addresses associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPostLogoutRedirectUrisAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPostLogoutRedirectUrisAsync([NotNull] TApplication application, ImmutableArray addresses, CancellationToken cancellationToken) { if (application == null) @@ -894,12 +823,12 @@ namespace OpenIddict.EntityFramework { application.PostLogoutRedirectUris = null; - return Task.CompletedTask; + return default; } application.PostLogoutRedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -908,10 +837,8 @@ namespace OpenIddict.EntityFramework /// The application. /// The additional properties associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (application == null) { @@ -922,12 +849,12 @@ namespace OpenIddict.EntityFramework { application.Properties = null; - return Task.CompletedTask; + return default; } application.Properties = properties.ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -936,10 +863,8 @@ namespace OpenIddict.EntityFramework /// The application. /// The callback addresses associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetRedirectUrisAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetRedirectUrisAsync([NotNull] TApplication application, ImmutableArray addresses, CancellationToken cancellationToken) { if (application == null) @@ -951,12 +876,12 @@ namespace OpenIddict.EntityFramework { application.RedirectUris = null; - return Task.CompletedTask; + return default; } application.RedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -964,10 +889,8 @@ namespace OpenIddict.EntityFramework /// /// The application to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs index 15549405..05795c98 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs @@ -103,11 +103,11 @@ namespace OpenIddict.EntityFramework /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations in the database. /// - public virtual Task CountAsync(CancellationToken cancellationToken) - => Authorizations.LongCountAsync(); + public virtual ValueTask CountAsync(CancellationToken cancellationToken) + => new ValueTask(Authorizations.LongCountAsync()); /// /// Determines the number of authorizations that match the specified query. @@ -116,17 +116,17 @@ namespace OpenIddict.EntityFramework /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations that match the specified query. /// - public virtual Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + public virtual ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) { throw new ArgumentNullException(nameof(query)); } - return query(Authorizations).LongCountAsync(); + return new ValueTask(query(Authorizations).LongCountAsync()); } /// @@ -134,10 +134,8 @@ namespace OpenIddict.EntityFramework /// /// The authorization to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { @@ -146,7 +144,7 @@ namespace OpenIddict.EntityFramework Authorizations.Add(authorization); - return Context.SaveChangesAsync(cancellationToken); + return new ValueTask(Context.SaveChangesAsync(cancellationToken)); } /// @@ -154,10 +152,8 @@ namespace OpenIddict.EntityFramework /// /// The authorization to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { @@ -218,11 +214,8 @@ namespace OpenIddict.EntityFramework /// The subject associated with the authorization. /// The client associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the subject/client. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the subject/client. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -237,12 +230,11 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(client); - return ImmutableArray.CreateRange( - await (from authorization in Authorizations.Include(authorization => authorization.Application) - where authorization.Application != null && - authorization.Application.Id.Equals(key) && - authorization.Subject == subject - select authorization).ToListAsync(cancellationToken)); + return (from authorization in Authorizations.Include(authorization => authorization.Application) + where authorization.Application != null && + authorization.Application.Id.Equals(key) && + authorization.Subject == subject + select authorization).AsAsyncEnumerable(cancellationToken); } /// @@ -252,11 +244,8 @@ namespace OpenIddict.EntityFramework /// The client associated with the authorization. /// The authorization status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken) { @@ -277,13 +266,12 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(client); - return ImmutableArray.CreateRange( - await (from authorization in Authorizations.Include(authorization => authorization.Application) - where authorization.Application != null && - authorization.Application.Id.Equals(key) && - authorization.Subject == subject && - authorization.Status == status - select authorization).ToListAsync(cancellationToken)); + return (from authorization in Authorizations.Include(authorization => authorization.Application) + where authorization.Application != null && + authorization.Application.Id.Equals(key) && + authorization.Subject == subject && + authorization.Status == status + select authorization).AsAsyncEnumerable(cancellationToken); } /// @@ -294,11 +282,8 @@ namespace OpenIddict.EntityFramework /// The authorization status. /// The authorization type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken) { @@ -324,14 +309,13 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(client); - return ImmutableArray.CreateRange( - await (from authorization in Authorizations.Include(authorization => authorization.Application) - where authorization.Application != null && - authorization.Application.Id.Equals(key) && - authorization.Subject == subject && - authorization.Status == status && - authorization.Type == type - select authorization).ToListAsync(cancellationToken)); + return (from authorization in Authorizations.Include(authorization => authorization.Application) + where authorization.Application != null && + authorization.Application.Id.Equals(key) && + authorization.Subject == subject && + authorization.Status == status && + authorization.Type == type + select authorization).AsAsyncEnumerable(cancellationToken); } /// @@ -343,51 +327,22 @@ namespace OpenIddict.EntityFramework /// The authorization type. /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken) - { - var authorizations = await FindAsync(subject, client, status, type, cancellationToken); - if (authorizations.IsEmpty) - { - return ImmutableArray.Create(); - } - - var builder = ImmutableArray.CreateBuilder(authorizations.Length); - - foreach (var authorization in authorizations) - { - async Task HasScopesAsync() - => (await GetScopesAsync(authorization, cancellationToken)) - .ToImmutableHashSet(StringComparer.Ordinal) - .IsSupersetOf(scopes); - - if (await HasScopesAsync()) - { - builder.Add(authorization); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); - } + => FindAsync(subject, client, status, type, cancellationToken) + .WhereAwait(async authorization => new HashSet( + await GetScopesAsync(authorization, cancellationToken), StringComparer.Ordinal).IsSupersetOf(scopes)); /// /// Retrieves the list of authorizations corresponding to the specified application identifier. /// /// The application identifier associated with the authorizations. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified application. - /// - public virtual async Task> FindByApplicationIdAsync( + /// The authorizations corresponding to the specified application. + public virtual IAsyncEnumerable FindByApplicationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) @@ -397,10 +352,9 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(identifier); - return ImmutableArray.CreateRange( - await (from authorization in Authorizations.Include(authorization => authorization.Application) - where authorization.Application.Id.Equals(key) - select authorization).ToListAsync(cancellationToken)); + return (from authorization in Authorizations.Include(authorization => authorization.Application) + where authorization.Application.Id.Equals(key) + select authorization).AsAsyncEnumerable(cancellationToken); } /// @@ -409,10 +363,10 @@ namespace OpenIddict.EntityFramework /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the authorization corresponding to the identifier. /// - public virtual Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -421,9 +375,9 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(identifier); - return (from authorization in Authorizations.Include(authorization => authorization.Application) - where authorization.Id.Equals(key) - select authorization).FirstOrDefaultAsync(cancellationToken); + return new ValueTask((from authorization in Authorizations.Include(authorization => authorization.Application) + where authorization.Id.Equals(key) + select authorization).FirstOrDefaultAsync(cancellationToken)); } /// @@ -431,11 +385,8 @@ namespace OpenIddict.EntityFramework /// /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified subject. - /// - public virtual async Task> FindBySubjectAsync( + /// The authorizations corresponding to the specified subject. + public virtual IAsyncEnumerable FindBySubjectAsync( [NotNull] string subject, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -443,10 +394,9 @@ namespace OpenIddict.EntityFramework throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); } - return ImmutableArray.CreateRange( - await (from authorization in Authorizations.Include(authorization => authorization.Application) - where authorization.Subject == subject - select authorization).ToListAsync(cancellationToken)); + return (from authorization in Authorizations.Include(authorization => authorization.Application) + where authorization.Subject == subject + select authorization).AsAsyncEnumerable(cancellationToken); } /// @@ -494,10 +444,10 @@ namespace OpenIddict.EntityFramework /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -506,8 +456,8 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(query)); } - return query( - Authorizations.Include(authorization => authorization.Application), state).FirstOrDefaultAsync(cancellationToken); + return new ValueTask(query( + Authorizations.Include(authorization => authorization.Application), state).FirstOrDefaultAsync(cancellationToken)); } /// @@ -690,11 +640,8 @@ namespace OpenIddict.EntityFramework /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) { var query = Authorizations.Include(authorization => authorization.Application) @@ -711,7 +658,7 @@ namespace OpenIddict.EntityFramework query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + return query.AsAsyncEnumerable(cancellationToken); } /// @@ -722,11 +669,8 @@ namespace OpenIddict.EntityFramework /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -735,18 +679,15 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(query)); } - return ImmutableArray.CreateRange(await query( - Authorizations.Include(authorization => authorization.Application), state).ToListAsync(cancellationToken)); + return query(Authorizations.Include(authorization => authorization.Application), state).AsAsyncEnumerable(cancellationToken); } /// /// Removes the authorizations that are marked as invalid and the ad-hoc ones that have no valid/nonexpired token attached. /// /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task PruneAsync(CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask PruneAsync(CancellationToken cancellationToken) { // Note: Entity Framework 6.x doesn't support set-based deletes, which prevents removing // entities in a single command without having to retrieve and materialize them first. @@ -770,7 +711,7 @@ namespace OpenIddict.EntityFramework } } - for (var offset = 0; offset < 100_000; offset = offset + 1_000) + for (var offset = 0; offset < 100_000; offset += 1_000) { cancellationToken.ThrowIfCancellationRequested(); @@ -778,44 +719,43 @@ namespace OpenIddict.EntityFramework // after it was retrieved from the database, the following logic is executed in // a repeatable read transaction, that will put a lock on the retrieved entries // and thus prevent them from being concurrently modified outside this block. - using (var transaction = CreateTransaction()) + using var transaction = CreateTransaction(); + + var authorizations = + await (from authorization in Authorizations.Include(authorization => authorization.Tokens) + where authorization.Status != OpenIddictConstants.Statuses.Valid || + (authorization.Type == OpenIddictConstants.AuthorizationTypes.AdHoc && + !authorization.Tokens.Any(token => token.Status == OpenIddictConstants.Statuses.Valid && + token.ExpirationDate > DateTimeOffset.UtcNow)) + orderby authorization.Id + select authorization).Skip(offset).Take(1_000).ToListAsync(cancellationToken); + + if (authorizations.Count == 0) { - var authorizations = - await (from authorization in Authorizations.Include(authorization => authorization.Tokens) - where authorization.Status != OpenIddictConstants.Statuses.Valid || - (authorization.Type == OpenIddictConstants.AuthorizationTypes.AdHoc && - !authorization.Tokens.Any(token => token.Status == OpenIddictConstants.Statuses.Valid && - token.ExpirationDate > DateTimeOffset.UtcNow)) - orderby authorization.Id - select authorization).Skip(offset).Take(1_000).ToListAsync(cancellationToken); - - if (authorizations.Count == 0) - { - break; - } + break; + } - // Note: new tokens may be attached after the authorizations were retrieved - // from the database since the transaction level is deliberately limited to - // repeatable read instead of serializable for performance reasons). In this - // case, the operation will fail, which is considered an acceptable risk. - Authorizations.RemoveRange(authorizations); - Tokens.RemoveRange(authorizations.SelectMany(authorization => authorization.Tokens)); + // Note: new tokens may be attached after the authorizations were retrieved + // from the database since the transaction level is deliberately limited to + // repeatable read instead of serializable for performance reasons). In this + // case, the operation will fail, which is considered an acceptable risk. + Authorizations.RemoveRange(authorizations); + Tokens.RemoveRange(authorizations.SelectMany(authorization => authorization.Tokens)); - try - { - await Context.SaveChangesAsync(cancellationToken); - transaction?.Commit(); - } + try + { + await Context.SaveChangesAsync(cancellationToken); + transaction?.Commit(); + } - catch (Exception exception) + catch (Exception exception) + { + if (exceptions == null) { - if (exceptions == null) - { - exceptions = new List(capacity: 1); - } - - exceptions.Add(exception); + exceptions = new List(capacity: 1); } + + exceptions.Add(exception); } } @@ -832,9 +772,9 @@ namespace OpenIddict.EntityFramework /// The unique identifier associated with the client application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task SetApplicationIdAsync([NotNull] TAuthorization authorization, + public virtual async ValueTask SetApplicationIdAsync([NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (authorization == null) @@ -878,9 +818,9 @@ namespace OpenIddict.EntityFramework /// The additional properties associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken) + public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (authorization == null) { @@ -891,12 +831,12 @@ namespace OpenIddict.EntityFramework { authorization.Properties = null; - return Task.CompletedTask; + return default; } authorization.Properties = properties.ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -906,9 +846,9 @@ namespace OpenIddict.EntityFramework /// The scopes associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetScopesAsync([NotNull] TAuthorization authorization, + public virtual ValueTask SetScopesAsync([NotNull] TAuthorization authorization, ImmutableArray scopes, CancellationToken cancellationToken) { if (authorization == null) @@ -920,12 +860,12 @@ namespace OpenIddict.EntityFramework { authorization.Scopes = null; - return Task.CompletedTask; + return default; } authorization.Scopes = new JArray(scopes.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -935,9 +875,9 @@ namespace OpenIddict.EntityFramework /// The status associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetStatusAsync([NotNull] TAuthorization authorization, + public virtual ValueTask SetStatusAsync([NotNull] TAuthorization authorization, [CanBeNull] string status, CancellationToken cancellationToken) { if (authorization == null) @@ -947,7 +887,7 @@ namespace OpenIddict.EntityFramework authorization.Status = status; - return Task.CompletedTask; + return default; } /// @@ -957,9 +897,9 @@ namespace OpenIddict.EntityFramework /// The subject associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetSubjectAsync([NotNull] TAuthorization authorization, + public virtual ValueTask SetSubjectAsync([NotNull] TAuthorization authorization, [CanBeNull] string subject, CancellationToken cancellationToken) { if (authorization == null) @@ -969,7 +909,7 @@ namespace OpenIddict.EntityFramework authorization.Subject = subject; - return Task.CompletedTask; + return default; } /// @@ -979,9 +919,9 @@ namespace OpenIddict.EntityFramework /// The type associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetTypeAsync([NotNull] TAuthorization authorization, + public virtual ValueTask SetTypeAsync([NotNull] TAuthorization authorization, [CanBeNull] string type, CancellationToken cancellationToken) { if (authorization == null) @@ -991,7 +931,7 @@ namespace OpenIddict.EntityFramework authorization.Type = type; - return Task.CompletedTask; + return default; } /// @@ -1000,9 +940,9 @@ namespace OpenIddict.EntityFramework /// The authorization to update. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + public virtual async ValueTask UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs index 0f657bae..8619b6d0 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs @@ -5,6 +5,7 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Data.Entity; @@ -85,11 +86,11 @@ namespace OpenIddict.EntityFramework /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes in the database. /// - public virtual Task CountAsync(CancellationToken cancellationToken) - => Scopes.LongCountAsync(); + public virtual ValueTask CountAsync(CancellationToken cancellationToken) + => new ValueTask(Scopes.LongCountAsync()); /// /// Determines the number of scopes that match the specified query. @@ -98,17 +99,17 @@ namespace OpenIddict.EntityFramework /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes that match the specified query. /// - public virtual Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + public virtual ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) { throw new ArgumentNullException(nameof(query)); } - return query(Scopes).LongCountAsync(); + return new ValueTask(query(Scopes).LongCountAsync()); } /// @@ -116,10 +117,8 @@ namespace OpenIddict.EntityFramework /// /// The scope to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { @@ -128,7 +127,7 @@ namespace OpenIddict.EntityFramework Scopes.Add(scope); - return Context.SaveChangesAsync(cancellationToken); + return new ValueTask(Context.SaveChangesAsync(cancellationToken)); } /// @@ -136,10 +135,8 @@ namespace OpenIddict.EntityFramework /// /// The scope to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { @@ -168,10 +165,10 @@ namespace OpenIddict.EntityFramework /// The unique identifier associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the identifier. /// - public virtual Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -180,9 +177,9 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(identifier); - return (from scope in Scopes - where scope.Id.Equals(key) - select scope).FirstOrDefaultAsync(cancellationToken); + return new ValueTask((from scope in Scopes + where scope.Id.Equals(key) + select scope).FirstOrDefaultAsync(cancellationToken)); } /// @@ -191,19 +188,19 @@ namespace OpenIddict.EntityFramework /// The name associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the specified name. /// - public virtual Task FindByNameAsync([NotNull] string name, CancellationToken cancellationToken) + public virtual ValueTask FindByNameAsync([NotNull] string name, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(name)) { throw new ArgumentException("The scope name cannot be null or empty.", nameof(name)); } - return (from scope in Scopes - where scope.Name == name - select scope).FirstOrDefaultAsync(cancellationToken); + return new ValueTask((from scope in Scopes + where scope.Name == name + select scope).FirstOrDefaultAsync(cancellationToken)); } /// @@ -211,11 +208,8 @@ namespace OpenIddict.EntityFramework /// /// The names associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes corresponding to the specified names. - /// - public virtual async Task> FindByNamesAsync( + /// The scopes corresponding to the specified names. + public virtual IAsyncEnumerable FindByNamesAsync( ImmutableArray names, CancellationToken cancellationToken) { if (names.Any(name => string.IsNullOrEmpty(name))) @@ -223,10 +217,9 @@ namespace OpenIddict.EntityFramework throw new ArgumentException("Scope names cannot be null or empty.", nameof(names)); } - return ImmutableArray.CreateRange( - await (from scope in Scopes - where names.Contains(scope.Name) - select scope).ToListAsync(cancellationToken)); + return (from scope in Scopes + where names.Contains(scope.Name) + select scope).AsAsyncEnumerable(cancellationToken); } /// @@ -234,11 +227,8 @@ namespace OpenIddict.EntityFramework /// /// The resource associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes associated with the specified resource. - /// - public virtual async Task> FindByResourceAsync( + /// The scopes associated with the specified resource. + public virtual IAsyncEnumerable FindByResourceAsync( [NotNull] string resource, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(resource)) @@ -251,22 +241,9 @@ namespace OpenIddict.EntityFramework // are retrieved, a second pass is made to ensure only valid elements are returned. // Implementers that use this method in a hot path may want to override this method // to use SQL Server 2016 functions like JSON_VALUE to make the query more efficient. - var scopes = await (from scope in Scopes - where scope.Resources.Contains(resource) - select scope).ToListAsync(cancellationToken); - - var builder = ImmutableArray.CreateBuilder(); - - foreach (var scope in scopes) - { - var resources = await GetResourcesAsync(scope, cancellationToken); - if (resources.Contains(resource, StringComparer.OrdinalIgnoreCase)) - { - builder.Add(scope); - } - } - - return builder.ToImmutable(); + return Scopes.Where(scope => scope.Resources.Contains(resource)) + .AsAsyncEnumerable(cancellationToken) + .WhereAwait(async scope => (await GetResourcesAsync(scope, cancellationToken)).Contains(resource, StringComparer.Ordinal)); } /// @@ -278,10 +255,10 @@ namespace OpenIddict.EntityFramework /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -290,7 +267,7 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(query)); } - return query(Scopes, state).FirstOrDefaultAsync(cancellationToken); + return new ValueTask(query(Scopes, state).FirstOrDefaultAsync(cancellationToken)); } /// @@ -473,11 +450,8 @@ namespace OpenIddict.EntityFramework /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) { var query = Scopes.OrderBy(scope => scope.Id).AsQueryable(); @@ -492,7 +466,7 @@ namespace OpenIddict.EntityFramework query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + return query.AsAsyncEnumerable(cancellationToken); } /// @@ -503,11 +477,8 @@ namespace OpenIddict.EntityFramework /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -516,7 +487,7 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(query)); } - return ImmutableArray.CreateRange(await query(Scopes, state).ToListAsync(cancellationToken)); + return query(Scopes, state).AsAsyncEnumerable(cancellationToken); } /// @@ -525,10 +496,8 @@ namespace OpenIddict.EntityFramework /// The scope. /// The description associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken) { if (scope == null) { @@ -537,7 +506,7 @@ namespace OpenIddict.EntityFramework scope.Description = description; - return Task.CompletedTask; + return default; } /// @@ -546,10 +515,8 @@ namespace OpenIddict.EntityFramework /// The scope. /// The display name associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetDisplayNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetDisplayNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) { if (scope == null) { @@ -558,7 +525,7 @@ namespace OpenIddict.EntityFramework scope.DisplayName = name; - return Task.CompletedTask; + return default; } /// @@ -567,10 +534,8 @@ namespace OpenIddict.EntityFramework /// The scope. /// The name associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) { if (scope == null) { @@ -579,7 +544,7 @@ namespace OpenIddict.EntityFramework scope.Name = name; - return Task.CompletedTask; + return default; } /// @@ -588,10 +553,8 @@ namespace OpenIddict.EntityFramework /// The scope. /// The additional properties associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (scope == null) { @@ -602,12 +565,12 @@ namespace OpenIddict.EntityFramework { scope.Properties = null; - return Task.CompletedTask; + return default; } scope.Properties = properties.ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -616,10 +579,8 @@ namespace OpenIddict.EntityFramework /// The scope. /// The resources associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetResourcesAsync([NotNull] TScope scope, ImmutableArray resources, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetResourcesAsync([NotNull] TScope scope, ImmutableArray resources, CancellationToken cancellationToken) { if (scope == null) { @@ -630,12 +591,12 @@ namespace OpenIddict.EntityFramework { scope.Resources = null; - return Task.CompletedTask; + return default; } scope.Resources = new JArray(resources.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -643,10 +604,8 @@ namespace OpenIddict.EntityFramework /// /// The scope to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs index ed6ef8e5..57297d2f 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs @@ -103,11 +103,11 @@ namespace OpenIddict.EntityFramework /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications in the database. /// - public virtual Task CountAsync(CancellationToken cancellationToken) - => Tokens.LongCountAsync(); + public virtual ValueTask CountAsync(CancellationToken cancellationToken) + => new ValueTask(Tokens.LongCountAsync()); /// /// Determines the number of tokens that match the specified query. @@ -116,17 +116,17 @@ namespace OpenIddict.EntityFramework /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of tokens that match the specified query. /// - public virtual Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + public virtual ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) { throw new ArgumentNullException(nameof(query)); } - return query(Tokens).LongCountAsync(); + return new ValueTask(query(Tokens).LongCountAsync()); } /// @@ -134,10 +134,8 @@ namespace OpenIddict.EntityFramework /// /// The token to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { @@ -146,7 +144,7 @@ namespace OpenIddict.EntityFramework Tokens.Add(token); - return Context.SaveChangesAsync(cancellationToken); + return new ValueTask(Context.SaveChangesAsync(cancellationToken)); } /// @@ -154,10 +152,8 @@ namespace OpenIddict.EntityFramework /// /// The token to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { @@ -187,11 +183,8 @@ namespace OpenIddict.EntityFramework /// The subject associated with the token. /// The client associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the subject/client. - /// - public virtual async Task> FindAsync([NotNull] string subject, + /// The tokens corresponding to the subject/client. + public virtual IAsyncEnumerable FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -206,12 +199,11 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(client); - return ImmutableArray.CreateRange( - await (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) - where token.Application != null && - token.Application.Id.Equals(key) && - token.Subject == subject - select token).ToListAsync(cancellationToken)); + return (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) + where token.Application != null && + token.Application.Id.Equals(key) && + token.Subject == subject + select token).AsAsyncEnumerable(cancellationToken); } /// @@ -221,11 +213,8 @@ namespace OpenIddict.EntityFramework /// The client associated with the token. /// The token status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The tokens corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken) { @@ -246,13 +235,12 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(client); - return ImmutableArray.CreateRange( - await (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) - where token.Application != null && - token.Application.Id.Equals(key) && - token.Subject == subject && - token.Status == status - select token).ToListAsync(cancellationToken)); + return (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) + where token.Application != null && + token.Application.Id.Equals(key) && + token.Subject == subject && + token.Status == status + select token).AsAsyncEnumerable(cancellationToken); } /// @@ -263,11 +251,8 @@ namespace OpenIddict.EntityFramework /// The token status. /// The token type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The tokens corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken) { @@ -293,14 +278,13 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(client); - return ImmutableArray.CreateRange( - await (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) - where token.Application != null && - token.Application.Id.Equals(key) && - token.Subject == subject && - token.Status == status && - token.Type == type - select token).ToListAsync(cancellationToken)); + return (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) + where token.Application != null && + token.Application.Id.Equals(key) && + token.Subject == subject && + token.Status == status && + token.Type == type + select token).AsAsyncEnumerable(cancellationToken); } /// @@ -308,11 +292,8 @@ namespace OpenIddict.EntityFramework /// /// The application identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified application. - /// - public virtual async Task> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + /// The tokens corresponding to the specified application. + public virtual IAsyncEnumerable FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -321,11 +302,10 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(identifier); - return ImmutableArray.CreateRange( - await (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) - where token.Application != null && - token.Application.Id.Equals(key) - select token).ToListAsync(cancellationToken)); + return (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) + where token.Application != null && + token.Application.Id.Equals(key) + select token).AsAsyncEnumerable(cancellationToken); } /// @@ -333,11 +313,8 @@ namespace OpenIddict.EntityFramework /// /// The authorization identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified authorization. - /// - public virtual async Task> FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + /// Tokens corresponding to the specified authorization. + public virtual IAsyncEnumerable FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -346,11 +323,10 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(identifier); - return ImmutableArray.CreateRange( - await (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) - where token.Authorization != null && - token.Authorization.Id.Equals(key) - select token).ToListAsync(cancellationToken)); + return (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) + where token.Authorization != null && + token.Authorization.Id.Equals(key) + select token).AsAsyncEnumerable(cancellationToken); } /// @@ -359,10 +335,10 @@ namespace OpenIddict.EntityFramework /// The unique identifier associated with the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the token corresponding to the unique identifier. /// - public virtual Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -371,9 +347,9 @@ namespace OpenIddict.EntityFramework var key = ConvertIdentifierFromString(identifier); - return (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) - where token.Id.Equals(key) - select token).FirstOrDefaultAsync(cancellationToken); + return new ValueTask((from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) + where token.Id.Equals(key) + select token).FirstOrDefaultAsync(cancellationToken)); } /// @@ -383,19 +359,19 @@ namespace OpenIddict.EntityFramework /// The reference identifier associated with the tokens. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the tokens corresponding to the specified reference identifier. /// - public virtual Task FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual ValueTask FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - return (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) - where token.ReferenceId == identifier - select token).FirstOrDefaultAsync(cancellationToken); + return new ValueTask((from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) + where token.ReferenceId == identifier + select token).FirstOrDefaultAsync(cancellationToken)); } /// @@ -403,21 +379,17 @@ namespace OpenIddict.EntityFramework /// /// The subject associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified subject. - /// - public virtual async Task> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken) + /// The tokens corresponding to the specified subject. + public virtual IAsyncEnumerable FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) { throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); } - return ImmutableArray.CreateRange( - await (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) - where token.Subject == subject - select token).ToListAsync(cancellationToken)); + return (from token in Tokens.Include(token => token.Application).Include(token => token.Authorization) + where token.Subject == subject + select token).AsAsyncEnumerable(cancellationToken); } /// @@ -465,10 +437,10 @@ namespace OpenIddict.EntityFramework /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -477,9 +449,9 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(query)); } - return query( + return new ValueTask(query( Tokens.Include(token => token.Application) - .Include(token => token.Authorization), state).FirstOrDefaultAsync(cancellationToken); + .Include(token => token.Authorization), state).FirstOrDefaultAsync(cancellationToken)); } /// @@ -739,11 +711,8 @@ namespace OpenIddict.EntityFramework /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) { var query = Tokens.Include(token => token.Application) @@ -761,7 +730,7 @@ namespace OpenIddict.EntityFramework query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + return query.AsAsyncEnumerable(cancellationToken); } /// @@ -772,11 +741,8 @@ namespace OpenIddict.EntityFramework /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -785,19 +751,17 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(query)); } - return ImmutableArray.CreateRange(await query( + return query( Tokens.Include(token => token.Application) - .Include(token => token.Authorization), state).ToListAsync(cancellationToken)); + .Include(token => token.Authorization), state).AsAsyncEnumerable(cancellationToken); } /// /// Removes the tokens that are marked as expired or invalid. /// /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task PruneAsync(CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask PruneAsync(CancellationToken cancellationToken) { // Note: Entity Framework 6.x doesn't support set-based deletes, which prevents removing // entities in a single command without having to retrieve and materialize them first. @@ -823,7 +787,7 @@ namespace OpenIddict.EntityFramework // Note: to avoid sending too many queries, the maximum number of elements // that can be removed by a single call to PruneAsync() is limited to 50000. - for (var offset = 0; offset < 50_000; offset = offset + 1_000) + for (var offset = 0; offset < 50_000; offset += 1_000) { cancellationToken.ThrowIfCancellationRequested(); @@ -831,37 +795,35 @@ namespace OpenIddict.EntityFramework // after it was retrieved from the database, the following logic is executed in // a repeatable read transaction, that will put a lock on the retrieved entries // and thus prevent them from being concurrently modified outside this block. - using (var transaction = CreateTransaction()) + using var transaction = CreateTransaction(); + + var tokens = await (from token in Tokens + where token.Status != OpenIddictConstants.Statuses.Valid || + token.ExpirationDate < DateTimeOffset.UtcNow + orderby token.Id + select token).Skip(offset).Take(1_000).ToListAsync(cancellationToken); + + if (tokens.Count == 0) { - var tokens = - await (from token in Tokens - where token.Status != OpenIddictConstants.Statuses.Valid || - token.ExpirationDate < DateTimeOffset.UtcNow - orderby token.Id - select token).Skip(offset).Take(1_000).ToListAsync(cancellationToken); - - if (tokens.Count == 0) - { - break; - } + break; + } - Tokens.RemoveRange(tokens); + Tokens.RemoveRange(tokens); - try - { - await Context.SaveChangesAsync(cancellationToken); - transaction?.Commit(); - } + try + { + await Context.SaveChangesAsync(cancellationToken); + transaction?.Commit(); + } - catch (Exception exception) + catch (Exception exception) + { + if (exceptions == null) { - if (exceptions == null) - { - exceptions = new List(capacity: 1); - } - - exceptions.Add(exception); + exceptions = new List(capacity: 1); } + + exceptions.Add(exception); } } @@ -877,10 +839,8 @@ namespace OpenIddict.EntityFramework /// The token. /// The unique identifier associated with the client application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task SetApplicationIdAsync([NotNull] TToken token, + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (token == null) @@ -923,10 +883,8 @@ namespace OpenIddict.EntityFramework /// The token. /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task SetAuthorizationIdAsync([NotNull] TToken token, + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (token == null) @@ -969,10 +927,8 @@ namespace OpenIddict.EntityFramework /// The token. /// The creation date. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetCreationDateAsync([NotNull] TToken token, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetCreationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken) { if (token == null) @@ -982,7 +938,7 @@ namespace OpenIddict.EntityFramework token.CreationDate = date; - return Task.CompletedTask; + return default; } /// @@ -991,10 +947,8 @@ namespace OpenIddict.EntityFramework /// The token. /// The expiration date. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetExpirationDateAsync([NotNull] TToken token, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetExpirationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken) { if (token == null) @@ -1004,7 +958,7 @@ namespace OpenIddict.EntityFramework token.ExpirationDate = date; - return Task.CompletedTask; + return default; } /// @@ -1013,10 +967,8 @@ namespace OpenIddict.EntityFramework /// The token. /// The payload associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken) { if (token == null) { @@ -1025,7 +977,7 @@ namespace OpenIddict.EntityFramework token.Payload = payload; - return Task.CompletedTask; + return default; } /// @@ -1034,10 +986,8 @@ namespace OpenIddict.EntityFramework /// The token. /// The additional properties associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (token == null) { @@ -1048,12 +998,12 @@ namespace OpenIddict.EntityFramework { token.Properties = null; - return Task.CompletedTask; + return default; } token.Properties = properties.ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -1064,10 +1014,8 @@ namespace OpenIddict.EntityFramework /// The token. /// The reference identifier associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (token == null) { @@ -1076,7 +1024,7 @@ namespace OpenIddict.EntityFramework token.ReferenceId = identifier; - return Task.CompletedTask; + return default; } /// @@ -1085,10 +1033,8 @@ namespace OpenIddict.EntityFramework /// The token. /// The status associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken) { if (token == null) { @@ -1097,7 +1043,7 @@ namespace OpenIddict.EntityFramework token.Status = status; - return Task.CompletedTask; + return default; } /// @@ -1106,10 +1052,8 @@ namespace OpenIddict.EntityFramework /// The token. /// The subject associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken) { if (token == null) { @@ -1118,7 +1062,7 @@ namespace OpenIddict.EntityFramework token.Subject = subject; - return Task.CompletedTask; + return default; } /// @@ -1127,10 +1071,8 @@ namespace OpenIddict.EntityFramework /// The token. /// The token type associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken) { if (token == null) { @@ -1139,7 +1081,7 @@ namespace OpenIddict.EntityFramework token.Type = type; - return Task.CompletedTask; + return default; } /// @@ -1147,10 +1089,8 @@ namespace OpenIddict.EntityFramework /// /// The token to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreHelpers.cs b/src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreHelpers.cs index 8cdbc5a5..c3f58649 100644 --- a/src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreHelpers.cs +++ b/src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreHelpers.cs @@ -13,7 +13,7 @@ using OpenIddict.EntityFrameworkCore.Models; namespace Microsoft.EntityFrameworkCore { /// - /// Exposes extensions allowing to register the OpenIddict Entity Framework Core entity sets. + /// Exposes extensions simplifying the integration between OpenIddict and Entity Framework Core. /// public static class OpenIddictEntityFrameworkCoreHelpers { diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs index cef96369..ca74f0d2 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs @@ -124,11 +124,11 @@ namespace OpenIddict.EntityFrameworkCore /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications in the database. /// - public virtual Task CountAsync(CancellationToken cancellationToken) - => Applications.LongCountAsync(); + public virtual ValueTask CountAsync(CancellationToken cancellationToken) + => new ValueTask(Applications.AsQueryable().LongCountAsync()); /// /// Determines the number of applications that match the specified query. @@ -137,17 +137,17 @@ namespace OpenIddict.EntityFrameworkCore /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications that match the specified query. /// - public virtual Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + public virtual ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) { throw new ArgumentNullException(nameof(query)); } - return query(Applications).LongCountAsync(); + return new ValueTask(query(Applications).LongCountAsync()); } /// @@ -155,10 +155,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The application to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { @@ -167,7 +165,7 @@ namespace OpenIddict.EntityFrameworkCore Context.Add(application); - return Context.SaveChangesAsync(cancellationToken); + return new ValueTask(Context.SaveChangesAsync(cancellationToken)); } /// @@ -175,17 +173,15 @@ namespace OpenIddict.EntityFrameworkCore /// /// The application to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { throw new ArgumentNullException(nameof(application)); } - async Task CreateTransactionAsync() + async ValueTask CreateTransactionAsync() { // Note: transactions that specify an explicit isolation level are only supported by // relational providers and trying to use them with a different provider results in @@ -234,41 +230,40 @@ namespace OpenIddict.EntityFrameworkCore // To prevent an SQL exception from being thrown if a new associated entity is // created after the existing entries have been listed, the following logic is // executed in a serializable transaction, that will lock the affected tables. - using (var transaction = await CreateTransactionAsync()) - { - // Remove all the authorizations associated with the application and - // the tokens attached to these implicit or explicit authorizations. - foreach (var authorization in await ListAuthorizationsAsync()) - { - foreach (var token in authorization.Tokens) - { - Context.Remove(token); - } - - Context.Remove(authorization); - } + using var transaction = await CreateTransactionAsync(); - // Remove all the tokens associated with the application. - foreach (var token in await ListTokensAsync()) + // Remove all the authorizations associated with the application and + // the tokens attached to these implicit or explicit authorizations. + foreach (var authorization in await ListAuthorizationsAsync()) + { + foreach (var token in authorization.Tokens) { Context.Remove(token); } - Context.Remove(application); + Context.Remove(authorization); + } - try - { - await Context.SaveChangesAsync(cancellationToken); - transaction?.Commit(); - } + // Remove all the tokens associated with the application. + foreach (var token in await ListTokensAsync()) + { + Context.Remove(token); + } - catch (DbUpdateConcurrencyException exception) - { - throw new OpenIddictExceptions.ConcurrencyException(new StringBuilder() - .AppendLine("The application was concurrently updated and cannot be persisted in its current state.") - .Append("Reload the application from the database and retry the operation.") - .ToString(), exception); - } + Context.Remove(application); + + try + { + await Context.SaveChangesAsync(cancellationToken); + transaction?.Commit(); + } + + catch (DbUpdateConcurrencyException exception) + { + throw new OpenIddictExceptions.ConcurrencyException(new StringBuilder() + .AppendLine("The application was concurrently updated and cannot be persisted in its current state.") + .Append("Reload the application from the database and retry the operation.") + .ToString(), exception); } } @@ -287,17 +282,17 @@ namespace OpenIddict.EntityFrameworkCore /// The client identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - public virtual Task FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual ValueTask FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - return FindByClientId(Context, identifier); + return new ValueTask(FindByClientId(Context, identifier)); } /// @@ -315,17 +310,17 @@ namespace OpenIddict.EntityFrameworkCore /// The unique identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - public virtual Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - return FindById(Context, ConvertIdentifierFromString(identifier)); + return new ValueTask(FindById(Context, ConvertIdentifierFromString(identifier))); } /// @@ -348,11 +343,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The post_logout_redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified post_logout_redirect_uri. - /// - public virtual async Task> FindByPostLogoutRedirectUriAsync( + /// The client applications corresponding to the specified post_logout_redirect_uri. + public virtual IAsyncEnumerable FindByPostLogoutRedirectUriAsync( [NotNull] string address, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(address)) @@ -360,24 +352,9 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The address cannot be null or empty.", nameof(address)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var application in FindByPostLogoutRedirectUri(Context, address)) - { - foreach (var uri in await GetPostLogoutRedirectUrisAsync(application, cancellationToken)) - { - // Note: the post_logout_redirect_uri must be compared - // using case-sensitive "Simple String Comparison". - if (string.Equals(uri, address, StringComparison.Ordinal)) - { - builder.Add(application); - - break; - } - } - } - - return builder.ToImmutable(); + return FindByPostLogoutRedirectUri(Context, address) + .WhereAwait(async application => (await GetPostLogoutRedirectUrisAsync(application, cancellationToken)) + .Contains(address, StringComparer.Ordinal)); } /// @@ -400,11 +377,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified redirect_uri. - /// - public virtual async Task> FindByRedirectUriAsync( + /// The client applications corresponding to the specified redirect_uri. + public virtual IAsyncEnumerable FindByRedirectUriAsync( [NotNull] string address, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(address)) @@ -412,24 +386,9 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The address cannot be null or empty.", nameof(address)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var application in FindByRedirectUri(Context, address)) - { - foreach (var uri in await GetRedirectUrisAsync(application, cancellationToken)) - { - // Note: the redirect_uri must be compared using case-sensitive "Simple String Comparison". - // See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest for more information. - if (string.Equals(uri, address, StringComparison.Ordinal)) - { - builder.Add(application); - - break; - } - } - } - - return builder.ToImmutable(); + return FindByRedirectUri(Context, address) + .WhereAwait(async application => (await GetRedirectUrisAsync(application, cancellationToken)) + .Contains(address, StringComparer.Ordinal)); } /// @@ -441,10 +400,10 @@ namespace OpenIddict.EntityFrameworkCore /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -453,7 +412,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(query)); } - return query(Applications.AsTracking(), state).FirstOrDefaultAsync(cancellationToken); + return new ValueTask(query(Applications.AsTracking(), state).FirstOrDefaultAsync(cancellationToken)); } /// @@ -750,14 +709,11 @@ namespace OpenIddict.EntityFrameworkCore /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) { - var query = Applications.OrderBy(application => application.Id).AsTracking(); + var query = Applications.AsQueryable().OrderBy(application => application.Id).AsTracking(); if (offset.HasValue) { @@ -769,7 +725,7 @@ namespace OpenIddict.EntityFrameworkCore query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + return query.AsAsyncEnumerable(); } /// @@ -780,11 +736,8 @@ namespace OpenIddict.EntityFrameworkCore /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -793,7 +746,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(query)); } - return ImmutableArray.CreateRange(await query(Applications.AsTracking(), state).ToListAsync(cancellationToken)); + return query(Applications.AsTracking(), state).AsAsyncEnumerable(); } /// @@ -802,10 +755,8 @@ namespace OpenIddict.EntityFrameworkCore /// The application. /// The client identifier associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetClientIdAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetClientIdAsync([NotNull] TApplication application, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (application == null) @@ -815,7 +766,7 @@ namespace OpenIddict.EntityFrameworkCore application.ClientId = identifier; - return Task.CompletedTask; + return default; } /// @@ -826,10 +777,8 @@ namespace OpenIddict.EntityFrameworkCore /// The application. /// The client secret associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetClientSecretAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetClientSecretAsync([NotNull] TApplication application, [CanBeNull] string secret, CancellationToken cancellationToken) { if (application == null) @@ -839,7 +788,7 @@ namespace OpenIddict.EntityFrameworkCore application.ClientSecret = secret; - return Task.CompletedTask; + return default; } /// @@ -848,10 +797,8 @@ namespace OpenIddict.EntityFrameworkCore /// The application. /// The client type associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetClientTypeAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetClientTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken) { if (application == null) @@ -861,7 +808,7 @@ namespace OpenIddict.EntityFrameworkCore application.Type = type; - return Task.CompletedTask; + return default; } /// @@ -870,10 +817,8 @@ namespace OpenIddict.EntityFrameworkCore /// The application. /// The consent type associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetConsentTypeAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetConsentTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken) { if (application == null) @@ -883,7 +828,7 @@ namespace OpenIddict.EntityFrameworkCore application.ConsentType = type; - return Task.CompletedTask; + return default; } /// @@ -892,10 +837,8 @@ namespace OpenIddict.EntityFrameworkCore /// The application. /// The display name associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetDisplayNameAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetDisplayNameAsync([NotNull] TApplication application, [CanBeNull] string name, CancellationToken cancellationToken) { if (application == null) @@ -905,7 +848,7 @@ namespace OpenIddict.EntityFrameworkCore application.DisplayName = name; - return Task.CompletedTask; + return default; } /// @@ -914,10 +857,8 @@ namespace OpenIddict.EntityFrameworkCore /// The application. /// The permissions associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPermissionsAsync([NotNull] TApplication application, ImmutableArray permissions, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPermissionsAsync([NotNull] TApplication application, ImmutableArray permissions, CancellationToken cancellationToken) { if (application == null) { @@ -928,12 +869,12 @@ namespace OpenIddict.EntityFrameworkCore { application.Permissions = null; - return Task.CompletedTask; + return default; } application.Permissions = new JArray(permissions.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -942,10 +883,8 @@ namespace OpenIddict.EntityFrameworkCore /// The application. /// The logout callback addresses associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPostLogoutRedirectUrisAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPostLogoutRedirectUrisAsync([NotNull] TApplication application, ImmutableArray addresses, CancellationToken cancellationToken) { if (application == null) @@ -957,12 +896,12 @@ namespace OpenIddict.EntityFrameworkCore { application.PostLogoutRedirectUris = null; - return Task.CompletedTask; + return default; } application.PostLogoutRedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -971,10 +910,8 @@ namespace OpenIddict.EntityFrameworkCore /// The application. /// The additional properties associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (application == null) { @@ -985,12 +922,12 @@ namespace OpenIddict.EntityFrameworkCore { application.Properties = null; - return Task.CompletedTask; + return default; } application.Properties = properties.ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -999,10 +936,8 @@ namespace OpenIddict.EntityFrameworkCore /// The application. /// The callback addresses associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetRedirectUrisAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetRedirectUrisAsync([NotNull] TApplication application, ImmutableArray addresses, CancellationToken cancellationToken) { if (application == null) @@ -1014,12 +949,12 @@ namespace OpenIddict.EntityFrameworkCore { application.RedirectUris = null; - return Task.CompletedTask; + return default; } application.RedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -1027,10 +962,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The application to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs index c86d11ad..77c5a04b 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs @@ -124,11 +124,11 @@ namespace OpenIddict.EntityFrameworkCore /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations in the database. /// - public virtual Task CountAsync(CancellationToken cancellationToken) - => Authorizations.LongCountAsync(); + public virtual ValueTask CountAsync(CancellationToken cancellationToken) + => new ValueTask(Authorizations.AsQueryable().LongCountAsync()); /// /// Determines the number of authorizations that match the specified query. @@ -137,17 +137,17 @@ namespace OpenIddict.EntityFrameworkCore /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations that match the specified query. /// - public virtual Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + public virtual ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) { throw new ArgumentNullException(nameof(query)); } - return query(Authorizations).LongCountAsync(); + return new ValueTask(query(Authorizations).LongCountAsync()); } /// @@ -155,10 +155,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The authorization to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { @@ -167,7 +165,7 @@ namespace OpenIddict.EntityFrameworkCore Context.Add(authorization); - return Context.SaveChangesAsync(cancellationToken); + return new ValueTask(Context.SaveChangesAsync(cancellationToken)); } /// @@ -175,17 +173,15 @@ namespace OpenIddict.EntityFrameworkCore /// /// The authorization to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { throw new ArgumentNullException(nameof(authorization)); } - async Task CreateTransactionAsync() + async ValueTask CreateTransactionAsync() { // Note: transactions that specify an explicit isolation level are only supported by // relational providers and trying to use them with a different provider results in @@ -222,29 +218,28 @@ namespace OpenIddict.EntityFrameworkCore // To prevent an SQL exception from being thrown if a new associated entity is // created after the existing entries have been listed, the following logic is // executed in a serializable transaction, that will lock the affected tables. - using (var transaction = await CreateTransactionAsync()) + using var transaction = await CreateTransactionAsync(); + + // Remove all the tokens associated with the authorization. + foreach (var token in await ListTokensAsync()) { - // Remove all the tokens associated with the authorization. - foreach (var token in await ListTokensAsync()) - { - Context.Remove(token); - } + Context.Remove(token); + } - Context.Remove(authorization); + Context.Remove(authorization); - try - { - await Context.SaveChangesAsync(cancellationToken); - transaction?.Commit(); - } + try + { + await Context.SaveChangesAsync(cancellationToken); + transaction?.Commit(); + } - catch (DbUpdateConcurrencyException exception) - { - throw new OpenIddictExceptions.ConcurrencyException(new StringBuilder() - .AppendLine("The authorization was concurrently updated and cannot be persisted in its current state.") - .Append("Reload the authorization from the database and retry the operation.") - .ToString(), exception); - } + catch (DbUpdateConcurrencyException exception) + { + throw new OpenIddictExceptions.ConcurrencyException(new StringBuilder() + .AppendLine("The authorization was concurrently updated and cannot be persisted in its current state.") + .Append("Reload the authorization from the database and retry the operation.") + .ToString(), exception); } } @@ -273,11 +268,8 @@ namespace OpenIddict.EntityFrameworkCore /// The subject associated with the authorization. /// The client associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the subject/client. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the subject/client. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -290,15 +282,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The client cannot be null or empty.", nameof(client)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var authorization in FindBySubjectAndClient(Context, - ConvertIdentifierFromString(client), subject)) - { - builder.Add(authorization); - } - - return builder.ToImmutable(); + return FindBySubjectAndClient(Context, ConvertIdentifierFromString(client), subject); } /// @@ -325,11 +309,8 @@ namespace OpenIddict.EntityFrameworkCore /// The client associated with the authorization. /// The authorization status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken) { @@ -348,15 +329,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The status cannot be null or empty.", nameof(status)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var authorization in FindBySubjectClientAndStatus(Context, - ConvertIdentifierFromString(client), subject, status)) - { - builder.Add(authorization); - } - - return builder.ToImmutable(); + return FindBySubjectClientAndStatus(Context, ConvertIdentifierFromString(client), subject, status); } /// @@ -386,11 +359,8 @@ namespace OpenIddict.EntityFrameworkCore /// The authorization status. /// The authorization type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken) { @@ -414,15 +384,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The type cannot be null or empty.", nameof(type)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var authorization in FindBySubjectClientStatusAndType(Context, - ConvertIdentifierFromString(client), subject, status, type)) - { - builder.Add(authorization); - } - - return builder.ToImmutable(); + return FindBySubjectClientStatusAndType(Context, ConvertIdentifierFromString(client), subject, status, type); } /// @@ -434,40 +396,14 @@ namespace OpenIddict.EntityFrameworkCore /// The authorization type. /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken) - { - var authorizations = await FindAsync(subject, client, status, type, cancellationToken); - if (authorizations.IsEmpty) - { - return ImmutableArray.Create(); - } - - var builder = ImmutableArray.CreateBuilder(authorizations.Length); - - foreach (var authorization in authorizations) - { - async Task HasScopesAsync() - => (await GetScopesAsync(authorization, cancellationToken)) - .ToImmutableHashSet(StringComparer.Ordinal) - .IsSupersetOf(scopes); - - if (await HasScopesAsync()) - { - builder.Add(authorization); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); - } + => FindAsync(subject, client, status, type, cancellationToken) + .WhereAwait(async authorization => new HashSet( + await GetScopesAsync(authorization, cancellationToken), StringComparer.Ordinal).IsSupersetOf(scopes)); /// /// Exposes a compiled query allowing to retrieve the list of @@ -491,11 +427,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The application identifier associated with the authorizations. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified application. - /// - public virtual async Task> FindByApplicationIdAsync( + /// The authorizations corresponding to the specified application. + public virtual IAsyncEnumerable FindByApplicationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) @@ -503,15 +436,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var authorization in FindByApplicationId(Context, - ConvertIdentifierFromString(identifier))) - { - builder.Add(authorization); - } - - return builder.ToImmutable(); + return FindByApplicationId(Context, ConvertIdentifierFromString(identifier)); } /// @@ -530,18 +455,15 @@ namespace OpenIddict.EntityFrameworkCore /// /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorization corresponding to the identifier. - /// - public virtual Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + /// The authorization corresponding to the identifier. + public virtual ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - return FindById(Context, ConvertIdentifierFromString(identifier)); + return new ValueTask(FindById(Context, ConvertIdentifierFromString(identifier))); } /// @@ -557,15 +479,12 @@ namespace OpenIddict.EntityFrameworkCore select authorization); /// - /// Retrieves . + /// Retrieves the subject associated with an authorization. /// /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified subject. - /// - public virtual async Task> FindBySubjectAsync( + /// The authorizations corresponding to the specified subject. + public virtual IAsyncEnumerable FindBySubjectAsync( [NotNull] string subject, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -573,14 +492,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var authorization in FindBySubject(Context, subject)) - { - builder.Add(authorization); - } - - return builder.ToImmutable(); + return FindBySubject(Context, subject); } /// @@ -628,10 +540,10 @@ namespace OpenIddict.EntityFrameworkCore /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -640,9 +552,9 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(query)); } - return query( + return new ValueTask(query( Authorizations.Include(authorization => authorization.Application) - .AsTracking(), state).FirstOrDefaultAsync(cancellationToken); + .AsTracking(), state).FirstOrDefaultAsync(cancellationToken)); } /// @@ -825,11 +737,8 @@ namespace OpenIddict.EntityFrameworkCore /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) { var query = Authorizations.Include(authorization => authorization.Application) @@ -846,7 +755,7 @@ namespace OpenIddict.EntityFrameworkCore query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + return query.AsAsyncEnumerable(); } /// @@ -857,11 +766,8 @@ namespace OpenIddict.EntityFrameworkCore /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -870,19 +776,17 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(query)); } - return ImmutableArray.CreateRange(await query( + return query( Authorizations.Include(authorization => authorization.Application) - .AsTracking(), state).ToListAsync(cancellationToken)); + .AsTracking(), state).AsAsyncEnumerable(); } /// /// Removes the authorizations that are marked as invalid and the ad-hoc ones that have no valid/nonexpired token attached. /// /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task PruneAsync(CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask PruneAsync(CancellationToken cancellationToken) { // Note: Entity Framework Core doesn't support set-based deletes, which prevents removing // entities in a single command without having to retrieve and materialize them first. @@ -890,7 +794,7 @@ namespace OpenIddict.EntityFrameworkCore IList exceptions = null; - async Task CreateTransactionAsync() + async ValueTask CreateTransactionAsync() { // Note: transactions that specify an explicit isolation level are only supported by // relational providers and trying to use them with a different provider results in @@ -916,7 +820,7 @@ namespace OpenIddict.EntityFrameworkCore return null; } - for (var offset = 0; offset < 100_000; offset = offset + 1_000) + for (var offset = 0; offset < 100_000; offset += 1_000) { cancellationToken.ThrowIfCancellationRequested(); @@ -924,44 +828,43 @@ namespace OpenIddict.EntityFrameworkCore // after it was retrieved from the database, the following logic is executed in // a repeatable read transaction, that will put a lock on the retrieved entries // and thus prevent them from being concurrently modified outside this block. - using (var transaction = await CreateTransactionAsync()) + using var transaction = await CreateTransactionAsync(); + + var authorizations = + await (from authorization in Authorizations.Include(authorization => authorization.Tokens).AsTracking() + where authorization.Status != OpenIddictConstants.Statuses.Valid || + (authorization.Type == OpenIddictConstants.AuthorizationTypes.AdHoc && + !authorization.Tokens.Any(token => token.Status == OpenIddictConstants.Statuses.Valid && + token.ExpirationDate > DateTimeOffset.UtcNow)) + orderby authorization.Id + select authorization).Skip(offset).Take(1_000).ToListAsync(cancellationToken); + + if (authorizations.Count == 0) { - var authorizations = - await (from authorization in Authorizations.Include(authorization => authorization.Tokens).AsTracking() - where authorization.Status != OpenIddictConstants.Statuses.Valid || - (authorization.Type == OpenIddictConstants.AuthorizationTypes.AdHoc && - !authorization.Tokens.Any(token => token.Status == OpenIddictConstants.Statuses.Valid && - token.ExpirationDate > DateTimeOffset.UtcNow)) - orderby authorization.Id - select authorization).Skip(offset).Take(1_000).ToListAsync(cancellationToken); - - if (authorizations.Count == 0) - { - break; - } + break; + } - // Note: new tokens may be attached after the authorizations were retrieved - // from the database since the transaction level is deliberately limited to - // repeatable read instead of serializable for performance reasons). In this - // case, the operation will fail, which is considered an acceptable risk. - Context.RemoveRange(authorizations); - Context.RemoveRange(authorizations.SelectMany(authorization => authorization.Tokens)); + // Note: new tokens may be attached after the authorizations were retrieved + // from the database since the transaction level is deliberately limited to + // repeatable read instead of serializable for performance reasons). In this + // case, the operation will fail, which is considered an acceptable risk. + Context.RemoveRange(authorizations); + Context.RemoveRange(authorizations.SelectMany(authorization => authorization.Tokens)); - try - { - await Context.SaveChangesAsync(cancellationToken); - transaction?.Commit(); - } + try + { + await Context.SaveChangesAsync(cancellationToken); + transaction?.Commit(); + } - catch (Exception exception) + catch (Exception exception) + { + if (exceptions == null) { - if (exceptions == null) - { - exceptions = new List(capacity: 1); - } - - exceptions.Add(exception); + exceptions = new List(capacity: 1); } + + exceptions.Add(exception); } } @@ -977,10 +880,8 @@ namespace OpenIddict.EntityFrameworkCore /// The authorization. /// The unique identifier associated with the client application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task SetApplicationIdAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask SetApplicationIdAsync([NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (authorization == null) @@ -1023,10 +924,8 @@ namespace OpenIddict.EntityFrameworkCore /// The authorization. /// The additional properties associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (authorization == null) { @@ -1037,12 +936,12 @@ namespace OpenIddict.EntityFrameworkCore { authorization.Properties = null; - return Task.CompletedTask; + return default; } authorization.Properties = properties.ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -1051,10 +950,8 @@ namespace OpenIddict.EntityFrameworkCore /// The authorization. /// The scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetScopesAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetScopesAsync([NotNull] TAuthorization authorization, ImmutableArray scopes, CancellationToken cancellationToken) { if (authorization == null) @@ -1066,12 +963,12 @@ namespace OpenIddict.EntityFrameworkCore { authorization.Scopes = null; - return Task.CompletedTask; + return default; } authorization.Scopes = new JArray(scopes.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -1080,10 +977,8 @@ namespace OpenIddict.EntityFrameworkCore /// The authorization. /// The status associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetStatusAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetStatusAsync([NotNull] TAuthorization authorization, [CanBeNull] string status, CancellationToken cancellationToken) { if (authorization == null) @@ -1093,7 +988,7 @@ namespace OpenIddict.EntityFrameworkCore authorization.Status = status; - return Task.CompletedTask; + return default; } /// @@ -1102,10 +997,8 @@ namespace OpenIddict.EntityFrameworkCore /// The authorization. /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetSubjectAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetSubjectAsync([NotNull] TAuthorization authorization, [CanBeNull] string subject, CancellationToken cancellationToken) { if (authorization == null) @@ -1115,7 +1008,7 @@ namespace OpenIddict.EntityFrameworkCore authorization.Subject = subject; - return Task.CompletedTask; + return default; } /// @@ -1124,10 +1017,8 @@ namespace OpenIddict.EntityFrameworkCore /// The authorization. /// The type associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetTypeAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetTypeAsync([NotNull] TAuthorization authorization, [CanBeNull] string type, CancellationToken cancellationToken) { if (authorization == null) @@ -1137,7 +1028,7 @@ namespace OpenIddict.EntityFrameworkCore authorization.Type = type; - return Task.CompletedTask; + return default; } /// @@ -1145,10 +1036,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The authorization to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs index b1526881..c9e04c11 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs @@ -103,11 +103,11 @@ namespace OpenIddict.EntityFrameworkCore /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes in the database. /// - public virtual Task CountAsync(CancellationToken cancellationToken) - => Scopes.LongCountAsync(); + public virtual ValueTask CountAsync(CancellationToken cancellationToken) + => new ValueTask(Scopes.AsQueryable().LongCountAsync()); /// /// Determines the number of scopes that match the specified query. @@ -116,17 +116,17 @@ namespace OpenIddict.EntityFrameworkCore /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes that match the specified query. /// - public virtual Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + public virtual ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) { throw new ArgumentNullException(nameof(query)); } - return query(Scopes).LongCountAsync(); + return new ValueTask(query(Scopes).LongCountAsync()); } /// @@ -134,10 +134,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The scope to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { @@ -146,7 +144,7 @@ namespace OpenIddict.EntityFrameworkCore Scopes.Add(scope); - return Context.SaveChangesAsync(cancellationToken); + return new ValueTask(Context.SaveChangesAsync(cancellationToken)); } /// @@ -154,10 +152,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The scope to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { @@ -195,17 +191,17 @@ namespace OpenIddict.EntityFrameworkCore /// The unique identifier associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the identifier. /// - public virtual Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - return FindById(Context, ConvertIdentifierFromString(identifier)); + return new ValueTask(FindById(Context, ConvertIdentifierFromString(identifier))); } /// @@ -223,17 +219,17 @@ namespace OpenIddict.EntityFrameworkCore /// The name associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the specified name. /// - public virtual Task FindByNameAsync([NotNull] string name, CancellationToken cancellationToken) + public virtual ValueTask FindByNameAsync([NotNull] string name, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(name)) { throw new ArgumentException("The scope name cannot be null or empty.", nameof(name)); } - return FindByName(Context, name); + return new ValueTask(FindByName(Context, name)); } /// @@ -250,11 +246,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The names associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes corresponding to the specified names. - /// - public virtual async Task> FindByNamesAsync( + /// The scopes corresponding to the specified names. + public virtual IAsyncEnumerable FindByNamesAsync( ImmutableArray names, CancellationToken cancellationToken) { if (names.Any(name => string.IsNullOrEmpty(name))) @@ -262,14 +255,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("Scope names cannot be null or empty.", nameof(names)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var scope in FindByNames(Context, names)) - { - builder.Add(scope); - } - - return builder.ToImmutable(); + return FindByNames(Context, names); } /// @@ -291,11 +277,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The resource associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes associated with the specified resource. - /// - public virtual async Task> FindByResourceAsync( + /// The scopes associated with the specified resource. + public virtual IAsyncEnumerable FindByResourceAsync( [NotNull] string resource, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(resource)) @@ -303,18 +286,8 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The resource cannot be null or empty.", nameof(resource)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var scope in FindByResource(Context, resource)) - { - var resources = await GetResourcesAsync(scope, cancellationToken); - if (resources.Contains(resource, StringComparer.Ordinal)) - { - builder.Add(scope); - } - } - - return builder.ToImmutable(); + return FindByResource(Context, resource) + .WhereAwait(async scope => (await GetResourcesAsync(scope, cancellationToken)).Contains(resource, StringComparer.Ordinal)); } /// @@ -326,10 +299,10 @@ namespace OpenIddict.EntityFrameworkCore /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -338,7 +311,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(query)); } - return query(Scopes.AsTracking(), state).FirstOrDefaultAsync(cancellationToken); + return new ValueTask(query(Scopes.AsTracking(), state).FirstOrDefaultAsync(cancellationToken)); } /// @@ -521,14 +494,11 @@ namespace OpenIddict.EntityFrameworkCore /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) { - var query = Scopes.OrderBy(scope => scope.Id).AsTracking(); + var query = Scopes.AsQueryable().OrderBy(scope => scope.Id).AsTracking(); if (offset.HasValue) { @@ -540,7 +510,7 @@ namespace OpenIddict.EntityFrameworkCore query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + return query.AsAsyncEnumerable(); } /// @@ -551,11 +521,8 @@ namespace OpenIddict.EntityFrameworkCore /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -564,7 +531,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(query)); } - return ImmutableArray.CreateRange(await query(Scopes.AsTracking(), state).ToListAsync(cancellationToken)); + return query(Scopes.AsTracking(), state).AsAsyncEnumerable(); } /// @@ -573,10 +540,8 @@ namespace OpenIddict.EntityFrameworkCore /// The scope. /// The description associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken) { if (scope == null) { @@ -585,7 +550,7 @@ namespace OpenIddict.EntityFrameworkCore scope.Description = description; - return Task.CompletedTask; + return default; } /// @@ -594,10 +559,8 @@ namespace OpenIddict.EntityFrameworkCore /// The scope. /// The display name associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetDisplayNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetDisplayNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) { if (scope == null) { @@ -606,7 +569,7 @@ namespace OpenIddict.EntityFrameworkCore scope.DisplayName = name; - return Task.CompletedTask; + return default; } /// @@ -615,10 +578,8 @@ namespace OpenIddict.EntityFrameworkCore /// The scope. /// The name associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) { if (scope == null) { @@ -627,7 +588,7 @@ namespace OpenIddict.EntityFrameworkCore scope.Name = name; - return Task.CompletedTask; + return default; } /// @@ -636,10 +597,8 @@ namespace OpenIddict.EntityFrameworkCore /// The scope. /// The additional properties associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (scope == null) { @@ -650,12 +609,12 @@ namespace OpenIddict.EntityFrameworkCore { scope.Properties = null; - return Task.CompletedTask; + return default; } scope.Properties = properties.ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -664,10 +623,8 @@ namespace OpenIddict.EntityFrameworkCore /// The scope. /// The resources associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetResourcesAsync([NotNull] TScope scope, ImmutableArray resources, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetResourcesAsync([NotNull] TScope scope, ImmutableArray resources, CancellationToken cancellationToken) { if (scope == null) { @@ -678,12 +635,12 @@ namespace OpenIddict.EntityFrameworkCore { scope.Resources = null; - return Task.CompletedTask; + return default; } scope.Resources = new JArray(resources.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -691,10 +648,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The scope to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs index 1ff551b1..a7a35855 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs @@ -124,11 +124,11 @@ namespace OpenIddict.EntityFrameworkCore /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications in the database. /// - public virtual Task CountAsync(CancellationToken cancellationToken) - => Tokens.LongCountAsync(); + public virtual ValueTask CountAsync(CancellationToken cancellationToken) + => new ValueTask(Tokens.AsQueryable().LongCountAsync()); /// /// Determines the number of tokens that match the specified query. @@ -137,17 +137,17 @@ namespace OpenIddict.EntityFrameworkCore /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of tokens that match the specified query. /// - public virtual Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + public virtual ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) { throw new ArgumentNullException(nameof(query)); } - return query(Tokens).LongCountAsync(); + return new ValueTask(query(Tokens).LongCountAsync()); } /// @@ -155,10 +155,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The token to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { @@ -167,7 +165,7 @@ namespace OpenIddict.EntityFrameworkCore Context.Add(token); - return Context.SaveChangesAsync(cancellationToken); + return new ValueTask(Context.SaveChangesAsync(cancellationToken)); } /// @@ -175,10 +173,8 @@ namespace OpenIddict.EntityFrameworkCore /// /// The token to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { @@ -227,11 +223,8 @@ namespace OpenIddict.EntityFrameworkCore /// The subject associated with the token. /// The client associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the subject/client. - /// - public virtual async Task> FindAsync([NotNull] string subject, + /// The tokens corresponding to the subject/client. + public virtual IAsyncEnumerable FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -244,15 +237,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The client cannot be null or empty.", nameof(client)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var token in FindBySubjectAndClient(Context, - ConvertIdentifierFromString(client), subject)) - { - builder.Add(token); - } - - return builder.ToImmutable(); + return FindBySubjectAndClient(Context, ConvertIdentifierFromString(client), subject); } /// @@ -280,11 +265,8 @@ namespace OpenIddict.EntityFrameworkCore /// The client associated with the token. /// The token status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The tokens corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken) { @@ -303,15 +285,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The status cannot be null or empty.", nameof(status)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var token in FindBySubjectClientAndStatus(Context, - ConvertIdentifierFromString(client), subject, status)) - { - builder.Add(token); - } - - return builder.ToImmutable(); + return FindBySubjectClientAndStatus(Context, ConvertIdentifierFromString(client), subject, status); } /// @@ -342,11 +316,8 @@ namespace OpenIddict.EntityFrameworkCore /// The token status. /// The token type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The tokens corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken) { @@ -370,15 +341,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The type cannot be null or empty.", nameof(type)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var token in FindBySubjectClientStatusAndType(Context, - ConvertIdentifierFromString(client), subject, status, type)) - { - builder.Add(token); - } - - return builder.ToImmutable(); + return FindBySubjectClientStatusAndType(Context, ConvertIdentifierFromString(client), subject, status, type); } /// @@ -404,25 +367,15 @@ namespace OpenIddict.EntityFrameworkCore /// /// The application identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified application. - /// - public virtual async Task> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + /// The tokens corresponding to the specified application. + public virtual IAsyncEnumerable FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var token in FindByApplicationId(Context, ConvertIdentifierFromString(identifier))) - { - builder.Add(token); - } - - return builder.ToImmutable(); + return FindByApplicationId(Context, ConvertIdentifierFromString(identifier)); } /// @@ -448,25 +401,15 @@ namespace OpenIddict.EntityFrameworkCore /// /// The authorization identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified authorization. - /// - public virtual async Task> FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + /// The tokens corresponding to the specified authorization. + public virtual IAsyncEnumerable FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var token in FindByAuthorizationId(Context, ConvertIdentifierFromString(identifier))) - { - builder.Add(token); - } - - return builder.ToImmutable(); + return FindByAuthorizationId(Context, ConvertIdentifierFromString(identifier)); } /// @@ -487,17 +430,17 @@ namespace OpenIddict.EntityFrameworkCore /// The unique identifier associated with the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the token corresponding to the unique identifier. /// - public virtual Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - return FindById(Context, ConvertIdentifierFromString(identifier)); + return new ValueTask(FindById(Context, ConvertIdentifierFromString(identifier))); } /// @@ -520,17 +463,17 @@ namespace OpenIddict.EntityFrameworkCore /// The reference identifier associated with the tokens. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the tokens corresponding to the specified reference identifier. /// - public virtual Task FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual ValueTask FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - return FindByReferenceId(Context, identifier); + return new ValueTask(FindByReferenceId(Context, identifier)); } /// @@ -551,25 +494,15 @@ namespace OpenIddict.EntityFrameworkCore /// /// The subject associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified subject. - /// - public virtual async Task> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken) + /// The tokens corresponding to the specified subject. + public virtual IAsyncEnumerable FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) { throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); } - var builder = ImmutableArray.CreateBuilder(); - - await foreach (var token in FindBySubject(Context, subject)) - { - builder.Add(token); - } - - return builder.ToImmutable(); + return FindBySubject(Context, subject); } /// @@ -617,10 +550,10 @@ namespace OpenIddict.EntityFrameworkCore /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual Task GetAsync( + public virtual ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -629,10 +562,10 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(query)); } - return query( + return new ValueTask(query( Tokens.Include(token => token.Application) .Include(token => token.Authorization) - .AsTracking(), state).FirstOrDefaultAsync(cancellationToken); + .AsTracking(), state).FirstOrDefaultAsync(cancellationToken)); } /// @@ -892,11 +825,8 @@ namespace OpenIddict.EntityFrameworkCore /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) { var query = Tokens.Include(token => token.Application) @@ -914,7 +844,7 @@ namespace OpenIddict.EntityFrameworkCore query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + return query.AsAsyncEnumerable(); } /// @@ -925,11 +855,8 @@ namespace OpenIddict.EntityFrameworkCore /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -938,20 +865,18 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(query)); } - return ImmutableArray.CreateRange(await query( + return query( Tokens.Include(token => token.Application) .Include(token => token.Authorization) - .AsTracking(), state).ToListAsync(cancellationToken)); + .AsTracking(), state).AsAsyncEnumerable(); } /// /// Removes the tokens that are marked as expired or invalid. /// /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task PruneAsync(CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask PruneAsync(CancellationToken cancellationToken) { // Note: Entity Framework Core doesn't support set-based deletes, which prevents removing // entities in a single command without having to retrieve and materialize them first. @@ -959,7 +884,7 @@ namespace OpenIddict.EntityFrameworkCore IList exceptions = null; - async Task CreateTransactionAsync() + async ValueTask CreateTransactionAsync() { // Note: transactions that specify an explicit isolation level are only supported by // relational providers and trying to use them with a different provider results in @@ -987,7 +912,7 @@ namespace OpenIddict.EntityFrameworkCore // Note: to avoid sending too many queries, the maximum number of elements // that can be removed by a single call to PruneAsync() is limited to 50000. - for (var offset = 0; offset < 50_000; offset = offset + 1_000) + for (var offset = 0; offset < 50_000; offset += 1_000) { cancellationToken.ThrowIfCancellationRequested(); @@ -995,37 +920,35 @@ namespace OpenIddict.EntityFrameworkCore // after it was retrieved from the database, the following logic is executed in // a repeatable read transaction, that will put a lock on the retrieved entries // and thus prevent them from being concurrently modified outside this block. - using (var transaction = await CreateTransactionAsync()) + using var transaction = await CreateTransactionAsync(); + + var tokens = await (from token in Tokens.AsTracking() + where token.Status != OpenIddictConstants.Statuses.Valid || + token.ExpirationDate < DateTimeOffset.UtcNow + orderby token.Id + select token).Skip(offset).Take(1_000).ToListAsync(cancellationToken); + + if (tokens.Count == 0) { - var tokens = - await (from token in Tokens.AsTracking() - where token.Status != OpenIddictConstants.Statuses.Valid || - token.ExpirationDate < DateTimeOffset.UtcNow - orderby token.Id - select token).Skip(offset).Take(1_000).ToListAsync(cancellationToken); - - if (tokens.Count == 0) - { - break; - } + break; + } - Context.RemoveRange(tokens); + Context.RemoveRange(tokens); - try - { - await Context.SaveChangesAsync(cancellationToken); - transaction?.Commit(); - } + try + { + await Context.SaveChangesAsync(cancellationToken); + transaction?.Commit(); + } - catch (Exception exception) + catch (Exception exception) + { + if (exceptions == null) { - if (exceptions == null) - { - exceptions = new List(capacity: 1); - } - - exceptions.Add(exception); + exceptions = new List(capacity: 1); } + + exceptions.Add(exception); } } @@ -1042,9 +965,9 @@ namespace OpenIddict.EntityFrameworkCore /// The unique identifier associated with the client application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task SetApplicationIdAsync([NotNull] TToken token, + public virtual async ValueTask SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (token == null) @@ -1088,9 +1011,9 @@ namespace OpenIddict.EntityFrameworkCore /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task SetAuthorizationIdAsync([NotNull] TToken token, + public virtual async ValueTask SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (token == null) @@ -1134,9 +1057,9 @@ namespace OpenIddict.EntityFrameworkCore /// The creation date. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetCreationDateAsync([NotNull] TToken token, + public virtual ValueTask SetCreationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken) { if (token == null) @@ -1146,7 +1069,7 @@ namespace OpenIddict.EntityFrameworkCore token.CreationDate = date; - return Task.CompletedTask; + return default; } /// @@ -1156,9 +1079,9 @@ namespace OpenIddict.EntityFrameworkCore /// The expiration date. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetExpirationDateAsync([NotNull] TToken token, + public virtual ValueTask SetExpirationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken) { if (token == null) @@ -1168,7 +1091,7 @@ namespace OpenIddict.EntityFrameworkCore token.ExpirationDate = date; - return Task.CompletedTask; + return default; } /// @@ -1178,9 +1101,9 @@ namespace OpenIddict.EntityFrameworkCore /// The payload associated with the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken) + public virtual ValueTask SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken) { if (token == null) { @@ -1189,7 +1112,7 @@ namespace OpenIddict.EntityFrameworkCore token.Payload = payload; - return Task.CompletedTask; + return default; } /// @@ -1199,9 +1122,9 @@ namespace OpenIddict.EntityFrameworkCore /// The additional properties associated with the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken) + public virtual ValueTask SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (token == null) { @@ -1212,12 +1135,12 @@ namespace OpenIddict.EntityFrameworkCore { token.Properties = null; - return Task.CompletedTask; + return default; } token.Properties = properties.ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -1229,9 +1152,9 @@ namespace OpenIddict.EntityFrameworkCore /// The reference identifier associated with the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) + public virtual ValueTask SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (token == null) { @@ -1240,7 +1163,7 @@ namespace OpenIddict.EntityFrameworkCore token.ReferenceId = identifier; - return Task.CompletedTask; + return default; } /// @@ -1250,9 +1173,9 @@ namespace OpenIddict.EntityFrameworkCore /// The status associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken) + public virtual ValueTask SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken) { if (token == null) { @@ -1261,7 +1184,7 @@ namespace OpenIddict.EntityFrameworkCore token.Status = status; - return Task.CompletedTask; + return default; } /// @@ -1271,9 +1194,9 @@ namespace OpenIddict.EntityFrameworkCore /// The subject associated with the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken) + public virtual ValueTask SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken) { if (token == null) { @@ -1282,7 +1205,7 @@ namespace OpenIddict.EntityFrameworkCore token.Subject = subject; - return Task.CompletedTask; + return default; } /// @@ -1292,9 +1215,9 @@ namespace OpenIddict.EntityFrameworkCore /// The token type associated with the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual Task SetTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken) + public virtual ValueTask SetTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken) { if (token == null) { @@ -1303,7 +1226,7 @@ namespace OpenIddict.EntityFrameworkCore token.Type = type; - return Task.CompletedTask; + return default; } /// @@ -1312,9 +1235,9 @@ namespace OpenIddict.EntityFrameworkCore /// The token to update. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation. + /// A that can be used to monitor the asynchronous operation. /// - public virtual async Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) + public virtual async ValueTask UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { diff --git a/src/OpenIddict.MongoDb/OpenIddictMongoDbHelpers.cs b/src/OpenIddict.MongoDb/OpenIddictMongoDbHelpers.cs new file mode 100644 index 00000000..41829988 --- /dev/null +++ b/src/OpenIddict.MongoDb/OpenIddictMongoDbHelpers.cs @@ -0,0 +1,75 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using JetBrains.Annotations; + +namespace MongoDB.Driver +{ + /// + /// Exposes extensions simplifying the integration between OpenIddict and MongoDB. + /// + internal static class OpenIddictMongoDbHelpers + { + /// + /// Executes the query and returns the results as a streamed async enumeration. + /// + /// The type of the returned entities. + /// The query source. + /// The that can be used to abort the operation. + /// The streamed async enumeration containing the results. + internal static IAsyncEnumerable ToAsyncEnumerable([NotNull] this IAsyncCursorSource source, CancellationToken cancellationToken) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return ExecuteAsync(); + + async IAsyncEnumerable ExecuteAsync() + { + using var cursor = await source.ToCursorAsync(); + + while (await cursor.MoveNextAsync(cancellationToken)) + { + foreach (var element in cursor.Current) + { + yield return element; + } + } + } + } + + /// + /// Executes the query and returns the results as a streamed async enumeration. + /// + /// The type of the returned entities. + /// The query source. + /// The that can be used to abort the operation. + /// The streamed async enumeration containing the results. + internal static IAsyncEnumerable ToAsyncEnumerable([NotNull] this IQueryable source, CancellationToken cancellationToken) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return ExecuteAsync(); + + async IAsyncEnumerable ExecuteAsync() + { + await foreach (var element in ((IAsyncCursorSource) source).ToAsyncEnumerable(cancellationToken)) + { + yield return element; + } + } + } + } +} diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictApplicationStore.cs index 81dbead4..03c604b7 100644 --- a/src/OpenIddict.MongoDb/Stores/OpenIddictApplicationStore.cs +++ b/src/OpenIddict.MongoDb/Stores/OpenIddictApplicationStore.cs @@ -5,8 +5,10 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -52,10 +54,10 @@ namespace OpenIddict.MongoDb /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications in the database. /// - public virtual async Task CountAsync(CancellationToken cancellationToken) + public virtual async ValueTask CountAsync(CancellationToken cancellationToken) { var database = await Context.GetDatabaseAsync(cancellationToken); var collection = database.GetCollection(Options.CurrentValue.ApplicationsCollectionName); @@ -70,10 +72,10 @@ namespace OpenIddict.MongoDb /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications that match the specified query. /// - public virtual async Task CountAsync( + public virtual async ValueTask CountAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) @@ -92,10 +94,8 @@ namespace OpenIddict.MongoDb /// /// The application to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { @@ -113,10 +113,8 @@ namespace OpenIddict.MongoDb /// /// The application to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { @@ -151,10 +149,10 @@ namespace OpenIddict.MongoDb /// The client identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - public virtual async Task FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual async ValueTask FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -173,10 +171,10 @@ namespace OpenIddict.MongoDb /// The unique identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - public virtual async Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual async ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -195,11 +193,8 @@ namespace OpenIddict.MongoDb /// /// The post_logout_redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified post_logout_redirect_uri. - /// - public virtual async Task> FindByPostLogoutRedirectUriAsync( + /// The client applications corresponding to the specified post_logout_redirect_uri. + public virtual IAsyncEnumerable FindByPostLogoutRedirectUriAsync( [NotNull] string address, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(address)) @@ -207,11 +202,19 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The address cannot be null or empty.", nameof(address)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.ApplicationsCollectionName); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.ApplicationsCollectionName); - return ImmutableArray.CreateRange(await collection.Find(application => - application.PostLogoutRedirectUris.Contains(address)).ToListAsync(cancellationToken)); + await foreach (var application in collection.Find(application => + application.PostLogoutRedirectUris.Contains(address)).ToAsyncEnumerable(cancellationToken)) + { + yield return application; + } + } } /// @@ -219,11 +222,8 @@ namespace OpenIddict.MongoDb /// /// The redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified redirect_uri. - /// - public virtual async Task> FindByRedirectUriAsync( + /// The client applications corresponding to the specified redirect_uri. + public virtual IAsyncEnumerable FindByRedirectUriAsync( [NotNull] string address, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(address)) @@ -231,11 +231,19 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The address cannot be null or empty.", nameof(address)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.ApplicationsCollectionName); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.ApplicationsCollectionName); - return ImmutableArray.CreateRange(await collection.Find(application => - application.RedirectUris.Contains(address)).ToListAsync(cancellationToken)); + await foreach (var application in collection.Find(application => + application.RedirectUris.Contains(address)).ToAsyncEnumerable(cancellationToken)) + { + yield return application; + } + } } /// @@ -247,10 +255,10 @@ namespace OpenIddict.MongoDb /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual async Task GetAsync( + public virtual async ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -512,12 +520,9 @@ namespace OpenIddict.MongoDb /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( - [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) + /// All the elements returned when executing the specified query. + public virtual async IAsyncEnumerable ListAsync( + [CanBeNull] int? count, [CanBeNull] int? offset, [EnumeratorCancellation] CancellationToken cancellationToken) { var database = await Context.GetDatabaseAsync(cancellationToken); var collection = database.GetCollection(Options.CurrentValue.ApplicationsCollectionName); @@ -534,7 +539,10 @@ namespace OpenIddict.MongoDb query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + await foreach (var application in ((IAsyncCursorSource) query).ToAsyncEnumerable(cancellationToken)) + { + yield return application; + } } /// @@ -545,11 +553,8 @@ namespace OpenIddict.MongoDb /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -558,11 +563,18 @@ namespace OpenIddict.MongoDb throw new ArgumentNullException(nameof(query)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.ApplicationsCollectionName); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.ApplicationsCollectionName); - return ImmutableArray.CreateRange( - await ((IMongoQueryable) query(collection.AsQueryable(), state)).ToListAsync(cancellationToken)); + await foreach (var element in query(collection.AsQueryable(), state).ToAsyncEnumerable(cancellationToken)) + { + yield return element; + } + } } /// @@ -571,10 +583,8 @@ namespace OpenIddict.MongoDb /// The application. /// The client identifier associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetClientIdAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetClientIdAsync([NotNull] TApplication application, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (application == null) @@ -584,7 +594,7 @@ namespace OpenIddict.MongoDb application.ClientId = identifier; - return Task.CompletedTask; + return default; } /// @@ -595,10 +605,8 @@ namespace OpenIddict.MongoDb /// The application. /// The client secret associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetClientSecretAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetClientSecretAsync([NotNull] TApplication application, [CanBeNull] string secret, CancellationToken cancellationToken) { if (application == null) @@ -608,7 +616,7 @@ namespace OpenIddict.MongoDb application.ClientSecret = secret; - return Task.CompletedTask; + return default; } /// @@ -617,10 +625,8 @@ namespace OpenIddict.MongoDb /// The application. /// The client type associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetClientTypeAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetClientTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken) { if (application == null) @@ -630,7 +636,7 @@ namespace OpenIddict.MongoDb application.Type = type; - return Task.CompletedTask; + return default; } /// @@ -639,10 +645,8 @@ namespace OpenIddict.MongoDb /// The application. /// The consent type associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetConsentTypeAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetConsentTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken) { if (application == null) @@ -652,7 +656,7 @@ namespace OpenIddict.MongoDb application.ConsentType = type; - return Task.CompletedTask; + return default; } /// @@ -661,10 +665,8 @@ namespace OpenIddict.MongoDb /// The application. /// The display name associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetDisplayNameAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetDisplayNameAsync([NotNull] TApplication application, [CanBeNull] string name, CancellationToken cancellationToken) { if (application == null) @@ -674,7 +676,7 @@ namespace OpenIddict.MongoDb application.DisplayName = name; - return Task.CompletedTask; + return default; } /// @@ -683,10 +685,8 @@ namespace OpenIddict.MongoDb /// The application. /// The permissions associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPermissionsAsync([NotNull] TApplication application, ImmutableArray permissions, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPermissionsAsync([NotNull] TApplication application, ImmutableArray permissions, CancellationToken cancellationToken) { if (application == null) { @@ -697,12 +697,12 @@ namespace OpenIddict.MongoDb { application.Permissions = null; - return Task.CompletedTask; + return default; } application.Permissions = permissions.ToArray(); - return Task.CompletedTask; + return default; } /// @@ -711,10 +711,8 @@ namespace OpenIddict.MongoDb /// The application. /// The logout callback addresses associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPostLogoutRedirectUrisAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPostLogoutRedirectUrisAsync([NotNull] TApplication application, ImmutableArray addresses, CancellationToken cancellationToken) { if (application == null) @@ -726,12 +724,12 @@ namespace OpenIddict.MongoDb { application.PostLogoutRedirectUris = null; - return Task.CompletedTask; + return default; } application.PostLogoutRedirectUris = addresses.ToArray(); - return Task.CompletedTask; + return default; } /// @@ -740,10 +738,8 @@ namespace OpenIddict.MongoDb /// The application. /// The additional properties associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (application == null) { @@ -754,12 +750,12 @@ namespace OpenIddict.MongoDb { application.Properties = null; - return Task.CompletedTask; + return default; } application.Properties = BsonDocument.Parse(properties.ToString(Formatting.None)); - return Task.CompletedTask; + return default; } /// @@ -768,10 +764,8 @@ namespace OpenIddict.MongoDb /// The application. /// The callback addresses associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetRedirectUrisAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetRedirectUrisAsync([NotNull] TApplication application, ImmutableArray addresses, CancellationToken cancellationToken) { if (application == null) @@ -783,12 +777,12 @@ namespace OpenIddict.MongoDb { application.RedirectUris = null; - return Task.CompletedTask; + return default; } application.RedirectUris = addresses.ToArray(); - return Task.CompletedTask; + return default; } /// @@ -796,10 +790,8 @@ namespace OpenIddict.MongoDb /// /// The application to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs index c76438d4..b5bc7eb1 100644 --- a/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -53,10 +54,10 @@ namespace OpenIddict.MongoDb /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations in the database. /// - public virtual async Task CountAsync(CancellationToken cancellationToken) + public virtual async ValueTask CountAsync(CancellationToken cancellationToken) { var database = await Context.GetDatabaseAsync(cancellationToken); var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); @@ -71,10 +72,10 @@ namespace OpenIddict.MongoDb /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations that match the specified query. /// - public virtual async Task CountAsync( + public virtual async ValueTask CountAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) @@ -93,10 +94,8 @@ namespace OpenIddict.MongoDb /// /// The authorization to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { @@ -114,10 +113,8 @@ namespace OpenIddict.MongoDb /// /// The authorization to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { @@ -149,11 +146,8 @@ namespace OpenIddict.MongoDb /// The subject associated with the authorization. /// The client associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the subject/client. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the subject/client. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -166,12 +160,20 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The client cannot be null or empty.", nameof(client)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange(await collection.Find(authorization => - authorization.Subject == subject && - authorization.ApplicationId == ObjectId.Parse(client)).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + + await foreach (var authorization in collection.Find(authorization => + authorization.Subject == subject && + authorization.ApplicationId == ObjectId.Parse(client)).ToAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } + } } /// @@ -181,11 +183,8 @@ namespace OpenIddict.MongoDb /// The client associated with the authorization. /// The authorization status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken) { @@ -204,13 +203,21 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The status cannot be null or empty.", nameof(status)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange(await collection.Find(authorization => - authorization.Subject == subject && - authorization.ApplicationId == ObjectId.Parse(client) && - authorization.Status == status).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + + await foreach (var authorization in collection.Find(authorization => + authorization.Subject == subject && + authorization.ApplicationId == ObjectId.Parse(client) && + authorization.Status == status).ToAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } + } } /// @@ -221,11 +228,8 @@ namespace OpenIddict.MongoDb /// The authorization status. /// The authorization type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken) { @@ -249,14 +253,22 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The type cannot be null or empty.", nameof(type)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); - return ImmutableArray.CreateRange(await collection.Find(authorization => - authorization.Subject == subject && - authorization.ApplicationId == ObjectId.Parse(client) && - authorization.Status == status && - authorization.Type == type).ToListAsync(cancellationToken)); + await foreach (var authorization in collection.Find(authorization => + authorization.Subject == subject && + authorization.ApplicationId == ObjectId.Parse(client) && + authorization.Status == status && + authorization.Type == type).ToAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } + } } /// @@ -268,11 +280,8 @@ namespace OpenIddict.MongoDb /// The authorization type. /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken) @@ -297,17 +306,25 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The type cannot be null or empty.", nameof(type)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); - // Note: Enumerable.All() is deliberately used without the extension method syntax to ensure - // ImmutableArrayExtensions.All() (which is not supported by MongoDB) is not used instead. - return ImmutableArray.CreateRange(await collection.Find(authorization => - authorization.Subject == subject && - authorization.ApplicationId == ObjectId.Parse(client) && - authorization.Status == status && - authorization.Type == type && - Enumerable.All(scopes, scope => authorization.Scopes.Contains(scope))).ToListAsync(cancellationToken)); + // Note: Enumerable.All() is deliberately used without the extension method syntax to ensure + // ImmutableArrayExtensions.All() (which is not supported by MongoDB) is not used instead. + await foreach (var authorization in collection.Find(authorization => + authorization.Subject == subject && + authorization.ApplicationId == ObjectId.Parse(client) && + authorization.Status == status && + authorization.Type == type && + Enumerable.All(scopes, scope => authorization.Scopes.Contains(scope))).ToAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } + } } /// @@ -315,11 +332,8 @@ namespace OpenIddict.MongoDb /// /// The application identifier associated with the authorizations. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified application. - /// - public virtual async Task> FindByApplicationIdAsync( + /// The authorizations corresponding to the specified application. + public virtual IAsyncEnumerable FindByApplicationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) @@ -327,11 +341,19 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange(await collection.Find(authorization => - authorization.ApplicationId == ObjectId.Parse(identifier)).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + + await foreach (var authorization in collection.Find(authorization => + authorization.ApplicationId == ObjectId.Parse(identifier)).ToAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } + } } /// @@ -340,10 +362,10 @@ namespace OpenIddict.MongoDb /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the authorization corresponding to the identifier. /// - public virtual async Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual async ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -362,11 +384,8 @@ namespace OpenIddict.MongoDb /// /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified subject. - /// - public virtual async Task> FindBySubjectAsync( + /// The authorizations corresponding to the specified subject. + public virtual IAsyncEnumerable FindBySubjectAsync( [NotNull] string subject, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -374,11 +393,19 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); - return ImmutableArray.CreateRange(await collection.Find(authorization => - authorization.Subject == subject).ToListAsync(cancellationToken)); + await foreach (var authorization in collection.Find(authorization => + authorization.Subject == subject).ToAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } + } } /// @@ -409,10 +436,10 @@ namespace OpenIddict.MongoDb /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual async Task GetAsync( + public virtual async ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -584,12 +611,9 @@ namespace OpenIddict.MongoDb /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( - [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) + /// All the elements returned when executing the specified query. + public virtual async IAsyncEnumerable ListAsync( + [CanBeNull] int? count, [CanBeNull] int? offset, [EnumeratorCancellation] CancellationToken cancellationToken) { var database = await Context.GetDatabaseAsync(cancellationToken); var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); @@ -606,7 +630,10 @@ namespace OpenIddict.MongoDb query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + await foreach (var authorization in ((IAsyncCursorSource) query).ToAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } } /// @@ -617,11 +644,8 @@ namespace OpenIddict.MongoDb /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -630,21 +654,26 @@ namespace OpenIddict.MongoDb throw new ArgumentNullException(nameof(query)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange( - await ((IMongoQueryable) query(collection.AsQueryable(), state)).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + + await foreach (var element in query(collection.AsQueryable(), state).ToAsyncEnumerable(cancellationToken)) + { + yield return element; + } + } } /// /// Removes the authorizations that are marked as invalid and the ad-hoc ones that have no valid/nonexpired token attached. /// /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task PruneAsync(CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask PruneAsync(CancellationToken cancellationToken) { var database = await Context.GetDatabaseAsync(cancellationToken); var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); @@ -708,10 +737,8 @@ namespace OpenIddict.MongoDb /// The authorization. /// The unique identifier associated with the client application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetApplicationIdAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetApplicationIdAsync([NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (authorization == null) @@ -729,7 +756,7 @@ namespace OpenIddict.MongoDb authorization.ApplicationId = ObjectId.Empty; } - return Task.CompletedTask; + return default; } /// @@ -738,10 +765,8 @@ namespace OpenIddict.MongoDb /// The authorization. /// The additional properties associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (authorization == null) { @@ -752,12 +777,12 @@ namespace OpenIddict.MongoDb { authorization.Properties = null; - return Task.CompletedTask; + return default; } authorization.Properties = BsonDocument.Parse(properties.ToString(Formatting.None)); - return Task.CompletedTask; + return default; } /// @@ -766,10 +791,8 @@ namespace OpenIddict.MongoDb /// The authorization. /// The scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetScopesAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetScopesAsync([NotNull] TAuthorization authorization, ImmutableArray scopes, CancellationToken cancellationToken) { if (authorization == null) @@ -781,12 +804,12 @@ namespace OpenIddict.MongoDb { authorization.Scopes = null; - return Task.CompletedTask; + return default; } authorization.Scopes = scopes.ToArray(); - return Task.CompletedTask; + return default; } /// @@ -795,10 +818,8 @@ namespace OpenIddict.MongoDb /// The authorization. /// The status associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetStatusAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetStatusAsync([NotNull] TAuthorization authorization, [CanBeNull] string status, CancellationToken cancellationToken) { if (authorization == null) @@ -808,7 +829,7 @@ namespace OpenIddict.MongoDb authorization.Status = status; - return Task.CompletedTask; + return default; } /// @@ -817,10 +838,8 @@ namespace OpenIddict.MongoDb /// The authorization. /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetSubjectAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetSubjectAsync([NotNull] TAuthorization authorization, [CanBeNull] string subject, CancellationToken cancellationToken) { if (authorization == null) @@ -830,7 +849,7 @@ namespace OpenIddict.MongoDb authorization.Subject = subject; - return Task.CompletedTask; + return default; } /// @@ -839,10 +858,8 @@ namespace OpenIddict.MongoDb /// The authorization. /// The type associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetTypeAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetTypeAsync([NotNull] TAuthorization authorization, [CanBeNull] string type, CancellationToken cancellationToken) { if (authorization == null) @@ -852,7 +869,7 @@ namespace OpenIddict.MongoDb authorization.Type = type; - return Task.CompletedTask; + return default; } /// @@ -860,10 +877,8 @@ namespace OpenIddict.MongoDb /// /// The authorization to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictScopeStore.cs index 5fa49aca..62d2dfbe 100644 --- a/src/OpenIddict.MongoDb/Stores/OpenIddictScopeStore.cs +++ b/src/OpenIddict.MongoDb/Stores/OpenIddictScopeStore.cs @@ -5,8 +5,10 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -52,10 +54,10 @@ namespace OpenIddict.MongoDb /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes in the database. /// - public virtual async Task CountAsync(CancellationToken cancellationToken) + public virtual async ValueTask CountAsync(CancellationToken cancellationToken) { var database = await Context.GetDatabaseAsync(cancellationToken); var collection = database.GetCollection(Options.CurrentValue.ScopesCollectionName); @@ -70,10 +72,10 @@ namespace OpenIddict.MongoDb /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes that match the specified query. /// - public virtual async Task CountAsync( + public virtual async ValueTask CountAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) @@ -92,10 +94,8 @@ namespace OpenIddict.MongoDb /// /// The scope to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { @@ -113,10 +113,8 @@ namespace OpenIddict.MongoDb /// /// The scope to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { @@ -143,10 +141,10 @@ namespace OpenIddict.MongoDb /// The unique identifier associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the identifier. /// - public virtual async Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual async ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -165,10 +163,10 @@ namespace OpenIddict.MongoDb /// The name associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the specified name. /// - public virtual async Task FindByNameAsync([NotNull] string name, CancellationToken cancellationToken) + public virtual async ValueTask FindByNameAsync([NotNull] string name, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(name)) { @@ -186,11 +184,8 @@ namespace OpenIddict.MongoDb /// /// The names associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes corresponding to the specified names. - /// - public virtual async Task> FindByNamesAsync( + /// The scopes corresponding to the specified names. + public virtual IAsyncEnumerable FindByNamesAsync( ImmutableArray names, CancellationToken cancellationToken) { if (names.Any(name => string.IsNullOrEmpty(name))) @@ -198,10 +193,18 @@ namespace OpenIddict.MongoDb throw new ArgumentException("Scope names cannot be null or empty.", nameof(names)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.ScopesCollectionName); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange(await collection.Find(scope => names.Contains(scope.Name)).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.ScopesCollectionName); + + await foreach (var scope in collection.Find(scope => names.Contains(scope.Name)).ToAsyncEnumerable(cancellationToken)) + { + yield return scope; + } + } } /// @@ -209,11 +212,8 @@ namespace OpenIddict.MongoDb /// /// The resource associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes associated with the specified resource. - /// - public virtual async Task> FindByResourceAsync( + /// The scopes associated with the specified resource. + public virtual IAsyncEnumerable FindByResourceAsync( [NotNull] string resource, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(resource)) @@ -221,11 +221,18 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The resource cannot be null or empty.", nameof(resource)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.ScopesCollectionName); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.ScopesCollectionName); - return ImmutableArray.CreateRange(await collection.Find(scope => - scope.Resources.Contains(resource)).ToListAsync(cancellationToken)); + await foreach (var scope in collection.Find(scope => scope.Resources.Contains(resource)).ToAsyncEnumerable(cancellationToken)) + { + yield return scope; + } + } } /// @@ -237,10 +244,10 @@ namespace OpenIddict.MongoDb /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual async Task GetAsync( + public virtual async ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -411,12 +418,9 @@ namespace OpenIddict.MongoDb /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( - [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) + /// All the elements returned when executing the specified query. + public virtual async IAsyncEnumerable ListAsync( + [CanBeNull] int? count, [CanBeNull] int? offset, [EnumeratorCancellation] CancellationToken cancellationToken) { var database = await Context.GetDatabaseAsync(cancellationToken); var collection = database.GetCollection(Options.CurrentValue.ScopesCollectionName); @@ -433,7 +437,10 @@ namespace OpenIddict.MongoDb query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + await foreach (var scope in ((IAsyncCursorSource) query).ToAsyncEnumerable(cancellationToken)) + { + yield return scope; + } } /// @@ -444,11 +451,8 @@ namespace OpenIddict.MongoDb /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -457,11 +461,18 @@ namespace OpenIddict.MongoDb throw new ArgumentNullException(nameof(query)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.ScopesCollectionName); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange( - await ((IMongoQueryable) query(collection.AsQueryable(), state)).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.ScopesCollectionName); + + await foreach (var element in query(collection.AsQueryable(), state).ToAsyncEnumerable(cancellationToken)) + { + yield return element; + } + } } /// @@ -470,10 +481,8 @@ namespace OpenIddict.MongoDb /// The scope. /// The description associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken) { if (scope == null) { @@ -482,7 +491,7 @@ namespace OpenIddict.MongoDb scope.Description = description; - return Task.CompletedTask; + return default; } /// @@ -491,10 +500,8 @@ namespace OpenIddict.MongoDb /// The scope. /// The display name associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetDisplayNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetDisplayNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) { if (scope == null) { @@ -503,7 +510,7 @@ namespace OpenIddict.MongoDb scope.DisplayName = name; - return Task.CompletedTask; + return default; } /// @@ -512,10 +519,8 @@ namespace OpenIddict.MongoDb /// The scope. /// The name associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) { if (scope == null) { @@ -524,7 +529,7 @@ namespace OpenIddict.MongoDb scope.Name = name; - return Task.CompletedTask; + return default; } /// @@ -533,10 +538,8 @@ namespace OpenIddict.MongoDb /// The scope. /// The additional properties associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (scope == null) { @@ -547,12 +550,12 @@ namespace OpenIddict.MongoDb { scope.Properties = null; - return Task.CompletedTask; + return default; } scope.Properties = BsonDocument.Parse(properties.ToString(Formatting.None)); - return Task.CompletedTask; + return default; } /// @@ -561,10 +564,8 @@ namespace OpenIddict.MongoDb /// The scope. /// The resources associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetResourcesAsync([NotNull] TScope scope, ImmutableArray resources, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetResourcesAsync([NotNull] TScope scope, ImmutableArray resources, CancellationToken cancellationToken) { if (scope == null) { @@ -575,12 +576,12 @@ namespace OpenIddict.MongoDb { scope.Resources = null; - return Task.CompletedTask; + return default; } scope.Resources = resources.ToArray(); - return Task.CompletedTask; + return default; } /// @@ -588,10 +589,8 @@ namespace OpenIddict.MongoDb /// /// The scope to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictTokenStore.cs index fbb90698..60471335 100644 --- a/src/OpenIddict.MongoDb/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.MongoDb/Stores/OpenIddictTokenStore.cs @@ -5,8 +5,10 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -52,10 +54,10 @@ namespace OpenIddict.MongoDb /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications in the database. /// - public virtual async Task CountAsync(CancellationToken cancellationToken) + public virtual async ValueTask CountAsync(CancellationToken cancellationToken) { var database = await Context.GetDatabaseAsync(cancellationToken); var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); @@ -70,10 +72,10 @@ namespace OpenIddict.MongoDb /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of tokens that match the specified query. /// - public virtual async Task CountAsync( + public virtual async ValueTask CountAsync( [NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) @@ -92,10 +94,8 @@ namespace OpenIddict.MongoDb /// /// The token to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { @@ -113,10 +113,8 @@ namespace OpenIddict.MongoDb /// /// The token to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { @@ -144,11 +142,8 @@ namespace OpenIddict.MongoDb /// The subject associated with the token. /// The client associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the subject/client. - /// - public virtual async Task> FindAsync([NotNull] string subject, + /// The tokens corresponding to the subject/client. + public virtual IAsyncEnumerable FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -161,12 +156,20 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The client cannot be null or empty.", nameof(client)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange(await collection.Find(token => - token.ApplicationId == ObjectId.Parse(client) && - token.Subject == subject).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); + + await foreach (var token in collection.Find(token => + token.ApplicationId == ObjectId.Parse(client) && + token.Subject == subject).ToAsyncEnumerable(cancellationToken)) + { + yield return token; + } + } } /// @@ -176,11 +179,8 @@ namespace OpenIddict.MongoDb /// The client associated with the token. /// The token status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The tokens corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken) { @@ -199,13 +199,21 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The status cannot be null or empty.", nameof(status)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); - return ImmutableArray.CreateRange(await collection.Find(token => - token.ApplicationId == ObjectId.Parse(client) && - token.Subject == subject && - token.Status == status).ToListAsync(cancellationToken)); + await foreach (var token in collection.Find(token => + token.ApplicationId == ObjectId.Parse(client) && + token.Subject == subject && + token.Status == status).ToAsyncEnumerable(cancellationToken)) + { + yield return token; + } + } } /// @@ -216,11 +224,8 @@ namespace OpenIddict.MongoDb /// The token status. /// The token type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The tokens corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken) { @@ -244,14 +249,22 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The type cannot be null or empty.", nameof(type)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); - return ImmutableArray.CreateRange(await collection.Find(token => - token.ApplicationId == ObjectId.Parse(client) && - token.Subject == subject && - token.Status == status && - token.Type == type).ToListAsync(cancellationToken)); + await foreach (var token in collection.Find(token => + token.ApplicationId == ObjectId.Parse(client) && + token.Subject == subject && + token.Status == status && + token.Type == type).ToAsyncEnumerable(cancellationToken)) + { + yield return token; + } + } } /// @@ -259,11 +272,8 @@ namespace OpenIddict.MongoDb /// /// The application identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified application. - /// - public virtual async Task> FindByApplicationIdAsync( + /// The tokens corresponding to the specified application. + public virtual IAsyncEnumerable FindByApplicationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) @@ -271,11 +281,19 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); - return ImmutableArray.CreateRange(await collection.Find(token => - token.ApplicationId == ObjectId.Parse(identifier)).ToListAsync(cancellationToken)); + await foreach (var token in collection.Find(token => + token.ApplicationId == ObjectId.Parse(identifier)).ToAsyncEnumerable(cancellationToken)) + { + yield return token; + } + } } /// @@ -283,11 +301,8 @@ namespace OpenIddict.MongoDb /// /// The authorization identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified authorization. - /// - public virtual async Task> FindByAuthorizationIdAsync( + /// The tokens corresponding to the specified authorization. + public virtual IAsyncEnumerable FindByAuthorizationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) @@ -295,10 +310,19 @@ namespace OpenIddict.MongoDb throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange(await collection.Find(token => token.AuthorizationId == ObjectId.Parse(identifier)).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); + + await foreach (var token in collection.Find(token => + token.AuthorizationId == ObjectId.Parse(identifier)).ToAsyncEnumerable(cancellationToken)) + { + yield return token; + } + } } /// @@ -307,10 +331,10 @@ namespace OpenIddict.MongoDb /// The unique identifier associated with the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the token corresponding to the unique identifier. /// - public virtual async Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual async ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -330,10 +354,10 @@ namespace OpenIddict.MongoDb /// The reference identifier associated with the tokens. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the tokens corresponding to the specified reference identifier. /// - public virtual async Task FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual async ValueTask FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -351,21 +375,26 @@ namespace OpenIddict.MongoDb /// /// The subject associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified subject. - /// - public virtual async Task> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken) + /// The tokens corresponding to the specified subject. + public virtual IAsyncEnumerable FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) { throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange(await collection.Find(token => token.Subject == subject).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); + + await foreach (var token in collection.Find(token => token.Subject == subject).ToAsyncEnumerable(cancellationToken)) + { + yield return token; + } + } } /// @@ -396,10 +425,10 @@ namespace OpenIddict.MongoDb /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual async Task GetAsync( + public virtual async ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -643,12 +672,9 @@ namespace OpenIddict.MongoDb /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( - [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) + /// All the elements returned when executing the specified query. + public virtual async IAsyncEnumerable ListAsync( + [CanBeNull] int? count, [CanBeNull] int? offset, [EnumeratorCancellation] CancellationToken cancellationToken) { var database = await Context.GetDatabaseAsync(cancellationToken); var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); @@ -665,7 +691,10 @@ namespace OpenIddict.MongoDb query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + await foreach (var token in ((IAsyncCursorSource) query).ToAsyncEnumerable(cancellationToken)) + { + yield return token; + } } /// @@ -676,11 +705,8 @@ namespace OpenIddict.MongoDb /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -689,21 +715,26 @@ namespace OpenIddict.MongoDb throw new ArgumentNullException(nameof(query)); } - var database = await Context.GetDatabaseAsync(cancellationToken); - var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); - return ImmutableArray.CreateRange( - await ((IMongoQueryable) query(collection.AsQueryable(), state)).ToListAsync(cancellationToken)); + await foreach (var element in query(collection.AsQueryable(), state).ToAsyncEnumerable(cancellationToken)) + { + yield return element; + } + } } /// /// Removes the tokens that are marked as expired or invalid. /// /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task PruneAsync(CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask PruneAsync(CancellationToken cancellationToken) { var database = await Context.GetDatabaseAsync(cancellationToken); var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName); @@ -718,10 +749,8 @@ namespace OpenIddict.MongoDb /// The token. /// The unique identifier associated with the client application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetApplicationIdAsync([NotNull] TToken token, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (token == null) @@ -739,7 +768,7 @@ namespace OpenIddict.MongoDb token.ApplicationId = ObjectId.Empty; } - return Task.CompletedTask; + return default; } /// @@ -748,10 +777,8 @@ namespace OpenIddict.MongoDb /// The token. /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetAuthorizationIdAsync([NotNull] TToken token, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (token == null) @@ -769,7 +796,7 @@ namespace OpenIddict.MongoDb token.AuthorizationId = ObjectId.Empty; } - return Task.CompletedTask; + return default; } /// @@ -778,10 +805,8 @@ namespace OpenIddict.MongoDb /// The token. /// The creation date. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetCreationDateAsync([NotNull] TToken token, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetCreationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken) { if (token == null) @@ -791,7 +816,7 @@ namespace OpenIddict.MongoDb token.CreationDate = date?.UtcDateTime; - return Task.CompletedTask; + return default; } /// @@ -800,10 +825,8 @@ namespace OpenIddict.MongoDb /// The token. /// The expiration date. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetExpirationDateAsync([NotNull] TToken token, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetExpirationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken) { if (token == null) @@ -813,7 +836,7 @@ namespace OpenIddict.MongoDb token.ExpirationDate = date?.UtcDateTime; - return Task.CompletedTask; + return default; } /// @@ -822,10 +845,8 @@ namespace OpenIddict.MongoDb /// The token. /// The payload associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken) { if (token == null) { @@ -834,7 +855,7 @@ namespace OpenIddict.MongoDb token.Payload = payload; - return Task.CompletedTask; + return default; } /// @@ -843,10 +864,8 @@ namespace OpenIddict.MongoDb /// The token. /// The additional properties associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (token == null) { @@ -857,12 +876,12 @@ namespace OpenIddict.MongoDb { token.Properties = null; - return Task.CompletedTask; + return default; } token.Properties = BsonDocument.Parse(properties.ToString(Formatting.None)); - return Task.CompletedTask; + return default; } /// @@ -873,10 +892,8 @@ namespace OpenIddict.MongoDb /// The token. /// The reference identifier associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (token == null) { @@ -885,7 +902,7 @@ namespace OpenIddict.MongoDb token.ReferenceId = identifier; - return Task.CompletedTask; + return default; } /// @@ -894,10 +911,8 @@ namespace OpenIddict.MongoDb /// The token. /// The status associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken) { if (token == null) { @@ -906,7 +921,7 @@ namespace OpenIddict.MongoDb token.Status = status; - return Task.CompletedTask; + return default; } /// @@ -915,10 +930,8 @@ namespace OpenIddict.MongoDb /// The token. /// The subject associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken) { if (token == null) { @@ -927,7 +940,7 @@ namespace OpenIddict.MongoDb token.Subject = subject; - return Task.CompletedTask; + return default; } /// @@ -936,10 +949,8 @@ namespace OpenIddict.MongoDb /// The token. /// The token type associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken) { if (token == null) { @@ -948,7 +959,7 @@ namespace OpenIddict.MongoDb token.Type = type; - return Task.CompletedTask; + return default; } /// @@ -956,10 +967,8 @@ namespace OpenIddict.MongoDb /// /// The token to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { diff --git a/src/OpenIddict.NHibernate/OpenIddictNHibernateHelpers.cs b/src/OpenIddict.NHibernate/OpenIddictNHibernateHelpers.cs index 3d6d39e9..9899ccba 100644 --- a/src/OpenIddict.NHibernate/OpenIddictNHibernateHelpers.cs +++ b/src/OpenIddict.NHibernate/OpenIddictNHibernateHelpers.cs @@ -5,7 +5,11 @@ */ using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; using JetBrains.Annotations; +using NHibernate.Linq; using NHibernate.Mapping.ByCode; using OpenIddict.NHibernate; using OpenIddict.NHibernate.Models; @@ -13,7 +17,7 @@ using OpenIddict.NHibernate.Models; namespace NHibernate.Cfg { /// - /// Exposes extensions allowing to register the OpenIddict NHibernate mappings. + /// Exposes extensions simplifying the integration between OpenIddict and NHibernate. /// public static class OpenIddictNHibernateHelpers { @@ -70,5 +74,30 @@ namespace NHibernate.Cfg return configuration; } + + /// + /// Executes the query and returns the results as a non-streamed async enumeration. + /// + /// The type of the returned entities. + /// The query source. + /// The that can be used to abort the operation. + /// The non-streamed async enumeration containing the results. + internal static IAsyncEnumerable AsAsyncEnumerable([NotNull] this IQueryable source, CancellationToken cancellationToken) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + foreach (var element in await source.ToListAsync(cancellationToken)) + { + yield return element; + } + } + } } } diff --git a/src/OpenIddict.NHibernate/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.NHibernate/Stores/OpenIddictApplicationStore.cs index 72579b40..2f60bf1b 100644 --- a/src/OpenIddict.NHibernate/Stores/OpenIddictApplicationStore.cs +++ b/src/OpenIddict.NHibernate/Stores/OpenIddictApplicationStore.cs @@ -5,9 +5,11 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -17,6 +19,7 @@ using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NHibernate; +using NHibernate.Cfg; using NHibernate.Linq; using OpenIddict.Abstractions; using OpenIddict.NHibernate.Models; @@ -100,10 +103,10 @@ namespace OpenIddict.NHibernate /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications in the database. /// - public virtual async Task CountAsync(CancellationToken cancellationToken) + public virtual async ValueTask CountAsync(CancellationToken cancellationToken) { var session = await Context.GetSessionAsync(cancellationToken); return await session.Query().LongCountAsync(cancellationToken); @@ -116,10 +119,10 @@ namespace OpenIddict.NHibernate /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications that match the specified query. /// - public virtual async Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + public virtual async ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) { @@ -135,10 +138,8 @@ namespace OpenIddict.NHibernate /// /// The application to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { @@ -155,10 +156,8 @@ namespace OpenIddict.NHibernate /// /// The application to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { @@ -171,13 +170,13 @@ namespace OpenIddict.NHibernate { // Delete all the tokens associated with the application. await (from authorization in session.Query() - where authorization.Application.Id.Equals(application.Id) - select authorization).DeleteAsync(cancellationToken); + where authorization.Application.Id.Equals(application.Id) + select authorization).DeleteAsync(cancellationToken); // Delete all the tokens associated with the application. await (from token in session.Query() - where token.Application.Id.Equals(application.Id) - select token).DeleteAsync(cancellationToken); + where token.Application.Id.Equals(application.Id) + select token).DeleteAsync(cancellationToken); await session.DeleteAsync(application, cancellationToken); await session.FlushAsync(cancellationToken); @@ -198,10 +197,10 @@ namespace OpenIddict.NHibernate /// The client identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - public virtual async Task FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual async ValueTask FindByClientIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -221,10 +220,10 @@ namespace OpenIddict.NHibernate /// The unique identifier associated with the application. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the client application corresponding to the identifier. /// - public virtual async Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual async ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -240,48 +239,34 @@ namespace OpenIddict.NHibernate /// /// The post_logout_redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified post_logout_redirect_uri. - /// - public virtual async Task> FindByPostLogoutRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken) + /// The client applications corresponding to the specified post_logout_redirect_uri. + public virtual IAsyncEnumerable FindByPostLogoutRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(address)) { throw new ArgumentException("The address cannot be null or empty.", nameof(address)); } - var session = await Context.GetSessionAsync(cancellationToken); - - // To optimize the efficiency of the query a bit, only applications whose stringified - // PostLogoutRedirectUris contains the specified URL are returned. Once the applications - // are retrieved, a second pass is made to ensure only valid elements are returned. - // Implementers that use this method in a hot path may want to override this method - // to use SQL Server 2016 functions like JSON_VALUE to make the query more efficient. - var applications = await (from application in session.Query() - where application.PostLogoutRedirectUris.Contains(address) - select application).ToListAsync(cancellationToken); - - var builder = ImmutableArray.CreateBuilder(); + return ExecuteAsync(cancellationToken); - foreach (var application in applications) + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) { - foreach (var uri in await GetPostLogoutRedirectUrisAsync(application, cancellationToken)) + var session = await Context.GetSessionAsync(cancellationToken); + + // To optimize the efficiency of the query a bit, only applications whose stringified + // PostLogoutRedirectUris contains the specified URL are returned. Once the applications + // are retrieved, a second pass is made to ensure only valid elements are returned. + // Implementers that use this method in a hot path may want to override this method + // to use SQL Server 2016 functions like JSON_VALUE to make the query more efficient. + await foreach (var application in session.Query() + .Where(application => application.PostLogoutRedirectUris.Contains(address)) + .AsAsyncEnumerable(cancellationToken) + .WhereAwait(async application => (await GetPostLogoutRedirectUrisAsync(application, cancellationToken)) + .Contains(address, StringComparer.Ordinal))) { - // Note: the post_logout_redirect_uri must be compared - // using case-sensitive "Simple String Comparison". - if (string.Equals(uri, address, StringComparison.Ordinal)) - { - builder.Add(application); - - break; - } + yield return application; } } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); } /// @@ -289,48 +274,34 @@ namespace OpenIddict.NHibernate /// /// The redirect_uri associated with the applications. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, whose result - /// returns the client applications corresponding to the specified redirect_uri. - /// - public virtual async Task> FindByRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken) + /// The client applications corresponding to the specified redirect_uri. + public virtual IAsyncEnumerable FindByRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(address)) { throw new ArgumentException("The address cannot be null or empty.", nameof(address)); } - var session = await Context.GetSessionAsync(cancellationToken); - - // To optimize the efficiency of the query a bit, only applications whose stringified - // RedirectUris property contains the specified URL are returned. Once the applications - // are retrieved, a second pass is made to ensure only valid elements are returned. - // Implementers that use this method in a hot path may want to override this method - // to use SQL Server 2016 functions like JSON_VALUE to make the query more efficient. - var applications = await (from application in session.Query() - where application.RedirectUris.Contains(address) - select application).ToListAsync(cancellationToken); - - var builder = ImmutableArray.CreateBuilder(); + return ExecuteAsync(cancellationToken); - foreach (var application in applications) + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) { - foreach (var uri in await GetRedirectUrisAsync(application, cancellationToken)) + var session = await Context.GetSessionAsync(cancellationToken); + + // To optimize the efficiency of the query a bit, only applications whose stringified + // RedirectUris contains the specified URL are returned. Once the applications + // are retrieved, a second pass is made to ensure only valid elements are returned. + // Implementers that use this method in a hot path may want to override this method + // to use SQL Server 2016 functions like JSON_VALUE to make the query more efficient. + await foreach (var application in session.Query() + .Where(application => application.RedirectUris.Contains(address)) + .AsAsyncEnumerable(cancellationToken) + .WhereAwait(async application => (await GetRedirectUrisAsync(application, cancellationToken)) + .Contains(address, StringComparer.Ordinal))) { - // Note: the redirect_uri must be compared using case-sensitive "Simple String Comparison". - // See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest for more information. - if (string.Equals(uri, address, StringComparison.Ordinal)) - { - builder.Add(application); - - break; - } + yield return application; } } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); } /// @@ -342,10 +313,10 @@ namespace OpenIddict.NHibernate /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual async Task GetAsync( + public virtual async ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -641,12 +612,9 @@ namespace OpenIddict.NHibernate /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( - [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) + /// All the elements returned when executing the specified query. + public virtual async IAsyncEnumerable ListAsync( + [CanBeNull] int? count, [CanBeNull] int? offset, [EnumeratorCancellation] CancellationToken cancellationToken) { var session = await Context.GetSessionAsync(cancellationToken); var query = session.Query() @@ -663,7 +631,10 @@ namespace OpenIddict.NHibernate query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + await foreach (var application in query.AsAsyncEnumerable(cancellationToken)) + { + yield return application; + } } /// @@ -674,11 +645,8 @@ namespace OpenIddict.NHibernate /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -687,8 +655,17 @@ namespace OpenIddict.NHibernate throw new ArgumentNullException(nameof(query)); } - var session = await Context.GetSessionAsync(cancellationToken); - return ImmutableArray.CreateRange(await query(session.Query(), state).ToListAsync(cancellationToken)); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + + await foreach (var element in query(session.Query(), state).AsAsyncEnumerable(cancellationToken)) + { + yield return element; + } + } } /// @@ -697,10 +674,8 @@ namespace OpenIddict.NHibernate /// The application. /// The client identifier associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetClientIdAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetClientIdAsync([NotNull] TApplication application, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (application == null) @@ -710,7 +685,7 @@ namespace OpenIddict.NHibernate application.ClientId = identifier; - return Task.CompletedTask; + return default; } /// @@ -721,10 +696,8 @@ namespace OpenIddict.NHibernate /// The application. /// The client secret associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetClientSecretAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetClientSecretAsync([NotNull] TApplication application, [CanBeNull] string secret, CancellationToken cancellationToken) { if (application == null) @@ -734,7 +707,7 @@ namespace OpenIddict.NHibernate application.ClientSecret = secret; - return Task.CompletedTask; + return default; } /// @@ -743,10 +716,8 @@ namespace OpenIddict.NHibernate /// The application. /// The client type associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetClientTypeAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetClientTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken) { if (application == null) @@ -756,7 +727,7 @@ namespace OpenIddict.NHibernate application.Type = type; - return Task.CompletedTask; + return default; } /// @@ -765,10 +736,8 @@ namespace OpenIddict.NHibernate /// The application. /// The consent type associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetConsentTypeAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetConsentTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken) { if (application == null) @@ -778,7 +747,7 @@ namespace OpenIddict.NHibernate application.ConsentType = type; - return Task.CompletedTask; + return default; } /// @@ -787,10 +756,8 @@ namespace OpenIddict.NHibernate /// The application. /// The display name associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetDisplayNameAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetDisplayNameAsync([NotNull] TApplication application, [CanBeNull] string name, CancellationToken cancellationToken) { if (application == null) @@ -800,7 +767,7 @@ namespace OpenIddict.NHibernate application.DisplayName = name; - return Task.CompletedTask; + return default; } /// @@ -809,10 +776,8 @@ namespace OpenIddict.NHibernate /// The application. /// The permissions associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPermissionsAsync([NotNull] TApplication application, ImmutableArray permissions, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPermissionsAsync([NotNull] TApplication application, ImmutableArray permissions, CancellationToken cancellationToken) { if (application == null) { @@ -823,12 +788,12 @@ namespace OpenIddict.NHibernate { application.Permissions = null; - return Task.CompletedTask; + return default; } application.Permissions = new JArray(permissions.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -837,10 +802,8 @@ namespace OpenIddict.NHibernate /// The application. /// The logout callback addresses associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPostLogoutRedirectUrisAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPostLogoutRedirectUrisAsync([NotNull] TApplication application, ImmutableArray addresses, CancellationToken cancellationToken) { if (application == null) @@ -852,12 +815,12 @@ namespace OpenIddict.NHibernate { application.PostLogoutRedirectUris = null; - return Task.CompletedTask; + return default; } application.PostLogoutRedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -866,10 +829,8 @@ namespace OpenIddict.NHibernate /// The application. /// The additional properties associated with the application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (application == null) { @@ -880,12 +841,12 @@ namespace OpenIddict.NHibernate { application.Properties = null; - return Task.CompletedTask; + return default; } application.Properties = properties.ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -894,10 +855,8 @@ namespace OpenIddict.NHibernate /// The application. /// The callback addresses associated with the application /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetRedirectUrisAsync([NotNull] TApplication application, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetRedirectUrisAsync([NotNull] TApplication application, ImmutableArray addresses, CancellationToken cancellationToken) { if (application == null) @@ -909,12 +868,12 @@ namespace OpenIddict.NHibernate { application.RedirectUris = null; - return Task.CompletedTask; + return default; } application.RedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -922,10 +881,8 @@ namespace OpenIddict.NHibernate /// /// The application to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken) { if (application == null) { diff --git a/src/OpenIddict.NHibernate/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.NHibernate/Stores/OpenIddictAuthorizationStore.cs index f96a3ecb..80e065db 100644 --- a/src/OpenIddict.NHibernate/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.NHibernate/Stores/OpenIddictAuthorizationStore.cs @@ -5,9 +5,11 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -17,6 +19,7 @@ using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NHibernate; +using NHibernate.Cfg; using NHibernate.Linq; using OpenIddict.Abstractions; using OpenIddict.NHibernate.Models; @@ -100,10 +103,10 @@ namespace OpenIddict.NHibernate /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations in the database. /// - public virtual async Task CountAsync(CancellationToken cancellationToken) + public virtual async ValueTask CountAsync(CancellationToken cancellationToken) { var session = await Context.GetSessionAsync(cancellationToken); return await session.Query().LongCountAsync(cancellationToken); @@ -116,10 +119,10 @@ namespace OpenIddict.NHibernate /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of authorizations that match the specified query. /// - public virtual async Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + public virtual async ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) { @@ -135,10 +138,8 @@ namespace OpenIddict.NHibernate /// /// The authorization to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { @@ -155,10 +156,8 @@ namespace OpenIddict.NHibernate /// /// The authorization to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { @@ -194,11 +193,8 @@ namespace OpenIddict.NHibernate /// The subject associated with the authorization. /// The client associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the subject/client. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the subject/client. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -211,16 +207,23 @@ namespace OpenIddict.NHibernate throw new ArgumentException("The client cannot be null or empty.", nameof(client)); } - var session = await Context.GetSessionAsync(cancellationToken); + return ExecuteAsync(cancellationToken); - var key = ConvertIdentifierFromString(client); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + var key = ConvertIdentifierFromString(client); - return ImmutableArray.CreateRange( - await (from authorization in session.Query().Fetch(authorization => authorization.Application) - where authorization.Application != null && - authorization.Application.Id.Equals(key) && - authorization.Subject == subject - select authorization).ToListAsync(cancellationToken)); + await foreach (var authorization in + (from authorization in session.Query().Fetch(authorization => authorization.Application) + where authorization.Application != null && + authorization.Application.Id.Equals(key) && + authorization.Subject == subject + select authorization).AsAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } + } } /// @@ -230,11 +233,8 @@ namespace OpenIddict.NHibernate /// The client associated with the authorization. /// The authorization status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken) { @@ -253,17 +253,24 @@ namespace OpenIddict.NHibernate throw new ArgumentException("The status cannot be null or empty.", nameof(status)); } - var session = await Context.GetSessionAsync(cancellationToken); + return ExecuteAsync(cancellationToken); - var key = ConvertIdentifierFromString(client); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + var key = ConvertIdentifierFromString(client); - return ImmutableArray.CreateRange( - await (from authorization in session.Query().Fetch(authorization => authorization.Application) - where authorization.Application != null && - authorization.Application.Id.Equals(key) && - authorization.Subject == subject && - authorization.Status == status - select authorization).ToListAsync(cancellationToken)); + await foreach (var authorization in + (from authorization in session.Query().Fetch(authorization => authorization.Application) + where authorization.Application != null && + authorization.Application.Id.Equals(key) && + authorization.Subject == subject && + authorization.Status == status + select authorization).AsAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } + } } /// @@ -274,11 +281,8 @@ namespace OpenIddict.NHibernate /// The authorization status. /// The authorization type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken) { @@ -302,18 +306,25 @@ namespace OpenIddict.NHibernate throw new ArgumentException("The type cannot be null or empty.", nameof(type)); } - var session = await Context.GetSessionAsync(cancellationToken); + return ExecuteAsync(cancellationToken); - var key = ConvertIdentifierFromString(client); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + var key = ConvertIdentifierFromString(client); - return ImmutableArray.CreateRange( - await (from authorization in session.Query().Fetch(authorization => authorization.Application) - where authorization.Application != null && - authorization.Application.Id.Equals(key) && - authorization.Subject == subject && - authorization.Status == status && - authorization.Type == type - select authorization).ToListAsync(cancellationToken)); + await foreach (var authorization in + (from authorization in session.Query().Fetch(authorization => authorization.Application) + where authorization.Application != null && + authorization.Application.Id.Equals(key) && + authorization.Subject == subject && + authorization.Status == status && + authorization.Type == type + select authorization).AsAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } + } } /// @@ -325,51 +336,22 @@ namespace OpenIddict.NHibernate /// The authorization type. /// The minimal scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The authorizations corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken) - { - var authorizations = await FindAsync(subject, client, status, type, cancellationToken); - if (authorizations.IsEmpty) - { - return ImmutableArray.Create(); - } - - var builder = ImmutableArray.CreateBuilder(authorizations.Length); - - foreach (var authorization in authorizations) - { - async Task HasScopesAsync() - => (await GetScopesAsync(authorization, cancellationToken)) - .ToImmutableHashSet(StringComparer.Ordinal) - .IsSupersetOf(scopes); - - if (await HasScopesAsync()) - { - builder.Add(authorization); - } - } - - return builder.Count == builder.Capacity ? - builder.MoveToImmutable() : - builder.ToImmutable(); - } + => FindAsync(subject, client, status, type, cancellationToken) + .WhereAwait(async authorization => new HashSet( + await GetScopesAsync(authorization, cancellationToken), StringComparer.Ordinal).IsSupersetOf(scopes)); /// /// Retrieves the list of authorizations corresponding to the specified application identifier. /// /// The application identifier associated with the authorizations. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified application. - /// - public virtual async Task> FindByApplicationIdAsync( + /// The authorizations corresponding to the specified application. + public virtual IAsyncEnumerable FindByApplicationIdAsync( [NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) @@ -377,15 +359,22 @@ namespace OpenIddict.NHibernate throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - var session = await Context.GetSessionAsync(cancellationToken); + return ExecuteAsync(cancellationToken); - var key = ConvertIdentifierFromString(identifier); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + var key = ConvertIdentifierFromString(identifier); - return ImmutableArray.CreateRange( - await (from authorization in session.Query().Fetch(authorization => authorization.Application) - where authorization.Application != null && - authorization.Application.Id.Equals(key) - select authorization).ToListAsync(cancellationToken)); + await foreach (var authorization in + (from authorization in session.Query().Fetch(authorization => authorization.Application) + where authorization.Application != null && + authorization.Application.Id.Equals(key) + select authorization).AsAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } + } } /// @@ -394,10 +383,10 @@ namespace OpenIddict.NHibernate /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the authorization corresponding to the identifier. /// - public virtual async Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual async ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -413,11 +402,8 @@ namespace OpenIddict.NHibernate /// /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the authorizations corresponding to the specified subject. - /// - public virtual async Task> FindBySubjectAsync( + /// The authorizations corresponding to the specified subject. + public virtual IAsyncEnumerable FindBySubjectAsync( [NotNull] string subject, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -425,12 +411,20 @@ namespace OpenIddict.NHibernate throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); } - var session = await Context.GetSessionAsync(cancellationToken); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange( - await (from authorization in session.Query().Fetch(authorization => authorization.Application) - where authorization.Subject == subject - select authorization).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + + await foreach (var authorization in + (from authorization in session.Query().Fetch(authorization => authorization.Application) + where authorization.Subject == subject + select authorization).AsAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } + } } /// @@ -466,10 +460,10 @@ namespace OpenIddict.NHibernate /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual async Task GetAsync( + public virtual async ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -548,7 +542,20 @@ namespace OpenIddict.NHibernate return new ValueTask>(ImmutableArray.Create()); } - return new ValueTask>(JArray.Parse(authorization.Scopes).Select(element => (string) element).ToImmutableArray()); + // Note: parsing the stringified scopes is an expensive operation. + // To mitigate that, the resulting array is stored in the memory cache. + var key = string.Concat("2ba4ab0f-e2ec-4d48-b3bd-28e2bb660c75", "\x1e", authorization.Scopes); + var scopes = Cache.GetOrCreate(key, entry => + { + entry.SetPriority(CacheItemPriority.High) + .SetSlidingExpiration(TimeSpan.FromMinutes(1)); + + return JArray.Parse(authorization.Scopes) + .Select(element => (string) element) + .ToImmutableArray(); + }); + + return new ValueTask>(scopes); } /// @@ -640,12 +647,9 @@ namespace OpenIddict.NHibernate /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( - [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) + /// All the elements returned when executing the specified query. + public virtual async IAsyncEnumerable ListAsync( + [CanBeNull] int? count, [CanBeNull] int? offset, [EnumeratorCancellation] CancellationToken cancellationToken) { var session = await Context.GetSessionAsync(cancellationToken); var query = session.Query() @@ -663,7 +667,10 @@ namespace OpenIddict.NHibernate query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + await foreach (var authorization in query.AsAsyncEnumerable(cancellationToken)) + { + yield return authorization; + } } /// @@ -674,11 +681,8 @@ namespace OpenIddict.NHibernate /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -687,20 +691,27 @@ namespace OpenIddict.NHibernate throw new ArgumentNullException(nameof(query)); } - var session = await Context.GetSessionAsync(cancellationToken); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); - return ImmutableArray.CreateRange(await query( - session.Query().Fetch(authorization => authorization.Application), state).ToListAsync(cancellationToken)); + await foreach (var element in query( + session.Query() + .Fetch(authorization => authorization.Application), state).AsAsyncEnumerable(cancellationToken)) + { + yield return element; + } + } } /// /// Removes the authorizations that are marked as invalid and the ad-hoc ones that have no valid/nonexpired token attached. /// /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task PruneAsync(CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask PruneAsync(CancellationToken cancellationToken) { var session = await Context.GetSessionAsync(cancellationToken); @@ -723,10 +734,8 @@ namespace OpenIddict.NHibernate /// The authorization. /// The unique identifier associated with the client application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task SetApplicationIdAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask SetApplicationIdAsync([NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (authorization == null) @@ -753,10 +762,8 @@ namespace OpenIddict.NHibernate /// The authorization. /// The additional properties associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (authorization == null) { @@ -767,12 +774,12 @@ namespace OpenIddict.NHibernate { authorization.Properties = null; - return Task.CompletedTask; + return default; } authorization.Properties = properties.ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -781,10 +788,8 @@ namespace OpenIddict.NHibernate /// The authorization. /// The scopes associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetScopesAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetScopesAsync([NotNull] TAuthorization authorization, ImmutableArray scopes, CancellationToken cancellationToken) { if (authorization == null) @@ -796,12 +801,12 @@ namespace OpenIddict.NHibernate { authorization.Scopes = null; - return Task.CompletedTask; + return default; } authorization.Scopes = new JArray(scopes.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -810,10 +815,8 @@ namespace OpenIddict.NHibernate /// The authorization. /// The status associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetStatusAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetStatusAsync([NotNull] TAuthorization authorization, [CanBeNull] string status, CancellationToken cancellationToken) { if (authorization == null) @@ -823,7 +826,7 @@ namespace OpenIddict.NHibernate authorization.Status = status; - return Task.CompletedTask; + return default; } /// @@ -832,10 +835,8 @@ namespace OpenIddict.NHibernate /// The authorization. /// The subject associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetSubjectAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetSubjectAsync([NotNull] TAuthorization authorization, [CanBeNull] string subject, CancellationToken cancellationToken) { if (authorization == null) @@ -845,7 +846,7 @@ namespace OpenIddict.NHibernate authorization.Subject = subject; - return Task.CompletedTask; + return default; } /// @@ -854,10 +855,8 @@ namespace OpenIddict.NHibernate /// The authorization. /// The type associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetTypeAsync([NotNull] TAuthorization authorization, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetTypeAsync([NotNull] TAuthorization authorization, [CanBeNull] string type, CancellationToken cancellationToken) { if (authorization == null) @@ -867,7 +866,7 @@ namespace OpenIddict.NHibernate authorization.Type = type; - return Task.CompletedTask; + return default; } /// @@ -875,10 +874,8 @@ namespace OpenIddict.NHibernate /// /// The authorization to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) { if (authorization == null) { diff --git a/src/OpenIddict.NHibernate/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.NHibernate/Stores/OpenIddictScopeStore.cs index 0562f8ff..9b740cd6 100644 --- a/src/OpenIddict.NHibernate/Stores/OpenIddictScopeStore.cs +++ b/src/OpenIddict.NHibernate/Stores/OpenIddictScopeStore.cs @@ -5,9 +5,11 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -17,6 +19,7 @@ using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NHibernate; +using NHibernate.Cfg; using NHibernate.Linq; using OpenIddict.Abstractions; using OpenIddict.NHibernate.Models; @@ -92,10 +95,10 @@ namespace OpenIddict.NHibernate /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes in the database. /// - public virtual async Task CountAsync(CancellationToken cancellationToken) + public virtual async ValueTask CountAsync(CancellationToken cancellationToken) { var session = await Context.GetSessionAsync(cancellationToken); return await session.Query().LongCountAsync(cancellationToken); @@ -108,10 +111,10 @@ namespace OpenIddict.NHibernate /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of scopes that match the specified query. /// - public virtual async Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + public virtual async ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) { @@ -127,10 +130,8 @@ namespace OpenIddict.NHibernate /// /// The scope to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { @@ -147,10 +148,8 @@ namespace OpenIddict.NHibernate /// /// The scope to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { @@ -180,10 +179,10 @@ namespace OpenIddict.NHibernate /// The unique identifier associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the identifier. /// - public virtual async Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual async ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -200,10 +199,10 @@ namespace OpenIddict.NHibernate /// The name associated with the scope. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the scope corresponding to the specified name. /// - public virtual async Task FindByNameAsync([NotNull] string name, CancellationToken cancellationToken) + public virtual async ValueTask FindByNameAsync([NotNull] string name, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(name)) { @@ -222,24 +221,27 @@ namespace OpenIddict.NHibernate /// /// The names associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes corresponding to the specified names. - /// - public virtual async Task> FindByNamesAsync( - ImmutableArray names, CancellationToken cancellationToken) + /// The scopes corresponding to the specified names. + public virtual IAsyncEnumerable FindByNamesAsync(ImmutableArray names, CancellationToken cancellationToken) { if (names.Any(name => string.IsNullOrEmpty(name))) { throw new ArgumentException("Scope names cannot be null or empty.", nameof(names)); } - var session = await Context.GetSessionAsync(cancellationToken); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); - return ImmutableArray.CreateRange( - await (from scope in session.Query() - where names.Contains(scope.Name) - select scope).ToListAsync(cancellationToken)); + await foreach (var scope in (from scope in session.Query() + where names.Contains(scope.Name) + select scope).AsAsyncEnumerable(cancellationToken)) + { + yield return scope; + } + } } /// @@ -247,41 +249,33 @@ namespace OpenIddict.NHibernate /// /// The resource associated with the scopes. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the scopes associated with the specified resource. - /// - public virtual async Task> FindByResourceAsync( - [NotNull] string resource, CancellationToken cancellationToken) + /// The scopes associated with the specified resource. + public virtual IAsyncEnumerable FindByResourceAsync([NotNull] string resource, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(resource)) { throw new ArgumentException("The resource cannot be null or empty.", nameof(resource)); } - var session = await Context.GetSessionAsync(cancellationToken); - - // To optimize the efficiency of the query a bit, only scopes whose stringified - // Resources column contains the specified resource are returned. Once the scopes - // are retrieved, a second pass is made to ensure only valid elements are returned. - // Implementers that use this method in a hot path may want to override this method - // to use SQL Server 2016 functions like JSON_VALUE to make the query more efficient. - var scopes = await (from scope in session.Query() - where scope.Resources.Contains(resource) - select scope).ToListAsync(cancellationToken); - - var builder = ImmutableArray.CreateBuilder(); + return ExecuteAsync(cancellationToken); - foreach (var scope in scopes) + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) { - var resources = await GetResourcesAsync(scope, cancellationToken); - if (resources.Contains(resource, StringComparer.OrdinalIgnoreCase)) + var session = await Context.GetSessionAsync(cancellationToken); + + // To optimize the efficiency of the query a bit, only scopes whose stringified + // Resources column contains the specified resource are returned. Once the scopes + // are retrieved, a second pass is made to ensure only valid elements are returned. + // Implementers that use this method in a hot path may want to override this method + // to use SQL Server 2016 functions like JSON_VALUE to make the query more efficient. + await foreach (var scope in session.Query() + .Where(scope => scope.Resources.Contains(resource)) + .AsAsyncEnumerable(cancellationToken) + .WhereAwait(async scope => (await GetResourcesAsync(scope, cancellationToken)).Contains(resource, StringComparer.Ordinal))) { - builder.Add(scope); + yield return scope; } } - - return builder.ToImmutable(); } /// @@ -293,10 +287,10 @@ namespace OpenIddict.NHibernate /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual async Task GetAsync( + public virtual async ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -478,12 +472,9 @@ namespace OpenIddict.NHibernate /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( - [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) + /// All the elements returned when executing the specified query. + public virtual async IAsyncEnumerable ListAsync( + [CanBeNull] int? count, [CanBeNull] int? offset, [EnumeratorCancellation] CancellationToken cancellationToken) { var session = await Context.GetSessionAsync(cancellationToken); var query = session.Query() @@ -500,7 +491,10 @@ namespace OpenIddict.NHibernate query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + await foreach (var scope in query.AsAsyncEnumerable(cancellationToken)) + { + yield return scope; + } } /// @@ -511,11 +505,8 @@ namespace OpenIddict.NHibernate /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -524,8 +515,17 @@ namespace OpenIddict.NHibernate throw new ArgumentNullException(nameof(query)); } - var session = await Context.GetSessionAsync(cancellationToken); - return ImmutableArray.CreateRange(await query(session.Query(), state).ToListAsync(cancellationToken)); + return ExecuteAsync(cancellationToken); + + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + + await foreach (var element in query(session.Query(), state).AsAsyncEnumerable(cancellationToken)) + { + yield return element; + } + } } /// @@ -534,10 +534,8 @@ namespace OpenIddict.NHibernate /// The scope. /// The description associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken) { if (scope == null) { @@ -546,7 +544,7 @@ namespace OpenIddict.NHibernate scope.Description = description; - return Task.CompletedTask; + return default; } /// @@ -555,10 +553,8 @@ namespace OpenIddict.NHibernate /// The scope. /// The display name associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetDisplayNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetDisplayNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) { if (scope == null) { @@ -567,7 +563,7 @@ namespace OpenIddict.NHibernate scope.DisplayName = name; - return Task.CompletedTask; + return default; } /// @@ -576,10 +572,8 @@ namespace OpenIddict.NHibernate /// The scope. /// The name associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken) { if (scope == null) { @@ -588,7 +582,7 @@ namespace OpenIddict.NHibernate scope.Name = name; - return Task.CompletedTask; + return default; } /// @@ -597,10 +591,8 @@ namespace OpenIddict.NHibernate /// The scope. /// The additional properties associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (scope == null) { @@ -611,12 +603,12 @@ namespace OpenIddict.NHibernate { scope.Properties = null; - return Task.CompletedTask; + return default; } scope.Properties = properties.ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -625,10 +617,8 @@ namespace OpenIddict.NHibernate /// The scope. /// The resources associated with the scope. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetResourcesAsync([NotNull] TScope scope, ImmutableArray resources, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetResourcesAsync([NotNull] TScope scope, ImmutableArray resources, CancellationToken cancellationToken) { if (scope == null) { @@ -639,12 +629,12 @@ namespace OpenIddict.NHibernate { scope.Resources = null; - return Task.CompletedTask; + return default; } scope.Resources = new JArray(resources.ToArray()).ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -652,10 +642,8 @@ namespace OpenIddict.NHibernate /// /// The scope to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken) { if (scope == null) { diff --git a/src/OpenIddict.NHibernate/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.NHibernate/Stores/OpenIddictTokenStore.cs index 7cf481e8..610e822d 100644 --- a/src/OpenIddict.NHibernate/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.NHibernate/Stores/OpenIddictTokenStore.cs @@ -5,9 +5,11 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -17,6 +19,7 @@ using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NHibernate; +using NHibernate.Cfg; using NHibernate.Linq; using OpenIddict.Abstractions; using OpenIddict.NHibernate.Models; @@ -100,10 +103,10 @@ namespace OpenIddict.NHibernate /// /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of applications in the database. /// - public virtual async Task CountAsync(CancellationToken cancellationToken) + public virtual async ValueTask CountAsync(CancellationToken cancellationToken) { var session = await Context.GetSessionAsync(cancellationToken); return await session.Query().LongCountAsync(cancellationToken); @@ -116,10 +119,10 @@ namespace OpenIddict.NHibernate /// The query to execute. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the number of tokens that match the specified query. /// - public virtual async Task CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + public virtual async ValueTask CountAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) { if (query == null) { @@ -135,10 +138,8 @@ namespace OpenIddict.NHibernate /// /// The token to create. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { @@ -155,10 +156,8 @@ namespace OpenIddict.NHibernate /// /// The token to delete. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { @@ -189,11 +188,8 @@ namespace OpenIddict.NHibernate /// The subject associated with the token. /// The client associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the subject/client. - /// - public virtual async Task> FindAsync([NotNull] string subject, + /// The tokens corresponding to the subject/client. + public virtual IAsyncEnumerable FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) @@ -206,18 +202,24 @@ namespace OpenIddict.NHibernate throw new ArgumentException("The client cannot be null or empty.", nameof(client)); } - var session = await Context.GetSessionAsync(cancellationToken); - - var key = ConvertIdentifierFromString(client); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange( - await (from token in session.Query() - .Fetch(token => token.Application) - .Fetch(token => token.Authorization) - where token.Application != null && - token.Application.Id.Equals(key) && - token.Subject == subject - select token).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + var key = ConvertIdentifierFromString(client); + + await foreach (var token in (from token in session.Query() + .Fetch(token => token.Application) + .Fetch(token => token.Authorization) + where token.Application != null && + token.Application.Id.Equals(key) && + token.Subject == subject + select token).AsAsyncEnumerable(cancellationToken)) + { + yield return token; + } + } } /// @@ -227,11 +229,8 @@ namespace OpenIddict.NHibernate /// The client associated with the token. /// The token status. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The tokens corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, CancellationToken cancellationToken) { @@ -250,19 +249,25 @@ namespace OpenIddict.NHibernate throw new ArgumentException("The status cannot be null or empty.", nameof(status)); } - var session = await Context.GetSessionAsync(cancellationToken); + return ExecuteAsync(cancellationToken); - var key = ConvertIdentifierFromString(client); - - return ImmutableArray.CreateRange( - await (from token in session.Query() - .Fetch(token => token.Application) - .Fetch(token => token.Authorization) - where token.Application != null && - token.Application.Id.Equals(key) && - token.Subject == subject && - token.Status == status - select token).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + var key = ConvertIdentifierFromString(client); + + await foreach (var token in (from token in session.Query() + .Fetch(token => token.Application) + .Fetch(token => token.Authorization) + where token.Application != null && + token.Application.Id.Equals(key) && + token.Subject == subject && + token.Status == status + select token).AsAsyncEnumerable(cancellationToken)) + { + yield return token; + } + } } /// @@ -273,11 +278,8 @@ namespace OpenIddict.NHibernate /// The token status. /// The token type. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the criteria. - /// - public virtual async Task> FindAsync( + /// The tokens corresponding to the criteria. + public virtual IAsyncEnumerable FindAsync( [NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken) { @@ -301,20 +303,26 @@ namespace OpenIddict.NHibernate throw new ArgumentException("The type cannot be null or empty.", nameof(type)); } - var session = await Context.GetSessionAsync(cancellationToken); + return ExecuteAsync(cancellationToken); - var key = ConvertIdentifierFromString(client); - - return ImmutableArray.CreateRange( - await (from token in session.Query() - .Fetch(token => token.Application) - .Fetch(token => token.Authorization) - where token.Application != null && - token.Application.Id.Equals(key) && - token.Subject == subject && - token.Status == status && - token.Type == type - select token).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + var key = ConvertIdentifierFromString(client); + + await foreach (var token in (from token in session.Query() + .Fetch(token => token.Application) + .Fetch(token => token.Authorization) + where token.Application != null && + token.Application.Id.Equals(key) && + token.Subject == subject && + token.Status == status && + token.Type == type + select token).AsAsyncEnumerable(cancellationToken)) + { + yield return token; + } + } } /// @@ -322,28 +330,31 @@ namespace OpenIddict.NHibernate /// /// The application identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified application. - /// - public virtual async Task> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + /// The tokens corresponding to the specified application. + public virtual IAsyncEnumerable FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - var session = await Context.GetSessionAsync(cancellationToken); + return ExecuteAsync(cancellationToken); - var key = ConvertIdentifierFromString(identifier); - - return ImmutableArray.CreateRange( - await (from token in session.Query() - .Fetch(token => token.Application) - .Fetch(token => token.Authorization) - where token.Application != null && - token.Application.Id.Equals(key) - select token).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + var key = ConvertIdentifierFromString(identifier); + + await foreach (var token in (from token in session.Query() + .Fetch(token => token.Application) + .Fetch(token => token.Authorization) + where token.Application != null && + token.Application.Id.Equals(key) + select token).AsAsyncEnumerable(cancellationToken)) + { + yield return token; + } + } } /// @@ -351,28 +362,31 @@ namespace OpenIddict.NHibernate /// /// The authorization identifier associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified authorization. - /// - public virtual async Task> FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + /// The tokens corresponding to the specified authorization. + public virtual IAsyncEnumerable FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - var session = await Context.GetSessionAsync(cancellationToken); - - var key = ConvertIdentifierFromString(identifier); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange( - await (from token in session.Query() - .Fetch(token => token.Application) - .Fetch(token => token.Authorization) - where token.Authorization != null && - token.Authorization.Id.Equals(key) - select token).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + var key = ConvertIdentifierFromString(identifier); + + await foreach (var token in (from token in session.Query() + .Fetch(token => token.Application) + .Fetch(token => token.Authorization) + where token.Authorization != null && + token.Authorization.Id.Equals(key) + select token).AsAsyncEnumerable(cancellationToken)) + { + yield return token; + } + } } /// @@ -381,10 +395,10 @@ namespace OpenIddict.NHibernate /// The unique identifier associated with the token. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the token corresponding to the unique identifier. /// - public virtual async Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual async ValueTask FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -402,10 +416,10 @@ namespace OpenIddict.NHibernate /// The reference identifier associated with the tokens. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the tokens corresponding to the specified reference identifier. /// - public virtual async Task FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual async ValueTask FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { @@ -426,25 +440,29 @@ namespace OpenIddict.NHibernate /// /// The subject associated with the tokens. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns the tokens corresponding to the specified subject. - /// - public virtual async Task> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken) + /// The tokens corresponding to the specified subject. + public virtual IAsyncEnumerable FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(subject)) { throw new ArgumentException("The subject cannot be null or empty.", nameof(subject)); } - var session = await Context.GetSessionAsync(cancellationToken); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange( - await (from token in session.Query() - .Fetch(token => token.Application) - .Fetch(token => token.Authorization) - where token.Subject == subject - select token).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + + await foreach (var token in (from token in session.Query() + .Fetch(token => token.Application) + .Fetch(token => token.Authorization) + where token.Subject == subject + select token).AsAsyncEnumerable(cancellationToken)) + { + yield return token; + } + } } /// @@ -480,10 +498,10 @@ namespace OpenIddict.NHibernate /// The optional state. /// The that can be used to abort the operation. /// - /// A that can be used to monitor the asynchronous operation, + /// A that can be used to monitor the asynchronous operation, /// whose result returns the first element returned when executing the query. /// - public virtual async Task GetAsync( + public virtual async ValueTask GetAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -733,12 +751,9 @@ namespace OpenIddict.NHibernate /// The number of results to return. /// The number of results to skip. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( - [CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken) + /// All the elements returned when executing the specified query. + public virtual async IAsyncEnumerable ListAsync( + [CanBeNull] int? count, [CanBeNull] int? offset, [EnumeratorCancellation] CancellationToken cancellationToken) { var session = await Context.GetSessionAsync(cancellationToken); var query = session.Query() @@ -757,7 +772,10 @@ namespace OpenIddict.NHibernate query = query.Take(count.Value); } - return ImmutableArray.CreateRange(await query.ToListAsync(cancellationToken)); + await foreach (var token in query.AsAsyncEnumerable(cancellationToken)) + { + yield return token; + } } /// @@ -768,11 +786,8 @@ namespace OpenIddict.NHibernate /// The query to execute. /// The optional state. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation, - /// whose result returns all the elements returned when executing the specified query. - /// - public virtual async Task> ListAsync( + /// All the elements returned when executing the specified query. + public virtual IAsyncEnumerable ListAsync( [NotNull] Func, TState, IQueryable> query, [CanBeNull] TState state, CancellationToken cancellationToken) { @@ -781,21 +796,28 @@ namespace OpenIddict.NHibernate throw new ArgumentNullException(nameof(query)); } - var session = await Context.GetSessionAsync(cancellationToken); + return ExecuteAsync(cancellationToken); - return ImmutableArray.CreateRange(await query( - session.Query().Fetch(token => token.Application) - .Fetch(token => token.Authorization), state).ToListAsync(cancellationToken)); + async IAsyncEnumerable ExecuteAsync(CancellationToken cancellationToken) + { + var session = await Context.GetSessionAsync(cancellationToken); + + await foreach (var element in query( + session.Query() + .Fetch(token => token.Application) + .Fetch(token => token.Authorization), state).AsAsyncEnumerable(cancellationToken)) + { + yield return element; + } + } } /// /// Removes the tokens that are marked as expired or invalid. /// /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task PruneAsync(CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask PruneAsync(CancellationToken cancellationToken) { var session = await Context.GetSessionAsync(cancellationToken); @@ -813,10 +835,8 @@ namespace OpenIddict.NHibernate /// The token. /// The unique identifier associated with the client application. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task SetApplicationIdAsync([NotNull] TToken token, + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (token == null) @@ -843,10 +863,8 @@ namespace OpenIddict.NHibernate /// The token. /// The unique identifier associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task SetAuthorizationIdAsync([NotNull] TToken token, + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (token == null) @@ -873,10 +891,8 @@ namespace OpenIddict.NHibernate /// The token. /// The creation date. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetCreationDateAsync([NotNull] TToken token, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetCreationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken) { if (token == null) @@ -886,7 +902,7 @@ namespace OpenIddict.NHibernate token.CreationDate = date; - return Task.CompletedTask; + return default; } /// @@ -895,10 +911,8 @@ namespace OpenIddict.NHibernate /// The token. /// The expiration date. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetExpirationDateAsync([NotNull] TToken token, + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetExpirationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken) { if (token == null) @@ -908,7 +922,7 @@ namespace OpenIddict.NHibernate token.ExpirationDate = date; - return Task.CompletedTask; + return default; } /// @@ -917,10 +931,8 @@ namespace OpenIddict.NHibernate /// The token. /// The payload associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken) { if (token == null) { @@ -929,7 +941,7 @@ namespace OpenIddict.NHibernate token.Payload = payload; - return Task.CompletedTask; + return default; } /// @@ -938,10 +950,8 @@ namespace OpenIddict.NHibernate /// The token. /// The additional properties associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken) { if (token == null) { @@ -952,12 +962,12 @@ namespace OpenIddict.NHibernate { token.Properties = null; - return Task.CompletedTask; + return default; } token.Properties = properties.ToString(Formatting.None); - return Task.CompletedTask; + return default; } /// @@ -968,10 +978,8 @@ namespace OpenIddict.NHibernate /// The token. /// The reference identifier associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) { if (token == null) { @@ -980,7 +988,7 @@ namespace OpenIddict.NHibernate token.ReferenceId = identifier; - return Task.CompletedTask; + return default; } /// @@ -989,10 +997,8 @@ namespace OpenIddict.NHibernate /// The token. /// The status associated with the authorization. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken) { if (token == null) { @@ -1001,7 +1007,7 @@ namespace OpenIddict.NHibernate token.Status = status; - return Task.CompletedTask; + return default; } /// @@ -1010,10 +1016,8 @@ namespace OpenIddict.NHibernate /// The token. /// The subject associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken) { if (token == null) { @@ -1022,7 +1026,7 @@ namespace OpenIddict.NHibernate token.Subject = subject; - return Task.CompletedTask; + return default; } /// @@ -1031,10 +1035,8 @@ namespace OpenIddict.NHibernate /// The token. /// The token type associated with the token. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual Task SetTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual ValueTask SetTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken) { if (token == null) { @@ -1043,7 +1045,7 @@ namespace OpenIddict.NHibernate token.Type = type; - return Task.CompletedTask; + return default; } /// @@ -1051,10 +1053,8 @@ namespace OpenIddict.NHibernate /// /// The token to update. /// The that can be used to abort the operation. - /// - /// A that can be used to monitor the asynchronous operation. - /// - public virtual async Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) + /// A that can be used to monitor the asynchronous operation. + public virtual async ValueTask UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs index d0e15e43..de1d067c 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs @@ -1017,7 +1017,7 @@ namespace OpenIddict.Server var scopes = context.Request.GetScopes().Except(context.Options.Scopes); if (scopes.Count != 0) { - foreach (var scope in await _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) + await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) { scopes = scopes.Remove(await _scopeManager.GetNameAsync(scope)); } diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs index 100dfca4..10394f8a 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs @@ -713,7 +713,7 @@ namespace OpenIddict.Server var scopes = context.Request.GetScopes().Except(context.Options.Scopes); if (scopes.Count != 0) { - foreach (var scope in await _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) + await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) { scopes = scopes.Remove(await _scopeManager.GetNameAsync(scope)); } diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs index 8c579ee6..96408ce0 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs @@ -446,30 +446,6 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - async Task ValidatePostLogoutRedirectUriAsync(string address) - { - var applications = await _applicationManager.FindByPostLogoutRedirectUriAsync(address); - if (applications.IsDefaultOrEmpty) - { - return false; - } - - if (context.Options.IgnoreEndpointPermissions) - { - return true; - } - - foreach (var application in applications) - { - if (await _applicationManager.HasPermissionAsync(application, Permissions.Endpoints.Logout)) - { - return true; - } - } - - return false; - } - if (!await ValidatePostLogoutRedirectUriAsync(context.PostLogoutRedirectUri)) { context.Logger.LogError("The logout request was rejected because the specified post_logout_redirect_uri " + @@ -481,6 +457,33 @@ namespace OpenIddict.Server return; } + + async Task ValidatePostLogoutRedirectUriAsync(string address) + { + // To be considered valid, a post_logout_redirect_uri must correspond to an existing client application + // that was granted the ept:logout permission, unless endpoint permissions checking was explicitly disabled. + + await using var enumerator = _applicationManager.FindByPostLogoutRedirectUriAsync(address).GetAsyncEnumerator(); + if (await enumerator.MoveNextAsync()) + { + if (context.Options.IgnoreEndpointPermissions) + { + return true; + } + + do + { + if (await _applicationManager.HasPermissionAsync(enumerator.Current, Permissions.Endpoints.Logout)) + { + return true; + } + } + + while (await enumerator.MoveNextAsync()); + } + + return false; + } } }