Browse Source

Fix inconsistencies between the Entity Framework 6 and Entity Framework Core stores

pull/2527/head
Kévin Chalet 3 weeks ago
parent
commit
e5266daf9e
  1. 6
      src/OpenIddict.Abstractions/Managers/IOpenIddictApplicationManager.cs
  2. 6
      src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs
  3. 8
      src/OpenIddict.Abstractions/Managers/IOpenIddictResourceManager.cs
  4. 6
      src/OpenIddict.Abstractions/Managers/IOpenIddictScopeManager.cs
  5. 8
      src/OpenIddict.Abstractions/Managers/IOpenIddictSessionManager.cs
  6. 6
      src/OpenIddict.Abstractions/Managers/IOpenIddictTokenManager.cs
  7. 1
      src/OpenIddict.Core/Managers/OpenIddictResourceManager.cs
  8. 49
      src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkHelpers.cs
  9. 67
      src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkApplicationStore.cs
  10. 109
      src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs
  11. 27
      src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkResourceStore.cs
  12. 41
      src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkScopeStore.cs
  13. 84
      src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkSessionStore.cs
  14. 146
      src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkTokenStore.cs
  15. 6
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreApplicationStore.cs
  16. 61
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs
  17. 2
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreResourceStore.cs
  18. 2
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreScopeStore.cs
  19. 32
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreSessionStore.cs
  20. 91
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreTokenStore.cs
  21. 2
      src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbApplicationStore.cs
  22. 2
      src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbAuthorizationStore.cs
  23. 2
      src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbResourceStore.cs
  24. 2
      src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbScopeStore.cs
  25. 2
      src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbSessionStore.cs
  26. 2
      src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbTokenStore.cs
  27. 10
      src/OpenIddict.Quartz/OpenIddictQuartzJob.cs

6
src/OpenIddict.Abstractions/Managers/IOpenIddictApplicationManager.cs

@ -19,10 +19,8 @@ namespace OpenIddict.Abstractions;
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Note: this interface is not meant to be implemented by custom managers, /// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictApplicationManager class. /// that should inherit from the generic class. It is primarily intended to
/// It is primarily intended to be used by services that cannot easily depend /// be used by services that cannot easily depend on the generic manager.
/// on the generic application manager. The actual application entity type
/// is automatically determined at runtime based on the OpenIddict core options.
/// </remarks> /// </remarks>
public interface IOpenIddictApplicationManager public interface IOpenIddictApplicationManager
{ {

6
src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs

@ -15,10 +15,8 @@ namespace OpenIddict.Abstractions;
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Note: this interface is not meant to be implemented by custom managers, /// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictAuthorizationManager class. /// that should inherit from the generic class. It is primarily intended to
/// It is primarily intended to be used by services that cannot easily depend /// be used by services that cannot easily depend on the generic manager.
/// on the generic authorization manager. The actual authorization entity type
/// is automatically determined at runtime based on the OpenIddict core options.
/// </remarks> /// </remarks>
public interface IOpenIddictAuthorizationManager public interface IOpenIddictAuthorizationManager
{ {

8
src/OpenIddict.Abstractions/Managers/IOpenIddictResourceManager.cs

@ -12,14 +12,12 @@ using System.Text.Json;
namespace OpenIddict.Abstractions; namespace OpenIddict.Abstractions;
/// <summary> /// <summary>
/// Provides methods allowing to manage the Resources stored in the store. /// Provides methods allowing to manage the resources stored in the store.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Note: this interface is not meant to be implemented by custom managers, /// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictResourceManager class. /// that should inherit from the generic class. It is primarily intended to
/// It is primarily intended to be used by services that cannot easily /// be used by services that cannot easily depend on the generic manager.
/// depend on the generic resource manager. The actual resource entity type is
/// automatically determined at runtime based on the OpenIddict core options.
/// </remarks> /// </remarks>
public interface IOpenIddictResourceManager public interface IOpenIddictResourceManager
{ {

6
src/OpenIddict.Abstractions/Managers/IOpenIddictScopeManager.cs

@ -16,10 +16,8 @@ namespace OpenIddict.Abstractions;
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Note: this interface is not meant to be implemented by custom managers, /// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictScopeManager class. /// that should inherit from the generic class. It is primarily intended to
/// It is primarily intended to be used by services that cannot easily /// be used by services that cannot easily depend on the generic manager.
/// depend on the generic scope manager. The actual scope entity type is
/// automatically determined at runtime based on the OpenIddict core options.
/// </remarks> /// </remarks>
public interface IOpenIddictScopeManager public interface IOpenIddictScopeManager
{ {

8
src/OpenIddict.Abstractions/Managers/IOpenIddictSessionManager.cs

@ -11,14 +11,12 @@ using System.Text.Json;
namespace OpenIddict.Abstractions; namespace OpenIddict.Abstractions;
/// <summary> /// <summary>
/// Provides methods allowing to manage the Sessions stored in the store. /// Provides methods allowing to manage the sessions stored in the store.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Note: this interface is not meant to be implemented by custom managers, /// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictSessionManager class. /// that should inherit from the generic class. It is primarily intended to
/// It is primarily intended to be used by services that cannot easily /// be used by services that cannot easily depend on the generic manager.
/// depend on the generic session manager. The actual session entity type is
/// automatically determined at runtime based on the OpenIddict core options.
/// </remarks> /// </remarks>
public interface IOpenIddictSessionManager public interface IOpenIddictSessionManager
{ {

6
src/OpenIddict.Abstractions/Managers/IOpenIddictTokenManager.cs

@ -15,10 +15,8 @@ namespace OpenIddict.Abstractions;
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Note: this interface is not meant to be implemented by custom managers, /// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictTokenManager class. /// that should inherit from the generic class. It is primarily intended to
/// It is primarily intended to be used by services that cannot easily /// be used by services that cannot easily depend on the generic manager.
/// depend on the generic token manager. The actual token entity type is
/// automatically determined at runtime based on the OpenIddict core options.
/// </remarks> /// </remarks>
public interface IOpenIddictTokenManager public interface IOpenIddictTokenManager
{ {

1
src/OpenIddict.Core/Managers/OpenIddictResourceManager.cs

@ -12,7 +12,6 @@ using System.Text;
using System.Text.Json; using System.Text.Json;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using static System.Net.Mime.MediaTypeNames;
using ValidationException = OpenIddict.Abstractions.OpenIddictExceptions.ValidationException; using ValidationException = OpenIddict.Abstractions.OpenIddictExceptions.ValidationException;
namespace OpenIddict.Core; namespace OpenIddict.Core;

49
src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkHelpers.cs

@ -4,9 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Data.Entity.Infrastructure;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using OpenIddict.EntityFramework; using OpenIddict.EntityFramework;
using OpenIddict.EntityFramework.Models; using OpenIddict.EntityFramework.Models;
@ -70,51 +68,4 @@ public static class OpenIddictEntityFrameworkHelpers
return builder; return builder;
} }
/// <summary>
/// Executes the query and returns the results as a streamed async enumeration.
/// </summary>
/// <typeparam name="T">The type of the returned entities.</typeparam>
/// <param name="source">The query source.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The streamed async enumeration containing the results.</returns>
#pragma warning disable MA0156
internal static IAsyncEnumerable<T> AsAsyncEnumerable<T>(this IQueryable<T> source, CancellationToken cancellationToken)
#pragma warning restore MA0156
{
ArgumentNullException.ThrowIfNull(source);
return ExecuteAsync(source, cancellationToken);
static async IAsyncEnumerable<T> ExecuteAsync(IQueryable<T> source, [EnumeratorCancellation] CancellationToken cancellationToken)
{
using var enumerator = ((IDbAsyncEnumerable<T>) source).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{
yield return enumerator.Current;
}
}
}
/// <summary>
/// Tries to create a new <see cref="DbContextTransaction"/> with the specified <paramref name="level"/>.
/// </summary>
/// <param name="context">The Entity Framework context.</param>
/// <param name="level">The desired level of isolation.</param>
/// <returns>The <see cref="DbContextTransaction"/> if it could be created, <see langword="null"/> otherwise.</returns>
internal static DbContextTransaction? CreateTransaction(this DbContext context, IsolationLevel level)
{
ArgumentNullException.ThrowIfNull(context);
try
{
return context.Database.BeginTransaction(level);
}
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{
return null;
}
}
} }

67
src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkApplicationStore.cs

@ -121,7 +121,7 @@ public class OpenIddictEntityFrameworkApplicationStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
Task<List<TAuthorization>> ListAuthorizationsAsync() Task<List<TAuthorization>> ListAuthorizationsAsync()
=> (from authorization in context.Set<TAuthorization>().Include(authorization => authorization.Tokens) => (from authorization in context.Set<TAuthorization>().Include(static authorization => authorization.Tokens)
where authorization.Application!.Id!.Equals(application.Id) where authorization.Application!.Id!.Equals(application.Id)
select authorization).ToListAsync(cancellationToken); select authorization).ToListAsync(cancellationToken);
@ -134,7 +134,7 @@ public class OpenIddictEntityFrameworkApplicationStore<
// To prevent an SQL exception from being thrown if a new associated entity is // 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 // created after the existing entries have been listed, the following logic is
// executed in a serializable transaction, that will lock the affected tables. // executed in a serializable transaction, that will lock the affected tables.
using var transaction = context.CreateTransaction(IsolationLevel.Serializable); using var transaction = CreateTransaction(context, IsolationLevel.Serializable);
// Remove all the authorizations associated with the application and // Remove all the authorizations associated with the application and
// the tokens attached to these implicit or explicit authorizations. // the tokens attached to these implicit or explicit authorizations.
@ -237,16 +237,18 @@ public class OpenIddictEntityFrameworkApplicationStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var applications = (from application in context.Set<TApplication>() var applications = from application in context.Set<TApplication>()
where application.PostLogoutRedirectUris!.Contains(uri) where application.PostLogoutRedirectUris!.Contains(uri)
select application).AsAsyncEnumerable(cancellationToken); select application;
await foreach (var application in applications.WithCancellation(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TApplication>) applications).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
var uris = await GetPostLogoutRedirectUrisAsync(application, cancellationToken); var uris = await GetPostLogoutRedirectUrisAsync(enumerator.Current, cancellationToken);
if (uris.Contains(uri, StringComparer.Ordinal)) if (uris.Contains(uri, StringComparer.Ordinal))
{ {
yield return application; yield return enumerator.Current;
} }
} }
} }
@ -270,16 +272,18 @@ public class OpenIddictEntityFrameworkApplicationStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var applications = (from application in context.Set<TApplication>() var applications = from application in context.Set<TApplication>()
where application.RedirectUris!.Contains(uri) where application.RedirectUris!.Contains(uri)
select application).AsAsyncEnumerable(cancellationToken); select application;
using var enumerator = ((IDbAsyncEnumerable<TApplication>) applications).GetAsyncEnumerator();
await foreach (var application in applications.WithCancellation(cancellationToken)) while (await enumerator.MoveNextAsync(cancellationToken))
{ {
var uris = await GetRedirectUrisAsync(application, cancellationToken); var uris = await GetRedirectUrisAsync(enumerator.Current, cancellationToken);
if (uris.Contains(uri, StringComparer.Ordinal)) if (uris.Contains(uri, StringComparer.Ordinal))
{ {
yield return application; yield return enumerator.Current;
} }
} }
} }
@ -658,7 +662,7 @@ public class OpenIddictEntityFrameworkApplicationStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TApplication> query = context.Set<TApplication>().OrderBy(application => application.Id!); IQueryable<TApplication> query = context.Set<TApplication>().OrderBy(static application => application.Id!);
if (offset is not null) if (offset is not null)
{ {
@ -670,9 +674,11 @@ public class OpenIddictEntityFrameworkApplicationStore<
query = query.Take(count.Value); query = query.Take(count.Value);
} }
await foreach (var application in query.AsAsyncEnumerable(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TApplication>) query).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return application; yield return enumerator.Current;
} }
} }
@ -689,9 +695,11 @@ public class OpenIddictEntityFrameworkApplicationStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var application in query(context.Set<TApplication>(), state).AsAsyncEnumerable(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TResult>) query(context.Set<TApplication>(), state)).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return application; yield return enumerator.Current;
} }
} }
} }
@ -1097,4 +1105,25 @@ public class OpenIddictEntityFrameworkApplicationStore<
return converter.ConvertToInvariantString(identifier); return converter.ConvertToInvariantString(identifier);
} }
/// <summary>
/// Tries to create a new <see cref="DbContextTransaction"/> with the specified <paramref name="level"/>.
/// </summary>
/// <param name="context">The Entity Framework context.</param>
/// <param name="level">The desired level of isolation.</param>
/// <returns>The <see cref="DbContextTransaction"/> if it could be created, <see langword="null"/> otherwise.</returns>
protected virtual DbContextTransaction? CreateTransaction(DbContext context, IsolationLevel level)
{
ArgumentNullException.ThrowIfNull(context);
try
{
return context.Database.BeginTransaction(level);
}
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{
return null;
}
}
} }

