diff --git a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs index 464b140f..8b0cf132 100644 --- a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs @@ -160,12 +160,15 @@ namespace OpenIddict.Core return ConvertIdentifierToString(authorization.Application.Id); } - var key = await GetAsync(authorizations => - from element in authorizations - where element.Id.Equals(authorization.Id) - select element.Application.Id, cancellationToken); + IQueryable Query(IQueryable authorizations) + { + return from element in authorizations + where element.Id.Equals(authorization.Id) + where element.Application != null + select element.Application.Id; + } - return ConvertIdentifierToString(key); + return ConvertIdentifierToString(await GetAsync(Query, cancellationToken)); } /// diff --git a/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs index d1f33308..8e069019 100644 --- a/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs @@ -243,12 +243,15 @@ namespace OpenIddict.Core return ConvertIdentifierToString(token.Application.Id); } - var key = await GetAsync(tokens => - from element in tokens - where element.Id.Equals(token.Id) - select element.Application.Id, cancellationToken); + IQueryable Query(IQueryable tokens) + { + return from element in tokens + where element.Id.Equals(token.Id) + where element.Application != null + select element.Application.Id; + } - return ConvertIdentifierToString(key); + return ConvertIdentifierToString(await GetAsync(Query, cancellationToken)); } /// @@ -272,12 +275,15 @@ namespace OpenIddict.Core return ConvertIdentifierToString(token.Authorization.Id); } - var key = await GetAsync(tokens => - from element in tokens - where element.Id.Equals(token.Id) - select element.Authorization.Id, cancellationToken); + IQueryable Query(IQueryable tokens) + { + return from element in tokens + where element.Id.Equals(token.Id) + where element.Authorization != null + select element.Authorization.Id; + } - return ConvertIdentifierToString(key); + return ConvertIdentifierToString(await GetAsync(Query, cancellationToken)); } /// diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs index 9eff8cdc..efdb3847 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs @@ -183,7 +183,17 @@ namespace OpenIddict.EntityFramework throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - return Authorizations.FindAsync(cancellationToken, ConvertIdentifierFromString(identifier)); + var authorization = (from entry in Context.ChangeTracker.Entries() + where entry.Entity != null + where entry.Entity.Id.Equals(ConvertIdentifierFromString(identifier)) + select entry.Entity).FirstOrDefault(); + + if (authorization != null) + { + return Task.FromResult(authorization); + } + + return base.FindByIdAsync(identifier, cancellationToken); } /// @@ -202,17 +212,21 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(authorization)); } - // If the application is not attached to the authorization instance (which is expected - // if the token was retrieved using the default FindBy*Async APIs as they don't - // eagerly load the application from the database), try to load it manually. + // If the application is not attached to the authorization, try to load it manually. + if (authorization.Application == null) + { + var reference = Context.Entry(authorization).Reference(entry => entry.Application); + if (reference.EntityEntry.State == EntityState.Detached) + { + return null; + } + + await reference.LoadAsync(cancellationToken); + } + if (authorization.Application == null) { - return ConvertIdentifierToString( - await Context.Entry(authorization) - .Reference(entry => entry.Application) - .Query() - .Select(application => application.Id) - .FirstOrDefaultAsync()); + return null; } return ConvertIdentifierToString(authorization.Application.Id); @@ -235,7 +249,7 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(query)); } - return query(Authorizations).FirstOrDefaultAsync(cancellationToken); + return query(Authorizations.Include(authorization => authorization.Application)).FirstOrDefaultAsync(cancellationToken); } /// @@ -255,7 +269,8 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(query)); } - return ImmutableArray.CreateRange(await query(Authorizations).ToListAsync(cancellationToken)); + return ImmutableArray.CreateRange(await query( + Authorizations.Include(authorization => authorization.Application)).ToListAsync(cancellationToken)); } /// @@ -287,15 +302,19 @@ namespace OpenIddict.EntityFramework else { - var key = await GetIdAsync(authorization, cancellationToken); - - // Try to retrieve the application associated with the authorization. - // If none can be found, assume that no application is attached. - var application = await Applications.FirstOrDefaultAsync(element => element.Authorizations.Any(t => t.Id.Equals(key))); - if (application != null) + // If the application is not attached to the authorization, try to load it manually. + if (authorization.Application == null) { - application.Authorizations.Remove(authorization); + var reference = Context.Entry(authorization).Reference(entry => entry.Application); + if (reference.EntityEntry.State == EntityState.Detached) + { + return; + } + + await reference.LoadAsync(cancellationToken); } + + authorization.Application = null; } } diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs index 95edee7a..69cfe996 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs @@ -167,7 +167,17 @@ namespace OpenIddict.EntityFramework throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - return Tokens.FindAsync(cancellationToken, ConvertIdentifierFromString(identifier)); + var token = (from entry in Context.ChangeTracker.Entries() + where entry.Entity != null + where entry.Entity.Id.Equals(ConvertIdentifierFromString(identifier)) + select entry.Entity).FirstOrDefault(); + + if (token != null) + { + return Task.FromResult(token); + } + + return base.FindByIdAsync(identifier, cancellationToken); } /// @@ -186,17 +196,21 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(token)); } - // If the application is not attached to the token instance (which is expected - // if the token was retrieved using the default FindBy*Async APIs as they don't - // eagerly load the application from the database), try to load it manually. + // If the application is not attached to the token, try to load it manually. + if (token.Application == null) + { + var reference = Context.Entry(token).Reference(entry => entry.Application); + if (reference.EntityEntry.State == EntityState.Detached) + { + return null; + } + + await reference.LoadAsync(cancellationToken); + } + if (token.Application == null) { - return ConvertIdentifierToString( - await Context.Entry(token) - .Reference(entry => entry.Application) - .Query() - .Select(application => application.Id) - .FirstOrDefaultAsync()); + return null; } return ConvertIdentifierToString(token.Application.Id); @@ -219,7 +233,9 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(query)); } - return query(Tokens).FirstOrDefaultAsync(cancellationToken); + return query( + Tokens.Include(token => token.Application) + .Include(token => token.Authorization)).FirstOrDefaultAsync(cancellationToken); } /// @@ -238,17 +254,21 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(token)); } - // If the authorization is not attached to the token instance (which is expected - // if the token was retrieved using the default FindBy*Async APIs as they don't - // eagerly load the authorization from the database), try to load it manually. + // If the authorization is not attached to the token, try to load it manually. + if (token.Authorization == null) + { + var reference = Context.Entry(token).Reference(entry => entry.Authorization); + if (reference.EntityEntry.State == EntityState.Detached) + { + return null; + } + + await reference.LoadAsync(cancellationToken); + } + if (token.Authorization == null) { - return ConvertIdentifierToString( - await Context.Entry(token) - .Reference(entry => entry.Authorization) - .Query() - .Select(authorization => authorization.Id) - .FirstOrDefaultAsync()); + return null; } return ConvertIdentifierToString(token.Authorization.Id); @@ -271,7 +291,9 @@ namespace OpenIddict.EntityFramework throw new ArgumentNullException(nameof(query)); } - return ImmutableArray.CreateRange(await query(Tokens).ToListAsync(cancellationToken)); + return ImmutableArray.CreateRange(await query( + Tokens.Include(token => token.Application) + .Include(token => token.Authorization)).ToListAsync(cancellationToken)); } /// @@ -303,15 +325,19 @@ namespace OpenIddict.EntityFramework else { - var key = await GetIdAsync(token, cancellationToken); - - // Try to retrieve the application associated with the token. - // If none can be found, assume that no application is attached. - var application = await Applications.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key))); - if (application != null) + // If the application is not attached to the token, try to load it manually. + if (token.Application == null) { - application.Tokens.Remove(token); + var reference = Context.Entry(token).Reference(entry => entry.Application); + if (reference.EntityEntry.State == EntityState.Detached) + { + return; + } + + await reference.LoadAsync(cancellationToken); } + + token.Application = null; } } @@ -344,15 +370,19 @@ namespace OpenIddict.EntityFramework else { - var key = await GetIdAsync(token, cancellationToken); - - // Try to retrieve the authorization associated with the token. - // If none can be found, assume that no authorization is attached. - var authorization = await Authorizations.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key))); - if (authorization != null) + // If the authorization is not attached to the token, try to load it manually. + if (token.Authorization == null) { - authorization.Tokens.Remove(token); + var reference = Context.Entry(token).Reference(entry => entry.Authorization); + if (reference.EntityEntry.State == EntityState.Detached) + { + return; + } + + await reference.LoadAsync(cancellationToken); } + + token.Authorization = null; } } diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs index d8788198..07a1d217 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs @@ -199,7 +199,7 @@ namespace OpenIddict.EntityFrameworkCore { var key = ConvertIdentifierFromString(client); - return from authorization in authorizations + return from authorization in authorizations.Include(authorization => authorization.Application) where authorization.Subject == subject join application in applications on authorization.Application.Id equals application.Id where application.Id.Equals(key) @@ -225,7 +225,17 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - return Authorizations.FindAsync(new object[] { ConvertIdentifierFromString(identifier) }, cancellationToken); + var authorization = (from entry in Context.ChangeTracker.Entries() + where entry.Entity != null + where entry.Entity.Id.Equals(ConvertIdentifierFromString(identifier)) + select entry.Entity).FirstOrDefault(); + + if (authorization != null) + { + return Task.FromResult(authorization); + } + + return base.FindByIdAsync(identifier, cancellationToken); } /// @@ -244,17 +254,21 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(authorization)); } - // If the application is not attached to the authorization instance (which is expected - // if the token was retrieved using the default FindBy*Async APIs as they don't - // eagerly load the application from the database), try to load it manually. + // If the application is not attached to the authorization, try to load it manually. + if (authorization.Application == null) + { + var reference = Context.Entry(authorization).Reference(entry => entry.Application); + if (reference.EntityEntry.State == EntityState.Detached) + { + return null; + } + + await reference.LoadAsync(cancellationToken); + } + if (authorization.Application == null) { - return ConvertIdentifierToString( - await Context.Entry(authorization) - .Reference(entry => entry.Application) - .Query() - .Select(application => application.Id) - .FirstOrDefaultAsync()); + return null; } return ConvertIdentifierToString(authorization.Application.Id); @@ -277,7 +291,7 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(query)); } - return query(Authorizations).FirstOrDefaultAsync(cancellationToken); + return query(Authorizations.Include(authorization => authorization.Application)).FirstOrDefaultAsync(cancellationToken); } /// @@ -297,7 +311,8 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(query)); } - return ImmutableArray.CreateRange(await query(Authorizations).ToListAsync(cancellationToken)); + return ImmutableArray.CreateRange(await query( + Authorizations.Include(authorization => authorization.Application)).ToListAsync(cancellationToken)); } /// @@ -329,15 +344,19 @@ namespace OpenIddict.EntityFrameworkCore else { - var key = await GetIdAsync(authorization, cancellationToken); - - // Try to retrieve the application associated with the authorization. - // If none can be found, assume that no application is attached. - var application = await Applications.FirstOrDefaultAsync(element => element.Authorizations.Any(t => t.Id.Equals(key))); - if (application != null) + // If the application is not attached to the authorization, try to load it manually. + if (authorization.Application == null) { - application.Authorizations.Remove(authorization); + var reference = Context.Entry(authorization).Reference(entry => entry.Application); + if (reference.EntityEntry.State == EntityState.Detached) + { + return; + } + + await reference.LoadAsync(cancellationToken); } + + authorization.Application = null; } } diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs index 9d0f8d49..dfcbad38 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs @@ -176,7 +176,7 @@ namespace OpenIddict.EntityFrameworkCore { var key = ConvertIdentifierFromString(identifier); - return from token in tokens + return from token in tokens.Include(token => token.Application).Include(token => token.Authorization) join application in applications on token.Application.Id equals application.Id where application.Id.Equals(key) select token; @@ -210,7 +210,7 @@ namespace OpenIddict.EntityFrameworkCore { var key = ConvertIdentifierFromString(identifier); - return from token in tokens + return from token in tokens.Include(token => token.Application).Include(token => token.Authorization) join authorization in authorizations on token.Authorization.Id equals authorization.Id where authorization.Id.Equals(key) select token; @@ -235,7 +235,17 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - return Tokens.FindAsync(new object[] { ConvertIdentifierFromString(identifier) }, cancellationToken); + var token = (from entry in Context.ChangeTracker.Entries() + where entry.Entity != null + where entry.Entity.Id.Equals(ConvertIdentifierFromString(identifier)) + select entry.Entity).FirstOrDefault(); + + if (token != null) + { + return Task.FromResult(token); + } + + return base.FindByIdAsync(identifier, cancellationToken); } /// @@ -254,17 +264,21 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(token)); } - // If the application is not attached to the token instance (which is expected - // if the token was retrieved using the default FindBy*Async APIs as they don't - // eagerly load the application from the database), try to load it manually. + // If the application is not attached to the token, try to load it manually. + if (token.Application == null) + { + var reference = Context.Entry(token).Reference(entry => entry.Application); + if (reference.EntityEntry.State == EntityState.Detached) + { + return null; + } + + await reference.LoadAsync(cancellationToken); + } + if (token.Application == null) { - return ConvertIdentifierToString( - await Context.Entry(token) - .Reference(entry => entry.Application) - .Query() - .Select(application => application.Id) - .FirstOrDefaultAsync()); + return null; } return ConvertIdentifierToString(token.Application.Id); @@ -287,7 +301,9 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(query)); } - return query(Tokens).FirstOrDefaultAsync(cancellationToken); + return query( + Tokens.Include(token => token.Application) + .Include(token => token.Authorization)).FirstOrDefaultAsync(cancellationToken); } /// @@ -306,17 +322,21 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(token)); } - // If the authorization is not attached to the token instance (which is expected - // if the token was retrieved using the default FindBy*Async APIs as they don't - // eagerly load the authorization from the database), try to load it manually. + // If the authorization is not attached to the token, try to load it manually. + if (token.Authorization == null) + { + var reference = Context.Entry(token).Reference(entry => entry.Authorization); + if (reference.EntityEntry.State == EntityState.Detached) + { + return null; + } + + await reference.LoadAsync(cancellationToken); + } + if (token.Authorization == null) { - return ConvertIdentifierToString( - await Context.Entry(token) - .Reference(entry => entry.Authorization) - .Query() - .Select(authorization => authorization.Id) - .FirstOrDefaultAsync()); + return null; } return ConvertIdentifierToString(token.Authorization.Id); @@ -339,7 +359,9 @@ namespace OpenIddict.EntityFrameworkCore throw new ArgumentNullException(nameof(query)); } - return ImmutableArray.CreateRange(await query(Tokens).ToListAsync(cancellationToken)); + return ImmutableArray.CreateRange(await query( + Tokens.Include(token => token.Application) + .Include(token => token.Authorization)).ToListAsync(cancellationToken)); } /// @@ -371,15 +393,19 @@ namespace OpenIddict.EntityFrameworkCore else { - var key = await GetIdAsync(token, cancellationToken); - - // Try to retrieve the application associated with the token. - // If none can be found, assume that no application is attached. - var application = await Applications.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key))); - if (application != null) + // If the application is not attached to the token, try to load it manually. + if (token.Application == null) { - application.Tokens.Remove(token); + var reference = Context.Entry(token).Reference(entry => entry.Application); + if (reference.EntityEntry.State == EntityState.Detached) + { + return; + } + + await reference.LoadAsync(cancellationToken); } + + token.Application = null; } } @@ -412,15 +438,19 @@ namespace OpenIddict.EntityFrameworkCore else { - var key = await GetIdAsync(token, cancellationToken); - - // Try to retrieve the authorization associated with the token. - // If none can be found, assume that no authorization is attached. - var authorization = await Authorizations.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key))); - if (authorization != null) + // If the authorization is not attached to the token, try to load it manually. + if (token.Authorization == null) { - authorization.Tokens.Remove(token); + var reference = Context.Entry(token).Reference(entry => entry.Authorization); + if (reference.EntityEntry.State == EntityState.Detached) + { + return; + } + + await reference.LoadAsync(cancellationToken); } + + token.Authorization = null; } }