109
src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs

@ -126,7 +126,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
// To prevent an SQL exception from being thrown if a new associated entity is // 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 // created after the existing entries have been listed, the following logic is
// executed in a serializable transaction, that will lock the affected tables. // executed in a serializable transaction, that will lock the affected tables.
using var transaction = context.CreateTransaction(IsolationLevel.Serializable); using var transaction = CreateTransaction(context, IsolationLevel.Serializable);
// Remove all the tokens associated with the authorization. // Remove all the tokens associated with the authorization.
var tokens = await ListTokensAsync(); var tokens = await ListTokensAsync();
@ -165,7 +165,8 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TAuthorization> authorizations = context.Set<TAuthorization>().Include(authorization => authorization.Application); IQueryable<TAuthorization> authorizations = context.Set<TAuthorization>()
.Include(static authorization => authorization.Application);
if (!string.IsNullOrEmpty(query.Subject)) if (!string.IsNullOrEmpty(query.Subject))
{ {
@ -188,18 +189,20 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
authorizations = authorizations.Where(authorization => authorization.Type == query.Type); authorizations = authorizations.Where(authorization => authorization.Type == query.Type);
} }
using var enumerator = ((IDbAsyncEnumerable<TAuthorization>) authorizations).GetAsyncEnumerator();
// Note: Entity Framework Core cannot translate the logic used to filter authorizations by scopes in a // Note: Entity Framework Core cannot translate the logic used to filter authorizations by scopes in a
// SQL query so the filtering is done manually after the results have been retrieved from the database. // SQL query so the filtering is done manually after the results have been retrieved from the database.
await foreach (var authorization in authorizations.AsAsyncEnumerable(cancellationToken)) while (await enumerator.MoveNextAsync(cancellationToken))
{ {
if (query.RequiredScopes is { IsDefaultOrEmpty: false } scopes && !(await GetScopesAsync(authorization, cancellationToken)) if (query.RequiredScopes is { IsDefaultOrEmpty: false } scopes && !(await GetScopesAsync(enumerator.Current, cancellationToken))
.ToHashSet(StringComparer.Ordinal) .ToHashSet(StringComparer.Ordinal)
.IsSupersetOf(scopes)) .IsSupersetOf(scopes))
{ {
continue; continue;
} }
yield return authorization; yield return enumerator.Current;
} }
} }
@ -215,12 +218,16 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var key = ConvertIdentifierFromString(identifier); var key = ConvertIdentifierFromString(identifier);
await foreach (var authorization in var authorizations = from authorization in context.Set<TAuthorization>()
(from authorization in context.Set<TAuthorization>().Include(authorization => authorization.Application) .Include(static authorization => authorization.Application)
where authorization.Application!.Id!.Equals(key) where authorization.Application!.Id!.Equals(key)
select authorization).AsAsyncEnumerable(cancellationToken)) select authorization;
using var enumerator = ((IDbAsyncEnumerable<TAuthorization>) authorizations).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return authorization; yield return enumerator.Current;
} }
} }
} }
@ -247,12 +254,16 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var authorization in var authorizations = from authorization in context.Set<TAuthorization>()
(from authorization in context.Set<TAuthorization>().Include(authorization => authorization.Application) .Include(static authorization => authorization.Application)
where authorization.Subject == subject where authorization.Subject == subject
select authorization).AsAsyncEnumerable(cancellationToken)) select authorization;
using var enumerator = ((IDbAsyncEnumerable<TAuthorization>) authorizations).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return authorization; yield return enumerator.Current;
} }
} }
} }
@ -294,7 +305,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
return await query( return await query(
context.Set<TAuthorization>().Include(authorization => authorization.Application), state).FirstOrDefaultAsync(cancellationToken); context.Set<TAuthorization>().Include(static authorization => authorization.Application), state).FirstOrDefaultAsync(cancellationToken);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -429,8 +440,8 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TAuthorization> query = context.Set<TAuthorization>() IQueryable<TAuthorization> query = context.Set<TAuthorization>()
.Include(authorization => authorization.Application) .Include(static authorization => authorization.Application)
.OrderBy(authorization => authorization.Id!); .OrderBy(static authorization => authorization.Id!);
if (offset is not null) if (offset is not null)
{ {
@ -442,9 +453,11 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
query = query.Take(count.Value); query = query.Take(count.Value);
} }
await foreach (var authorization in query.AsAsyncEnumerable(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TAuthorization>) query).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return authorization; yield return enumerator.Current;
} }
} }
@ -461,11 +474,13 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var authorization in query(context using var enumerator = ((IDbAsyncEnumerable<TResult>) query(context
.Set<TAuthorization>() .Set<TAuthorization>()
.Include(authorization => authorization.Application), state).AsAsyncEnumerable(cancellationToken)) .Include(static authorization => authorization.Application), state)).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return authorization; yield return enumerator.Current;
} }
} }
} }
@ -493,7 +508,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
// after it was retrieved from the database, the following logic is executed in // 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 // a repeatable read transaction, that will put a lock on the retrieved entries
// and thus prevent them from being concurrently modified outside this block. // and thus prevent them from being concurrently modified outside this block.
using var transaction = context.CreateTransaction(IsolationLevel.RepeatableRead); using var transaction = CreateTransaction(context, IsolationLevel.RepeatableRead);
// Note: the Oracle MySQL provider doesn't support DateTimeOffset and is unable // Note: the Oracle MySQL provider doesn't support DateTimeOffset and is unable
// to create a SQL query with an expression calling DateTimeOffset.UtcDateTime. // to create a SQL query with an expression calling DateTimeOffset.UtcDateTime.
@ -502,7 +517,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
var date = threshold.UtcDateTime; var date = threshold.UtcDateTime;
var authorizations = var authorizations =
await (from authorization in context.Set<TAuthorization>().Include(authorization => authorization.Tokens) await (from authorization in context.Set<TAuthorization>().Include(static authorization => authorization.Tokens)
where authorization.CreationDate < date where authorization.CreationDate < date
where authorization.Status != Statuses.Valid || authorization.Type == AuthorizationTypes.AdHoc where authorization.Status != Statuses.Valid || authorization.Type == AuthorizationTypes.AdHoc
where !authorization.Tokens.Any() where !authorization.Tokens.Any()
@ -528,7 +543,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception)) catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{ {
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -537,7 +552,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
result += authorizations.Count; result += authorizations.Count;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0243), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0243), exceptions);
} }
@ -550,7 +565,8 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TAuthorization> query = context.Set<TAuthorization>().Include(authorization => authorization.Application); IQueryable<TAuthorization> query = context.Set<TAuthorization>()
.Include(static authorization => authorization.Application);
if (!string.IsNullOrEmpty(subject)) if (!string.IsNullOrEmpty(subject))
{ {
@ -592,7 +608,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(authorization).State = EntityState.Unchanged; context.Entry(authorization).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -601,7 +617,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -622,7 +638,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
var result = 0L; var result = 0L;
foreach (var authorization in await (from authorization in context.Set<TAuthorization>() foreach (var authorization in await (from authorization in context.Set<TAuthorization>()
.Include(authorization => authorization.Application) .Include(static authorization => authorization.Application)
where authorization.Application!.Id!.Equals(key) where authorization.Application!.Id!.Equals(key)
select authorization).ToListAsync(cancellationToken)) select authorization).ToListAsync(cancellationToken))
{ {
@ -638,7 +654,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(authorization).State = EntityState.Unchanged; context.Entry(authorization).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -647,7 +663,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -667,7 +683,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
var result = 0L; var result = 0L;
foreach (var authorization in await (from authorization in context.Set<TAuthorization>() foreach (var authorization in await (from authorization in context.Set<TAuthorization>()
.Include(authorization => authorization.Application) .Include(static authorization => authorization.Application)
where authorization.Subject == subject where authorization.Subject == subject
select authorization).ToListAsync(cancellationToken)) select authorization).ToListAsync(cancellationToken))
{ {
@ -683,7 +699,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(authorization).State = EntityState.Unchanged; context.Entry(authorization).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -692,7 +708,7 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -931,4 +947,25 @@ public class OpenIddictEntityFrameworkAuthorizationStore<
return converter.ConvertToInvariantString(identifier); return converter.ConvertToInvariantString(identifier);
} }
/// <summary>
/// Tries to create a new <see cref="DbContextTransaction"/> with the specified <paramref name="level"/>.
/// </summary>
/// <param name="context">The Entity Framework context.</param>
/// <param name="level">The desired level of isolation.</param>
/// <returns>The <see cref="DbContextTransaction"/> if it could be created, <see langword="null"/> otherwise.</returns>
protected virtual DbContextTransaction? CreateTransaction(DbContext context, IsolationLevel level)
{
ArgumentNullException.ThrowIfNull(context);
try
{
return context.Database.BeginTransaction(level);
}
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{
return null;
}
}
} }

27
src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkResourceStore.cs

@ -173,12 +173,15 @@ public class OpenIddictEntityFrameworkResourceStore<
// Note: Enumerable.Contains() is deliberately used without the extension method syntax to ensure // Note: Enumerable.Contains() is deliberately used without the extension method syntax to ensure
// ImmutableArray.Contains() (which is not fully supported by Entity Framework 6.x) is not used instead. // ImmutableArray.Contains() (which is not fully supported by Entity Framework 6.x) is not used instead.
await foreach (var resource in var resources = from resource in context.Set<TResource>()
(from resource in context.Set<TResource>() where Enumerable.Contains(names, resource.Name)
where Enumerable.Contains(names, resource.Name) select resource;
select resource).AsAsyncEnumerable(cancellationToken))
using var enumerator = ((IDbAsyncEnumerable<TResource>) resources).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return resource; yield return enumerator.Current;
} }
} }
} }
@ -356,7 +359,7 @@ public class OpenIddictEntityFrameworkResourceStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TResource> query = context.Set<TResource>().OrderBy(resource => resource.Id!); IQueryable<TResource> query = context.Set<TResource>().OrderBy(static resource => resource.Id!);
if (offset is not null) if (offset is not null)
{ {
@ -368,9 +371,11 @@ public class OpenIddictEntityFrameworkResourceStore<
query = query.Take(count.Value); query = query.Take(count.Value);
} }
await foreach (var resource in query.AsAsyncEnumerable(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TResource>) query).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return resource; yield return enumerator.Current;
} }
} }
@ -387,9 +392,11 @@ public class OpenIddictEntityFrameworkResourceStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var resource in query(context.Set<TResource>(), state).AsAsyncEnumerable(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TResult>) query(context.Set<TResource>(), state)).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return resource; yield return enumerator.Current;
} }
} }
} }

41
src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkScopeStore.cs

@ -173,12 +173,15 @@ public class OpenIddictEntityFrameworkScopeStore<
// Note: Enumerable.Contains() is deliberately used without the extension method syntax to ensure // Note: Enumerable.Contains() is deliberately used without the extension method syntax to ensure
// ImmutableArray.Contains() (which is not fully supported by Entity Framework 6.x) is not used instead. // ImmutableArray.Contains() (which is not fully supported by Entity Framework 6.x) is not used instead.
await foreach (var scope in var scopes = from scope in context.Set<TScope>()
(from scope in context.Set<TScope>() where Enumerable.Contains(names, scope.Name)
where Enumerable.Contains(names, scope.Name) select scope;
select scope).AsAsyncEnumerable(cancellationToken))
using var enumerator = ((IDbAsyncEnumerable<TScope>) scopes).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return scope; yield return enumerator.Current;
} }
} }
} }
@ -200,16 +203,18 @@ public class OpenIddictEntityFrameworkScopeStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var scopes = (from scope in context.Set<TScope>() var scopes = from scope in context.Set<TScope>()
where scope.Resources!.Contains(resource) where scope.Resources!.Contains(resource)
select scope).AsAsyncEnumerable(cancellationToken); select scope;
using var enumerator = ((IDbAsyncEnumerable<TScope>) scopes).GetAsyncEnumerator();
await foreach (var scope in scopes.WithCancellation(cancellationToken)) while (await enumerator.MoveNextAsync(cancellationToken))
{ {
var resources = await GetResourcesAsync(scope, cancellationToken); var resources = await GetResourcesAsync(enumerator.Current, cancellationToken);
if (resources.Contains(resource, StringComparer.Ordinal)) if (resources.Contains(resource, StringComparer.Ordinal))
{ {
yield return scope; yield return enumerator.Current;
} }
} }
} }
@ -426,7 +431,7 @@ public class OpenIddictEntityFrameworkScopeStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TScope> query = context.Set<TScope>().OrderBy(scope => scope.Id!); IQueryable<TScope> query = context.Set<TScope>().OrderBy(static scope => scope.Id!);
if (offset is not null) if (offset is not null)
{ {
@ -438,9 +443,11 @@ public class OpenIddictEntityFrameworkScopeStore<
query = query.Take(count.Value); query = query.Take(count.Value);
} }
await foreach (var scope in query.AsAsyncEnumerable(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TScope>) query).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return scope; yield return enumerator.Current;
} }
} }
@ -457,9 +464,11 @@ public class OpenIddictEntityFrameworkScopeStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var scope in query(context.Set<TScope>(), state).AsAsyncEnumerable(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TResult>) query(context.Set<TScope>(), state)).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return scope; yield return enumerator.Current;
} }
} }
} }

84
src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkSessionStore.cs

@ -145,8 +145,8 @@ public class OpenIddictEntityFrameworkSessionStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TSession> sessions = context.Set<TSession>() IQueryable<TSession> sessions = context.Set<TSession>()
.Include(session => session.Application) .Include(static session => session.Application)
.Include(session => session.Authorization); .Include(static session => session.Authorization);
if (!string.IsNullOrEmpty(query.Subject)) if (!string.IsNullOrEmpty(query.Subject))
{ {
@ -169,9 +169,11 @@ public class OpenIddictEntityFrameworkSessionStore<
sessions = sessions.Where(session => session.Status == query.Status); sessions = sessions.Where(session => session.Status == query.Status);
} }
await foreach (var session in sessions.AsAsyncEnumerable(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TSession>) sessions).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return session; yield return enumerator.Current;
} }
} }
@ -187,12 +189,17 @@ public class OpenIddictEntityFrameworkSessionStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var key = ConvertIdentifierFromString(identifier); var key = ConvertIdentifierFromString(identifier);
await foreach (var session in var sessions = from session in context.Set<TSession>()
(from session in context.Set<TSession>().Include(session => session.Application).Include(session => session.Authorization) .Include(static session => session.Application)
where session.Application!.Id!.Equals(key) .Include(static session => session.Authorization)
select session).AsAsyncEnumerable(cancellationToken)) where session.Application!.Id!.Equals(key)
select session;
using var enumerator = ((IDbAsyncEnumerable<TSession>) sessions).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return session; yield return enumerator.Current;
} }
} }
} }
@ -209,12 +216,17 @@ public class OpenIddictEntityFrameworkSessionStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var key = ConvertIdentifierFromString(identifier); var key = ConvertIdentifierFromString(identifier);
await foreach (var session in var sessions = from session in context.Set<TSession>()
(from session in context.Set<TSession>().Include(session => session.Application).Include(session => session.Authorization) .Include(static session => session.Application)
where session.Authorization!.Id!.Equals(key) .Include(static session => session.Authorization)
select session).AsAsyncEnumerable(cancellationToken)) where session.Authorization!.Id!.Equals(key)
select session;
using var enumerator = ((IDbAsyncEnumerable<TSession>) sessions).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return session; yield return enumerator.Current;
} }
} }
} }
@ -241,12 +253,17 @@ public class OpenIddictEntityFrameworkSessionStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var session in var sessions = from session in context.Set<TSession>()
(from session in context.Set<TSession>().Include(session => session.Application).Include(session => session.Authorization) .Include(static session => session.Application)
where session.LoginId == identifier .Include(static session => session.Authorization)
select session).AsAsyncEnumerable(cancellationToken)) where session.LoginId == identifier
select session;
using var enumerator = ((IDbAsyncEnumerable<TSession>) sessions).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return session; yield return enumerator.Current;
} }
} }
} }
@ -262,12 +279,17 @@ public class OpenIddictEntityFrameworkSessionStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var session in var sessions = from session in context.Set<TSession>()
(from session in context.Set<TSession>().Include(session => session.Application).Include(session => session.Authorization) .Include(static session => session.Application)
where session.Subject == subject .Include(static session => session.Authorization)
select session).AsAsyncEnumerable(cancellationToken)) where session.Subject == subject
select session;
using var enumerator = ((IDbAsyncEnumerable<TSession>) sessions).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return session; yield return enumerator.Current;
} }
} }
} }
@ -431,7 +453,7 @@ public class OpenIddictEntityFrameworkSessionStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TSession> query = context.Set<TSession>().OrderBy(session => session.Id!); IQueryable<TSession> query = context.Set<TSession>().OrderBy(static session => session.Id!);
if (offset is not null) if (offset is not null)
{ {
@ -443,9 +465,11 @@ public class OpenIddictEntityFrameworkSessionStore<
query = query.Take(count.Value); query = query.Take(count.Value);
} }
await foreach (var session in query.AsAsyncEnumerable(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TSession>) query).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return session; yield return enumerator.Current;
} }
} }
@ -462,9 +486,11 @@ public class OpenIddictEntityFrameworkSessionStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var session in query(context.Set<TSession>(), state).AsAsyncEnumerable(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TResult>) query(context.Set<TSession>(), state)).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return session; yield return enumerator.Current;
} }
} }
} }

146
src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkTokenStore.cs

@ -142,8 +142,8 @@ public class OpenIddictEntityFrameworkTokenStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TToken> tokens = context.Set<TToken>() IQueryable<TToken> tokens = context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization); .Include(static token => token.Authorization);
if (!string.IsNullOrEmpty(query.Subject)) if (!string.IsNullOrEmpty(query.Subject))
{ {
@ -166,9 +166,11 @@ public class OpenIddictEntityFrameworkTokenStore<
tokens = tokens.Where(token => token.Type == query.Type); tokens = tokens.Where(token => token.Type == query.Type);
} }
await foreach (var token in tokens.AsAsyncEnumerable(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TToken>) tokens).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return token; yield return enumerator.Current;
} }
} }
@ -184,12 +186,17 @@ public class OpenIddictEntityFrameworkTokenStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var key = ConvertIdentifierFromString(identifier); var key = ConvertIdentifierFromString(identifier);
await foreach (var token in var tokens = from token in context.Set<TToken>()
(from token in context.Set<TToken>().Include(token => token.Application).Include(token => token.Authorization) .Include(static token => token.Application)
where token.Application!.Id!.Equals(key) .Include(static token => token.Authorization)
select token).AsAsyncEnumerable(cancellationToken)) where token.Application!.Id!.Equals(key)
select token;
using var enumerator = ((IDbAsyncEnumerable<TToken>) tokens).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return token; yield return enumerator.Current;
} }
} }
} }
@ -206,12 +213,17 @@ public class OpenIddictEntityFrameworkTokenStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var key = ConvertIdentifierFromString(identifier); var key = ConvertIdentifierFromString(identifier);
await foreach (var token in var tokens = from token in context.Set<TToken>()
(from token in context.Set<TToken>().Include(token => token.Application).Include(token => token.Authorization) .Include(static token => token.Application)
where token.Authorization!.Id!.Equals(key) .Include(static token => token.Authorization)
select token).AsAsyncEnumerable(cancellationToken)) where token.Authorization!.Id!.Equals(key)
select token;
using var enumerator = ((IDbAsyncEnumerable<TToken>) tokens).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return token; yield return enumerator.Current;
} }
} }
} }
@ -242,7 +254,9 @@ public class OpenIddictEntityFrameworkTokenStore<
select entry.Entity).FirstOrDefault(); select entry.Entity).FirstOrDefault();
Task<TToken?> QueryAsync() => Task<TToken?> QueryAsync() =>
(from token in context.Set<TToken>().Include(token => token.Application).Include(token => token.Authorization) (from token in context.Set<TToken>()
.Include(static token => token.Application)
.Include(static token => token.Authorization)
where token.ReferenceId == identifier where token.ReferenceId == identifier
select token).FirstOrDefaultAsync(cancellationToken); select token).FirstOrDefaultAsync(cancellationToken);
} }
@ -258,12 +272,15 @@ public class OpenIddictEntityFrameworkTokenStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var token in var tokens = from token in context.Set<TToken>().Include(token => token.Application).Include(token => token.Authorization)
(from token in context.Set<TToken>().Include(token => token.Application).Include(token => token.Authorization) where token.Subject == subject
where token.Subject == subject select token;
select token).AsAsyncEnumerable(cancellationToken))
using var enumerator = ((IDbAsyncEnumerable<TToken>) tokens).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return token; yield return enumerator.Current;
} }
} }
} }
@ -304,10 +321,9 @@ public class OpenIddictEntityFrameworkTokenStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
return await query( return await query(context.Set<TToken>()
context.Set<TToken>() .Include(static token => token.Application)
.Include(token => token.Application) .Include(static token => token.Authorization), state).FirstOrDefaultAsync(cancellationToken);
.Include(token => token.Authorization), state).FirstOrDefaultAsync(cancellationToken);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -463,9 +479,9 @@ public class OpenIddictEntityFrameworkTokenStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TToken> query = context.Set<TToken>() IQueryable<TToken> query = context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization) .Include(static token => token.Authorization)
.OrderBy(token => token.Id!); .OrderBy(static token => token.Id!);
if (offset is not null) if (offset is not null)
{ {
@ -477,9 +493,11 @@ public class OpenIddictEntityFrameworkTokenStore<
query = query.Take(count.Value); query = query.Take(count.Value);
} }
await foreach (var token in query.AsAsyncEnumerable(cancellationToken)) using var enumerator = ((IDbAsyncEnumerable<TToken>) query).GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return token; yield return enumerator.Current;
} }
} }
@ -496,12 +514,13 @@ public class OpenIddictEntityFrameworkTokenStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var token in query( using var enumerator = ((IDbAsyncEnumerable<TResult>) query(context.Set<TToken>()
context.Set<TToken>() .Include(static token => token.Application)
.Include(token => token.Application) .Include(static token => token.Authorization), state)).GetAsyncEnumerator();
.Include(token => token.Authorization), state).AsAsyncEnumerable(cancellationToken))
while (await enumerator.MoveNextAsync(cancellationToken))
{ {
yield return token; yield return enumerator.Current;
} }
} }
} }
@ -529,7 +548,7 @@ public class OpenIddictEntityFrameworkTokenStore<
// after it was retrieved from the database, the following logic is executed in // 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 // a repeatable read transaction, that will put a lock on the retrieved entries
// and thus prevent them from being concurrently modified outside this block. // and thus prevent them from being concurrently modified outside this block.
using var transaction = context.CreateTransaction(IsolationLevel.RepeatableRead); using var transaction = CreateTransaction(context, IsolationLevel.RepeatableRead);
// Note: the Oracle MySQL provider doesn't support DateTimeOffset and is unable // Note: the Oracle MySQL provider doesn't support DateTimeOffset and is unable
// to create a SQL query with an expression calling DateTimeOffset.UtcDateTime. // to create a SQL query with an expression calling DateTimeOffset.UtcDateTime.
@ -561,7 +580,7 @@ public class OpenIddictEntityFrameworkTokenStore<
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception)) catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{ {
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -570,7 +589,7 @@ public class OpenIddictEntityFrameworkTokenStore<
result += tokens.Count; result += tokens.Count;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -583,7 +602,9 @@ public class OpenIddictEntityFrameworkTokenStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TToken> query = context.Set<TToken>().Include(token => token.Application).Include(token => token.Authorization); IQueryable<TToken> query = context.Set<TToken>()
.Include(static token => token.Application)
.Include(static token => token.Authorization);
if (!string.IsNullOrEmpty(subject)) if (!string.IsNullOrEmpty(subject))
{ {
@ -625,7 +646,7 @@ public class OpenIddictEntityFrameworkTokenStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(token).State = EntityState.Unchanged; context.Entry(token).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -634,7 +655,7 @@ public class OpenIddictEntityFrameworkTokenStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -655,8 +676,8 @@ public class OpenIddictEntityFrameworkTokenStore<
var result = 0L; var result = 0L;
foreach (var token in await (from token in context.Set<TToken>() foreach (var token in await (from token in context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization) .Include(static token => token.Authorization)
where token.Application!.Id!.Equals(key) where token.Application!.Id!.Equals(key)
where token.Status != Statuses.Revoked where token.Status != Statuses.Revoked
select token).ToListAsync(cancellationToken)) select token).ToListAsync(cancellationToken))
@ -673,7 +694,7 @@ public class OpenIddictEntityFrameworkTokenStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(token).State = EntityState.Unchanged; context.Entry(token).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -682,7 +703,7 @@ public class OpenIddictEntityFrameworkTokenStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -703,8 +724,8 @@ public class OpenIddictEntityFrameworkTokenStore<
var result = 0L; var result = 0L;
foreach (var token in await (from token in context.Set<TToken>() foreach (var token in await (from token in context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization) .Include(static token => token.Authorization)
where token.Authorization!.Id!.Equals(key) where token.Authorization!.Id!.Equals(key)
where token.Status != Statuses.Revoked where token.Status != Statuses.Revoked
select token).ToListAsync(cancellationToken)) select token).ToListAsync(cancellationToken))
@ -721,7 +742,7 @@ public class OpenIddictEntityFrameworkTokenStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(token).State = EntityState.Unchanged; context.Entry(token).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -730,7 +751,7 @@ public class OpenIddictEntityFrameworkTokenStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -750,8 +771,8 @@ public class OpenIddictEntityFrameworkTokenStore<
var result = 0L; var result = 0L;
foreach (var token in await (from token in context.Set<TToken>() foreach (var token in await (from token in context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization) .Include(static token => token.Authorization)
where token.Subject == subject where token.Subject == subject
where token.Status != Statuses.Revoked where token.Status != Statuses.Revoked
select token).ToListAsync(cancellationToken)) select token).ToListAsync(cancellationToken))
@ -768,7 +789,7 @@ public class OpenIddictEntityFrameworkTokenStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(token).State = EntityState.Unchanged; context.Entry(token).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -777,7 +798,7 @@ public class OpenIddictEntityFrameworkTokenStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -1053,4 +1074,25 @@ public class OpenIddictEntityFrameworkTokenStore<
return converter.ConvertToInvariantString(identifier); return converter.ConvertToInvariantString(identifier);
} }
/// <summary>
/// Tries to create a new <see cref="DbContextTransaction"/> with the specified <paramref name="level"/>.
/// </summary>
/// <param name="context">The Entity Framework context.</param>
/// <param name="level">The desired level of isolation.</param>
/// <returns>The <see cref="DbContextTransaction"/> if it could be created, <see langword="null"/> otherwise.</returns>
protected virtual DbContextTransaction? CreateTransaction(DbContext context, IsolationLevel level)
{
ArgumentNullException.ThrowIfNull(context);
try
{
return context.Database.BeginTransaction(level);
}
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{
return null;
}
}
} }

6
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreApplicationStore.cs

@ -187,7 +187,9 @@ public class OpenIddictEntityFrameworkCoreApplicationStore<
// Remove all the authorizations associated with the application and // Remove all the authorizations associated with the application and
// the tokens attached to these implicit or explicit authorizations. // the tokens attached to these implicit or explicit authorizations.
var authorizations = await ( var authorizations = await (
from authorization in context.Set<TAuthorization>().Include(authorization => authorization.Tokens).AsTracking() from authorization in context.Set<TAuthorization>()
.Include(static authorization => authorization.Tokens)
.AsTracking()
where authorization.Application!.Id!.Equals(application.Id) where authorization.Application!.Id!.Equals(application.Id)
select authorization).ToListAsync(cancellationToken); select authorization).ToListAsync(cancellationToken);
@ -481,7 +483,7 @@ public class OpenIddictEntityFrameworkCoreApplicationStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var query = context.Set<TApplication>().OrderBy(application => application.Id!).AsTracking(); var query = context.Set<TApplication>().OrderBy(static application => application.Id!).AsTracking();
if (offset is not null) if (offset is not null)
{ {

61
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs

@ -223,7 +223,9 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TAuthorization> authorizations = context.Set<TAuthorization>().Include(authorization => authorization.Application).AsTracking(); IQueryable<TAuthorization> authorizations = context.Set<TAuthorization>()
.Include(static authorization => authorization.Application)
.AsTracking();
if (!string.IsNullOrEmpty(query.Subject)) if (!string.IsNullOrEmpty(query.Subject))
{ {
@ -274,7 +276,9 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
var key = ConvertIdentifierFromString(identifier); var key = ConvertIdentifierFromString(identifier);
await foreach (var authorization in await foreach (var authorization in
(from authorization in context.Set<TAuthorization>().Include(authorization => authorization.Application).AsTracking() (from authorization in context.Set<TAuthorization>()
.Include(static authorization => authorization.Application)
.AsTracking()
where authorization.Application!.Id!.Equals(key) where authorization.Application!.Id!.Equals(key)
select authorization).AsAsyncEnumerable().WithCancellation(cancellationToken)) select authorization).AsAsyncEnumerable().WithCancellation(cancellationToken))
{ {
@ -306,7 +310,9 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var authorization in await foreach (var authorization in
(from authorization in context.Set<TAuthorization>().Include(authorization => authorization.Application).AsTracking() (from authorization in context.Set<TAuthorization>()
.Include(static authorization => authorization.Application)
.AsTracking()
where authorization.Subject == subject where authorization.Subject == subject
select authorization).AsAsyncEnumerable().WithCancellation(cancellationToken)) select authorization).AsAsyncEnumerable().WithCancellation(cancellationToken))
{ {
@ -351,9 +357,9 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
return await query( return await query(context.Set<TAuthorization>()
context.Set<TAuthorization>().Include(authorization => authorization.Application) .Include(static authorization => authorization.Application)
.AsTracking(), state).FirstOrDefaultAsync(cancellationToken); .AsTracking(), state).FirstOrDefaultAsync(cancellationToken);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -434,9 +440,9 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var query = context.Set<TAuthorization>() var query = context.Set<TAuthorization>()
.Include(authorization => authorization.Application) .Include(static authorization => authorization.Application)
.OrderBy(authorization => authorization.Id!) .OrderBy(static authorization => authorization.Id!)
.AsTracking(); .AsTracking();
if (offset is not null) if (offset is not null)
{ {
@ -467,10 +473,9 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var authorization in query( await foreach (var authorization in query(context.Set<TAuthorization>()
context.Set<TAuthorization>() .Include(static authorization => authorization.Application)
.Include(authorization => authorization.Application) .AsTracking(), state).AsAsyncEnumerable().WithCancellation(cancellationToken))
.AsTracking(), state).AsAsyncEnumerable().WithCancellation(cancellationToken))
{ {
yield return authorization; yield return authorization;
} }
@ -541,7 +546,9 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
IsolationLevel.RepeatableRead, cancellationToken); IsolationLevel.RepeatableRead, cancellationToken);
var authorizations = await var authorizations = await
(from authorization in context.Set<TAuthorization>().Include(authorization => authorization.Tokens).AsTracking() (from authorization in context.Set<TAuthorization>()
.Include(static authorization => authorization.Tokens)
.AsTracking()
where authorization.CreationDate < date where authorization.CreationDate < date
where authorization.Status != Statuses.Valid || authorization.Type == AuthorizationTypes.AdHoc where authorization.Status != Statuses.Valid || authorization.Type == AuthorizationTypes.AdHoc
where !authorization.Tokens.Any() where !authorization.Tokens.Any()
@ -568,7 +575,7 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception)) catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{ {
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
} }
} }
@ -585,7 +592,7 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
} }
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0243), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0243), exceptions);
} }
@ -599,7 +606,7 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TAuthorization> query = Options.CurrentValue.DisableBulkOperations IQueryable<TAuthorization> query = Options.CurrentValue.DisableBulkOperations
? context.Set<TAuthorization>().Include(authorization => authorization.Application).AsTracking() ? context.Set<TAuthorization>().Include(static authorization => authorization.Application).AsTracking()
: context.Set<TAuthorization>(); : context.Set<TAuthorization>();
if (!string.IsNullOrEmpty(subject)) if (!string.IsNullOrEmpty(subject))
@ -651,7 +658,7 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(authorization).State = EntityState.Unchanged; context.Entry(authorization).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -660,7 +667,7 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -692,7 +699,9 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
var result = 0L; var result = 0L;
foreach (var authorization in await (from authorization in context.Set<TAuthorization>().Include(authorization => authorization.Application).AsTracking() foreach (var authorization in await (from authorization in context.Set<TAuthorization>()
.Include(static authorization => authorization.Application)
.AsTracking()
where authorization.Application!.Id!.Equals(key) where authorization.Application!.Id!.Equals(key)
select authorization).ToListAsync(cancellationToken)) select authorization).ToListAsync(cancellationToken))
{ {
@ -708,7 +717,7 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(authorization).State = EntityState.Unchanged; context.Entry(authorization).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -717,7 +726,7 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -749,8 +758,8 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
var result = 0L; var result = 0L;
foreach (var authorization in await (from authorization in context.Set<TAuthorization>() foreach (var authorization in await (from authorization in context.Set<TAuthorization>()
.Include(authorization => authorization.Application) .Include(static authorization => authorization.Application)
.AsTracking() .AsTracking()
where authorization.Subject == subject where authorization.Subject == subject
select authorization).ToListAsync(cancellationToken)) select authorization).ToListAsync(cancellationToken))
{ {
@ -766,7 +775,7 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(authorization).State = EntityState.Unchanged; context.Entry(authorization).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -775,7 +784,7 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }

2
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreResourceStore.cs

@ -279,7 +279,7 @@ public class OpenIddictEntityFrameworkCoreResourceStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var query = context.Set<TResource>().OrderBy(resource => resource.Id!).AsTracking(); var query = context.Set<TResource>().OrderBy(static resource => resource.Id!).AsTracking();
if (offset is not null) if (offset is not null)
{ {

2
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreScopeStore.cs

@ -307,7 +307,7 @@ public class OpenIddictEntityFrameworkCoreScopeStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var query = context.Set<TScope>().OrderBy(scope => scope.Id!).AsTracking(); var query = context.Set<TScope>().OrderBy(static scope => scope.Id!).AsTracking();
if (offset is not null) if (offset is not null)
{ {

32
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreSessionStore.cs

@ -153,9 +153,9 @@ public class OpenIddictEntityFrameworkCoreSessionStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TSession> sessions = context.Set<TSession>() IQueryable<TSession> sessions = context.Set<TSession>()
.Include(session => session.Application) .Include(static session => session.Application)
.Include(session => session.Authorization) .Include(static session => session.Authorization)
.AsTracking(); .AsTracking();
if (!string.IsNullOrEmpty(query.Subject)) if (!string.IsNullOrEmpty(query.Subject))
{ {
@ -198,9 +198,9 @@ public class OpenIddictEntityFrameworkCoreSessionStore<
await foreach (var session in await foreach (var session in
(from session in context.Set<TSession>() (from session in context.Set<TSession>()
.Include(session => session.Application) .Include(static session => session.Application)
.Include(session => session.Authorization) .Include(static session => session.Authorization)
.AsTracking() .AsTracking()
where session.Application!.Id!.Equals(key) where session.Application!.Id!.Equals(key)
select session).AsAsyncEnumerable().WithCancellation(cancellationToken)) select session).AsAsyncEnumerable().WithCancellation(cancellationToken))
{ {
@ -223,9 +223,9 @@ public class OpenIddictEntityFrameworkCoreSessionStore<
await foreach (var session in await foreach (var session in
(from session in context.Set<TSession>() (from session in context.Set<TSession>()
.Include(session => session.Application) .Include(static session => session.Application)
.Include(session => session.Authorization) .Include(static session => session.Authorization)
.AsTracking() .AsTracking()
where session.Authorization!.Id!.Equals(key) where session.Authorization!.Id!.Equals(key)
select session).AsAsyncEnumerable().WithCancellation(cancellationToken)) select session).AsAsyncEnumerable().WithCancellation(cancellationToken))
{ {
@ -258,9 +258,9 @@ public class OpenIddictEntityFrameworkCoreSessionStore<
await foreach (var session in await foreach (var session in
(from session in context.Set<TSession>() (from session in context.Set<TSession>()
.Include(session => session.Application) .Include(static session => session.Application)
.Include(session => session.Authorization) .Include(static session => session.Authorization)
.AsTracking() .AsTracking()
where session.LoginId == identifier where session.LoginId == identifier
select session).AsAsyncEnumerable().WithCancellation(cancellationToken)) select session).AsAsyncEnumerable().WithCancellation(cancellationToken))
{ {
@ -282,9 +282,9 @@ public class OpenIddictEntityFrameworkCoreSessionStore<
await foreach (var session in await foreach (var session in
(from session in context.Set<TSession>() (from session in context.Set<TSession>()
.Include(session => session.Application) .Include(static session => session.Application)
.Include(session => session.Authorization) .Include(static session => session.Authorization)
.AsTracking() .AsTracking()
where session.Subject == subject where session.Subject == subject
select session).AsAsyncEnumerable().WithCancellation(cancellationToken)) select session).AsAsyncEnumerable().WithCancellation(cancellationToken))
{ {
@ -428,7 +428,7 @@ public class OpenIddictEntityFrameworkCoreSessionStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var query = context.Set<TSession>().OrderBy(session => session.Id!).AsTracking(); var query = context.Set<TSession>().OrderBy(static session => session.Id!).AsTracking();
if (offset is not null) if (offset is not null)
{ {

91
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreTokenStore.cs

@ -149,9 +149,9 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TToken> tokens = context.Set<TToken>() IQueryable<TToken> tokens = context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization) .Include(static token => token.Authorization)
.AsTracking(); .AsTracking();
if (!string.IsNullOrEmpty(query.Subject)) if (!string.IsNullOrEmpty(query.Subject))
{ {
@ -194,9 +194,9 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
await foreach (var token in await foreach (var token in
(from token in context.Set<TToken>() (from token in context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization) .Include(static token => token.Authorization)
.AsTracking() .AsTracking()
where token.Application!.Id!.Equals(key) where token.Application!.Id!.Equals(key)
select token).AsAsyncEnumerable().WithCancellation(cancellationToken)) select token).AsAsyncEnumerable().WithCancellation(cancellationToken))
{ {
@ -219,9 +219,9 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
await foreach (var token in await foreach (var token in
(from token in context.Set<TToken>() (from token in context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization) .Include(static token => token.Authorization)
.AsTracking() .AsTracking()
where token.Authorization!.Id!.Equals(key) where token.Authorization!.Id!.Equals(key)
select token).AsAsyncEnumerable().WithCancellation(cancellationToken)) select token).AsAsyncEnumerable().WithCancellation(cancellationToken))
{ {
@ -256,7 +256,10 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
select entry.Entity).FirstOrDefault(); select entry.Entity).FirstOrDefault();
Task<TToken?> QueryAsync() => Task<TToken?> QueryAsync() =>
(from token in context.Set<TToken>().Include(token => token.Application).Include(token => token.Authorization).AsTracking() (from token in context.Set<TToken>()
.Include(static token => token.Application)
.Include(static token => token.Authorization)
.AsTracking()
where token.ReferenceId == identifier where token.ReferenceId == identifier
select token).FirstOrDefaultAsync(cancellationToken); select token).FirstOrDefaultAsync(cancellationToken);
} }
@ -274,9 +277,9 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
await foreach (var token in await foreach (var token in
(from token in context.Set<TToken>() (from token in context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization) .Include(static token => token.Authorization)
.AsTracking() .AsTracking()
where token.Subject == subject where token.Subject == subject
select token).AsAsyncEnumerable().WithCancellation(cancellationToken)) select token).AsAsyncEnumerable().WithCancellation(cancellationToken))
{ {
@ -321,8 +324,9 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
return await query(context.Set<TToken>().Include(token => token.Application) return await query(context.Set<TToken>()
.Include(token => token.Authorization) .Include(static token => token.Application)
.Include(static token => token.Authorization)
.AsTracking(), state).FirstOrDefaultAsync(cancellationToken); .AsTracking(), state).FirstOrDefaultAsync(cancellationToken);
} }
@ -455,10 +459,10 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
var query = context.Set<TToken>() var query = context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization) .Include(static token => token.Authorization)
.OrderBy(token => token.Id!) .OrderBy(static token => token.Id!)
.AsTracking(); .AsTracking();
if (offset is not null) if (offset is not null)
{ {
@ -489,11 +493,10 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
{ {
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
await foreach (var token in query( await foreach (var token in query(context.Set<TToken>()
context.Set<TToken>() .Include(static token => token.Application)
.Include(token => token.Application) .Include(static token => token.Authorization)
.Include(token => token.Authorization) .AsTracking(), state).AsAsyncEnumerable().WithCancellation(cancellationToken))
.AsTracking(), state).AsAsyncEnumerable().WithCancellation(cancellationToken))
{ {
yield return token; yield return token;
} }
@ -589,7 +592,7 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception)) catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{ {
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
} }
} }
@ -606,7 +609,7 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
} }
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -620,7 +623,7 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
var context = await Context.GetDbContextAsync(cancellationToken); var context = await Context.GetDbContextAsync(cancellationToken);
IQueryable<TToken> query = Options.CurrentValue.DisableBulkOperations IQueryable<TToken> query = Options.CurrentValue.DisableBulkOperations
? context.Set<TToken>().Include(token => token.Application).Include(token => token.Authorization).AsTracking() ? context.Set<TToken>().Include(static token => token.Application).Include(static token => token.Authorization).AsTracking()
: context.Set<TToken>(); : context.Set<TToken>();
if (!string.IsNullOrEmpty(subject)) if (!string.IsNullOrEmpty(subject))
@ -672,7 +675,7 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(token).State = EntityState.Unchanged; context.Entry(token).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -681,7 +684,7 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -715,9 +718,9 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
var result = 0L; var result = 0L;
foreach (var token in await (from token in context.Set<TToken>() foreach (var token in await (from token in context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization) .Include(static token => token.Authorization)
.AsTracking() .AsTracking()
where token.Application!.Id!.Equals(key) where token.Application!.Id!.Equals(key)
where token.Status != Statuses.Revoked where token.Status != Statuses.Revoked
select token).ToListAsync(cancellationToken)) select token).ToListAsync(cancellationToken))
@ -734,7 +737,7 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(token).State = EntityState.Unchanged; context.Entry(token).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -743,7 +746,7 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -777,9 +780,9 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
var result = 0L; var result = 0L;
foreach (var token in await (from token in context.Set<TToken>() foreach (var token in await (from token in context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization) .Include(static token => token.Authorization)
.AsTracking() .AsTracking()
where token.Authorization!.Id!.Equals(key) where token.Authorization!.Id!.Equals(key)
where token.Status != Statuses.Revoked where token.Status != Statuses.Revoked
select token).ToListAsync(cancellationToken)) select token).ToListAsync(cancellationToken))
@ -796,7 +799,7 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(token).State = EntityState.Unchanged; context.Entry(token).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -805,7 +808,7 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }
@ -838,9 +841,9 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
var result = 0L; var result = 0L;
foreach (var token in await (from token in context.Set<TToken>() foreach (var token in await (from token in context.Set<TToken>()
.Include(token => token.Application) .Include(static token => token.Application)
.Include(token => token.Authorization) .Include(static token => token.Authorization)
.AsTracking() .AsTracking()
where token.Subject == subject where token.Subject == subject
where token.Status != Statuses.Revoked where token.Status != Statuses.Revoked
select token).ToListAsync(cancellationToken)) select token).ToListAsync(cancellationToken))
@ -857,7 +860,7 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
// Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing. // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
context.Entry(token).State = EntityState.Unchanged; context.Entry(token).State = EntityState.Unchanged;
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
continue; continue;
@ -866,7 +869,7 @@ public class OpenIddictEntityFrameworkCoreTokenStore<
result++; result++;
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions); throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
} }

2
src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbApplicationStore.cs

@ -352,7 +352,7 @@ public class OpenIddictMongoDbApplicationStore<
var database = await Context.GetDatabaseAsync(cancellationToken); var database = await Context.GetDatabaseAsync(cancellationToken);
var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName); var collection = database.GetCollection<TApplication>(Options.CurrentValue.ApplicationsCollectionName);
var query = (IQueryable<TApplication>) collection.AsQueryable().OrderBy(application => application.Id); var query = (IQueryable<TApplication>) collection.AsQueryable().OrderBy(static application => application.Id);
if (offset is not null) if (offset is not null)
{ {

2
src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbAuthorizationStore.cs

@ -315,7 +315,7 @@ public class OpenIddictMongoDbAuthorizationStore<
var database = await Context.GetDatabaseAsync(cancellationToken); var database = await Context.GetDatabaseAsync(cancellationToken);
var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName); var collection = database.GetCollection<TAuthorization>(Options.CurrentValue.AuthorizationsCollectionName);
var query = (IQueryable<TAuthorization>) collection.AsQueryable().OrderBy(authorization => authorization.Id); var query = (IQueryable<TAuthorization>) collection.AsQueryable().OrderBy(static authorization => authorization.Id);
if (offset is not null) if (offset is not null)
{ {

2
src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbResourceStore.cs

@ -257,7 +257,7 @@ public class OpenIddictMongoDbResourceStore<
var database = await Context.GetDatabaseAsync(cancellationToken); var database = await Context.GetDatabaseAsync(cancellationToken);
var collection = database.GetCollection<TResource>(Options.CurrentValue.ResourcesCollectionName); var collection = database.GetCollection<TResource>(Options.CurrentValue.ResourcesCollectionName);
var query = (IQueryable<TResource>) collection.AsQueryable().OrderBy(resource => resource.Id); var query = (IQueryable<TResource>) collection.AsQueryable().OrderBy(static resource => resource.Id);
if (offset is not null) if (offset is not null)
{ {

2
src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbScopeStore.cs

@ -284,7 +284,7 @@ public class OpenIddictMongoDbScopeStore<
var database = await Context.GetDatabaseAsync(cancellationToken); var database = await Context.GetDatabaseAsync(cancellationToken);
var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName); var collection = database.GetCollection<TScope>(Options.CurrentValue.ScopesCollectionName);
var query = (IQueryable<TScope>) collection.AsQueryable().OrderBy(scope => scope.Id); var query = (IQueryable<TScope>) collection.AsQueryable().OrderBy(static scope => scope.Id);
if (offset is not null) if (offset is not null)
{ {

2
src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbSessionStore.cs

@ -341,7 +341,7 @@ public class OpenIddictMongoDbSessionStore<
var database = await Context.GetDatabaseAsync(cancellationToken); var database = await Context.GetDatabaseAsync(cancellationToken);
var collection = database.GetCollection<TSession>(Options.CurrentValue.SessionsCollectionName); var collection = database.GetCollection<TSession>(Options.CurrentValue.SessionsCollectionName);
var query = (IQueryable<TSession>) collection.AsQueryable().OrderBy(session => session.Id); var query = (IQueryable<TSession>) collection.AsQueryable().OrderBy(static session => session.Id);
if (offset is not null) if (offset is not null)
{ {

2
src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbTokenStore.cs

@ -365,7 +365,7 @@ public class OpenIddictMongoDbTokenStore<
var database = await Context.GetDatabaseAsync(cancellationToken); var database = await Context.GetDatabaseAsync(cancellationToken);
var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName); var collection = database.GetCollection<TToken>(Options.CurrentValue.TokensCollectionName);
var query = (IQueryable<TToken>) collection.AsQueryable().OrderBy(token => token.Id); var query = (IQueryable<TToken>) collection.AsQueryable().OrderBy(static token => token.Id);
if (offset is not null) if (offset is not null)
{ {

10
src/OpenIddict.Quartz/OpenIddictQuartzJob.cs

@ -88,7 +88,7 @@ public sealed class OpenIddictQuartzJob : IJob
// occurred while trying to prune the entities. In this case, add the inner exceptions to the collection. // occurred while trying to prune the entities. In this case, add the inner exceptions to the collection.
catch (AggregateException exception) when (!OpenIddictHelpers.IsFatal(exception)) catch (AggregateException exception) when (!OpenIddictHelpers.IsFatal(exception))
{ {
exceptions ??= []; exceptions ??= new List<Exception>(capacity: exception.InnerExceptions.Count);
exceptions.AddRange(exception.InnerExceptions); exceptions.AddRange(exception.InnerExceptions);
} }
@ -96,7 +96,7 @@ public sealed class OpenIddictQuartzJob : IJob
// to be re-thrown later (typically, at the very end of this job, as an AggregateException). // to be re-thrown later (typically, at the very end of this job, as an AggregateException).
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception)) catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{ {
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
} }
} }
@ -133,7 +133,7 @@ public sealed class OpenIddictQuartzJob : IJob
// occurred while trying to prune the entities. In this case, add the inner exceptions to the collection. // occurred while trying to prune the entities. In this case, add the inner exceptions to the collection.
catch (AggregateException exception) when (!OpenIddictHelpers.IsFatal(exception)) catch (AggregateException exception) when (!OpenIddictHelpers.IsFatal(exception))
{ {
exceptions ??= []; exceptions ??= new List<Exception>(capacity: exception.InnerExceptions.Count);
exceptions.AddRange(exception.InnerExceptions); exceptions.AddRange(exception.InnerExceptions);
} }
@ -141,12 +141,12 @@ public sealed class OpenIddictQuartzJob : IJob
// to be re-thrown later (typically, at the very end of this job, as an AggregateException). // to be re-thrown later (typically, at the very end of this job, as an AggregateException).
catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception)) catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
{ {
exceptions ??= []; exceptions ??= new List<Exception>(capacity: 1);
exceptions.Add(exception); exceptions.Add(exception);
} }
} }
if (exceptions is not null) if (exceptions is { Count: > 0 })
{ {
throw new JobExecutionException(new AggregateException(exceptions)) throw new JobExecutionException(new AggregateException(exceptions))
{ {

Loading…
Cancel
Save