Browse Source

Cross-compile the EF Core stores to support EF Core 2.x on .NET Standard 2.0

pull/820/head
Kévin Chalet 7 years ago
parent
commit
092097a57c
  1. 2
      Directory.Build.props
  2. 14
      Directory.Build.targets
  3. 6
      eng/Versions.props
  4. 6
      src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj
  5. 53
      src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreHelpers.cs
  6. 25
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
  7. 76
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
  8. 28
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs
  9. 92
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
  10. 3
      test/OpenIddict.EntityFrameworkCore.Tests/OpenIddict.EntityFrameworkCore.Tests.csproj

2
Directory.Build.props

@ -4,7 +4,7 @@
<PropertyGroup>
<LangVersion>preview</LangVersion>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn);CS1591;NU5128</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DebugSymbols>true</DebugSymbols>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\eng\key.snk</AssemblyOriginatorKeyFile>

14
Directory.Build.targets

@ -12,4 +12,18 @@
<Copyright>$(_ProjectCopyright)</Copyright>
</PropertyGroup>
<!--
Note: Entity Framework Core 2.x references System.Interactive.Async 3.x, that includes
its own IAsyncEnumerable. To work around collisions between this type and the new type
now included in the BCL (System.Runtime), an alias is added to System.Interactive.Async.
-->
<Target Name="AddAssemblyAliasToReactiveAsync" AfterTargets="ResolveAssemblyReferences">
<ItemGroup>
<ReferencePath Condition=" '%(FileName)' == 'System.Interactive.Async' ">
<Aliases>reactive</Aliases>
</ReferencePath>
</ItemGroup>
</Target>
</Project>

6
eng/Versions.props

@ -23,12 +23,16 @@
'$(TargetFramework)' == 'netcoreapp3.0' ">3.0.0</ExtensionsVersion>
</PropertyGroup>
<PropertyGroup>
<EntityFrameworkCoreVersion Condition=" '$(TargetFramework)' == 'netstandard2.0' ">2.1.0</EntityFrameworkCoreVersion>
<EntityFrameworkCoreVersion Condition=" '$(TargetFramework)' == 'netstandard2.1' ">3.0.0</EntityFrameworkCoreVersion>
</PropertyGroup>
<PropertyGroup>
<BclAsyncInterfacesVersion>1.0.0</BclAsyncInterfacesVersion>
<BouncyCastleVersion>1.8.5</BouncyCastleVersion>
<DataAnnotationsVersion>4.4.0</DataAnnotationsVersion>
<EntityFrameworkVersion>6.3.0</EntityFrameworkVersion>
<EntityFrameworkCoreVersion>3.0.0</EntityFrameworkCoreVersion>
<JetBrainsVersion>2019.1.3</JetBrainsVersion>
<JsonNetVersion>12.0.2</JsonNetVersion>
<JsonNetBsonVersion>1.0.2</JsonNetBsonVersion>

6
src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
</PropertyGroup>
<PropertyGroup>
@ -23,4 +23,8 @@
<Compile Include="..\..\shared\OpenIddict.Extensions\*\*.cs" />
</ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
<DefineConstants>$(DefineConstants);SUPPORTS_BCL_ASYNC_ENUMERABLE</DefineConstants>
</PropertyGroup>
</Project>

53
src/OpenIddict.EntityFrameworkCore/OpenIddictEntityFrameworkCoreHelpers.cs

@ -5,8 +5,12 @@
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using OpenIddict.EntityFrameworkCore;
using OpenIddict.EntityFrameworkCore.Models;
@ -112,5 +116,54 @@ namespace Microsoft.EntityFrameworkCore
.ApplyConfiguration(new OpenIddictScopeConfiguration<TScope, TKey>())
.ApplyConfiguration(new OpenIddictTokenConfiguration<TToken, TApplication, TAuthorization, TKey>());
}
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
/// <summary>
/// Converts an EF Core/IX-based async enumeration to a BCL enumeration.
/// </summary>
/// <typeparam name="T">The type of the returned entities.</typeparam>
/// <param name="source">The EF Core/IX async enumeration.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The non-streamed async enumeration containing the results.</returns>
internal static IAsyncEnumerable<T> AsAsyncEnumerable<T>(
[NotNull] this AsyncEnumerable<T> source, CancellationToken cancellationToken = default)
{
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<T> ExecuteAsync(CancellationToken cancellationToken)
{
foreach (var element in await source.ToListAsync(cancellationToken))
{
yield return element;
}
}
}
/// <summary>
/// Executes the query and returns the results as a non-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 non-streamed async enumeration containing the results.</returns>
internal static IAsyncEnumerable<T> AsAsyncEnumerable<T>(
[NotNull] this IQueryable<T> source, CancellationToken cancellationToken = default)
{
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<T> ExecuteAsync(CancellationToken cancellationToken)
{
foreach (var element in await source.ToListAsync(cancellationToken))
{
yield return element;
}
}
}
#endif
}
}

25
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs

@ -16,6 +16,7 @@ using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
@ -327,7 +328,14 @@ namespace OpenIddict.EntityFrameworkCore
/// Exposes a compiled query allowing to retrieve all the applications
/// associated with the specified post_logout_redirect_uri.
/// </summary>
private static readonly Func<TContext, string, IAsyncEnumerable<TApplication>> FindByPostLogoutRedirectUri =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, string, IAsyncEnumerable<TApplication>>
#else
Func<TContext, string, AsyncEnumerable<TApplication>>
#endif
FindByPostLogoutRedirectUri =
// To optimize the efficiency of the query a bit, only applications whose stringified
// PostLogoutRedirectUris contains the specified URL are returned. Once the applications
// are retrieved, a second pass is made to ensure only valid elements are returned.
@ -353,6 +361,9 @@ namespace OpenIddict.EntityFrameworkCore
}
return FindByPostLogoutRedirectUri(Context, address)
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
.WhereAwait(async application => (await GetPostLogoutRedirectUrisAsync(application, cancellationToken))
.Contains(address, StringComparer.Ordinal));
}
@ -361,7 +372,14 @@ namespace OpenIddict.EntityFrameworkCore
/// Exposes a compiled query allowing to retrieve all the
/// applications associated with the specified redirect_uri.
/// </summary>
private static readonly Func<TContext, string, IAsyncEnumerable<TApplication>> FindByRedirectUri =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, string, IAsyncEnumerable<TApplication>>
#else
Func<TContext, string, AsyncEnumerable<TApplication>>
#endif
FindByRedirectUri =
// To optimize the efficiency of the query a bit, only applications whose stringified
// RedirectUris property contains the specified URL are returned. Once the applications
// are retrieved, a second pass is made to ensure only valid elements are returned.
@ -387,6 +405,9 @@ namespace OpenIddict.EntityFrameworkCore
}
return FindByRedirectUri(Context, address)
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
.WhereAwait(async application => (await GetRedirectUrisAsync(application, cancellationToken))
.Contains(address, StringComparer.Ordinal));
}

76
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs

@ -16,6 +16,7 @@ using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
@ -247,7 +248,14 @@ namespace OpenIddict.EntityFrameworkCore
/// Exposes a compiled query allowing to retrieve the authorizations corresponding
/// to the specified subject and associated with the application identifier.
/// </summary>
private static readonly Func<TContext, TKey, string, IAsyncEnumerable<TAuthorization>> FindBySubjectAndClient =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, TKey, string, IAsyncEnumerable<TAuthorization>>
#else
Func<TContext, TKey, string, AsyncEnumerable<TAuthorization>>
#endif
FindBySubjectAndClient =
// Note: due to a bug in Entity Framework Core's query visitor, the authorizations can't be
// filtered using authorization.Application.Id.Equals(key). To work around this issue,
// this compiled query uses an explicit join before applying the equality check.
@ -282,13 +290,24 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentException("The client cannot be null or empty.", nameof(client));
}
return FindBySubjectAndClient(Context, ConvertIdentifierFromString(client), subject);
return FindBySubjectAndClient(Context, ConvertIdentifierFromString(client), subject)
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
;
}
/// <summary>
/// Exposes a compiled query allowing to retrieve the authorizations matching the specified parameters.
/// </summary>
private static readonly Func<TContext, TKey, string, string, IAsyncEnumerable<TAuthorization>> FindBySubjectClientAndStatus =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, TKey, string, string, IAsyncEnumerable<TAuthorization>>
#else
Func<TContext, TKey, string, string, AsyncEnumerable<TAuthorization>>
#endif
FindBySubjectClientAndStatus =
// Note: due to a bug in Entity Framework Core's query visitor, the authorizations can't be
// filtered using authorization.Application.Id.Equals(key). To work around this issue,
// this compiled query uses an explicit join before applying the equality check.
@ -329,13 +348,24 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentException("The status cannot be null or empty.", nameof(status));
}
return FindBySubjectClientAndStatus(Context, ConvertIdentifierFromString(client), subject, status);
return FindBySubjectClientAndStatus(Context, ConvertIdentifierFromString(client), subject, status)
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
;
}
/// <summary>
/// Exposes a compiled query allowing to retrieve the authorizations matching the specified parameters.
/// </summary>
private static readonly Func<TContext, TKey, string, string, string, IAsyncEnumerable<TAuthorization>> FindBySubjectClientStatusAndType =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, TKey, string, string, string, IAsyncEnumerable<TAuthorization>>
#else
Func<TContext, TKey, string, string, string, AsyncEnumerable<TAuthorization>>
#endif
FindBySubjectClientStatusAndType =
// Note: due to a bug in Entity Framework Core's query visitor, the authorizations can't be
// filtered using authorization.Application.Id.Equals(key). To work around this issue,
// this compiled query uses an explicit join before applying the equality check.
@ -384,7 +414,11 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentException("The type cannot be null or empty.", nameof(type));
}
return FindBySubjectClientStatusAndType(Context, ConvertIdentifierFromString(client), subject, status, type);
return FindBySubjectClientStatusAndType(Context, ConvertIdentifierFromString(client), subject, status, type)
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
;
}
/// <summary>
@ -409,7 +443,14 @@ namespace OpenIddict.EntityFrameworkCore
/// Exposes a compiled query allowing to retrieve the list of
/// authorizations corresponding to the specified application identifier.
/// </summary>
private static readonly Func<TContext, TKey, IAsyncEnumerable<TAuthorization>> FindByApplicationId =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, TKey, IAsyncEnumerable<TAuthorization>>
#else
Func<TContext, TKey, AsyncEnumerable<TAuthorization>>
#endif
FindByApplicationId =
// Note: due to a bug in Entity Framework Core's query visitor, the authorizations can't be
// filtered using authorization.Application.Id.Equals(key). To work around this issue,
// this compiled query uses an explicit join before applying the equality check.
@ -436,7 +477,11 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
return FindByApplicationId(Context, ConvertIdentifierFromString(identifier));
return FindByApplicationId(Context, ConvertIdentifierFromString(identifier))
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
;
}
/// <summary>
@ -470,7 +515,14 @@ namespace OpenIddict.EntityFrameworkCore
/// Exposes a compiled query allowing to retrieve all the
/// authorizations corresponding to the specified subject.
/// </summary>
private static readonly Func<TContext, string, IAsyncEnumerable<TAuthorization>> FindBySubject =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, string, IAsyncEnumerable<TAuthorization>>
#else
Func<TContext, string, AsyncEnumerable<TAuthorization>>
#endif
FindBySubject =
EF.CompileAsyncQuery((TContext context, string subject) =>
from authorization in context.Set<TAuthorization>()
.Include(authorization => authorization.Application)
@ -492,7 +544,11 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
}
return FindBySubject(Context, subject);
return FindBySubject(Context, subject)
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
;
}
/// <summary>

28
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs

@ -14,6 +14,7 @@ using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
@ -235,8 +236,13 @@ namespace OpenIddict.EntityFrameworkCore
/// <summary>
/// Exposes a compiled query allowing to retrieve a list of scopes using their name.
/// </summary>
private static readonly Func<TContext, ImmutableArray<string>, IAsyncEnumerable<TScope>> FindByNames =
EF.CompileAsyncQuery((TContext context, ImmutableArray<string> names) =>
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, ImmutableArray<string>, IAsyncEnumerable<TScope>>
#else
Func<TContext, ImmutableArray<string>, AsyncEnumerable<TScope>>
#endif
FindByNames = EF.CompileAsyncQuery((TContext context, ImmutableArray<string> names) =>
from scope in context.Set<TScope>().AsTracking()
where names.Contains(scope.Name)
select scope);
@ -255,13 +261,24 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentException("Scope names cannot be null or empty.", nameof(names));
}
return FindByNames(Context, names);
return FindByNames(Context, names)
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
;
}
/// <summary>
/// Exposes a compiled query allowing to retrieve all the scopes that contain the specified resource.
/// </summary>
private static readonly Func<TContext, string, IAsyncEnumerable<TScope>> FindByResource =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, string, IAsyncEnumerable<TScope>>
#else
Func<TContext, string, AsyncEnumerable<TScope>>
#endif
FindByResource =
// To optimize the efficiency of the query a bit, only scopes whose stringified
// Resources column contains the specified resource are returned. Once the scopes
// are retrieved, a second pass is made to ensure only valid elements are returned.
@ -287,6 +304,9 @@ namespace OpenIddict.EntityFrameworkCore
}
return FindByResource(Context, resource)
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
.WhereAwait(async scope => (await GetResourcesAsync(scope, cancellationToken)).Contains(resource, StringComparer.Ordinal));
}

92
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs

@ -6,7 +6,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Data;
using System.Linq;
@ -16,6 +15,7 @@ using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
@ -201,7 +201,14 @@ namespace OpenIddict.EntityFrameworkCore
/// Exposes a compiled query allowing to retrieve the tokens corresponding
/// to the specified subject and associated with the application identifier.
/// </summary>
private static readonly Func<TContext, TKey, string, IAsyncEnumerable<TToken>> FindBySubjectAndClient =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, TKey, string, IAsyncEnumerable<TToken>>
#else
Func<TContext, TKey, string, AsyncEnumerable<TToken>>
#endif
FindBySubjectAndClient =
// Note: due to a bug in Entity Framework Core's query visitor, the authorizations can't be
// filtered using token.Application.Id.Equals(key). To work around this issue,
// this compiled query uses an explicit join before applying the equality check.
@ -237,13 +244,24 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentException("The client cannot be null or empty.", nameof(client));
}
return FindBySubjectAndClient(Context, ConvertIdentifierFromString(client), subject);
return FindBySubjectAndClient(Context, ConvertIdentifierFromString(client), subject)
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
;
}
/// <summary>
/// Exposes a compiled query allowing to retrieve the tokens matching the specified parameters.
/// </summary>
private static readonly Func<TContext, TKey, string, string, IAsyncEnumerable<TToken>> FindBySubjectClientAndStatus =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, TKey, string, string, IAsyncEnumerable<TToken>>
#else
Func<TContext, TKey, string, string, AsyncEnumerable<TToken>>
#endif
FindBySubjectClientAndStatus =
// Note: due to a bug in Entity Framework Core's query visitor, the authorizations can't be
// filtered using token.Application.Id.Equals(key). To work around this issue,
// this compiled query uses an explicit join before applying the equality check.
@ -285,13 +303,24 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentException("The status cannot be null or empty.", nameof(status));
}
return FindBySubjectClientAndStatus(Context, ConvertIdentifierFromString(client), subject, status);
return FindBySubjectClientAndStatus(Context, ConvertIdentifierFromString(client), subject, status)
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
;
}
/// <summary>
/// Exposes a compiled query allowing to retrieve the tokens matching the specified parameters.
/// </summary>
private static readonly Func<TContext, TKey, string, string, string, IAsyncEnumerable<TToken>> FindBySubjectClientStatusAndType =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, TKey, string, string, string, IAsyncEnumerable<TToken>>
#else
Func<TContext, TKey, string, string, string, AsyncEnumerable<TToken>>
#endif
FindBySubjectClientStatusAndType =
// Note: due to a bug in Entity Framework Core's query visitor, the authorizations can't be
// filtered using token.Application.Id.Equals(key). To work around this issue,
// this compiled query uses an explicit join before applying the equality check.
@ -341,14 +370,25 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentException("The type cannot be null or empty.", nameof(type));
}
return FindBySubjectClientStatusAndType(Context, ConvertIdentifierFromString(client), subject, status, type);
return FindBySubjectClientStatusAndType(Context, ConvertIdentifierFromString(client), subject, status, type)
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
;
}
/// <summary>
/// Exposes a compiled query allowing to retrieve the list of
/// tokens corresponding to the specified application identifier.
/// </summary>
private static readonly Func<TContext, TKey, IAsyncEnumerable<TToken>> FindByApplicationId =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, TKey, IAsyncEnumerable<TToken>>
#else
Func<TContext, TKey, AsyncEnumerable<TToken>>
#endif
FindByApplicationId =
// Note: due to a bug in Entity Framework Core's query visitor, the tokens can't be
// filtered using token.Application.Id.Equals(key). To work around this issue,
// this compiled query uses an explicit join before applying the equality check.
@ -375,14 +415,25 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
return FindByApplicationId(Context, ConvertIdentifierFromString(identifier));
return FindByApplicationId(Context, ConvertIdentifierFromString(identifier))
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
;
}
/// <summary>
/// Exposes a compiled query allowing to retrieve the list of
/// tokens corresponding to the specified authorization identifier.
/// </summary>
private static readonly Func<TContext, TKey, IAsyncEnumerable<TToken>> FindByAuthorizationId =
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, TKey, IAsyncEnumerable<TToken>>
#else
Func<TContext, TKey, AsyncEnumerable<TToken>>
#endif
FindByAuthorizationId =
// Note: due to a bug in Entity Framework Core's query visitor, the tokens can't be
// filtered using token.Authorization.Id.Equals(key). To work around this issue,
// this compiled query uses an explicit join before applying the equality check.
@ -409,7 +460,11 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
return FindByAuthorizationId(Context, ConvertIdentifierFromString(identifier));
return FindByAuthorizationId(Context, ConvertIdentifierFromString(identifier))
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
;
}
/// <summary>
@ -480,8 +535,13 @@ namespace OpenIddict.EntityFrameworkCore
/// Exposes a compiled query allowing to retrieve the
/// list of tokens corresponding to the specified subject.
/// </summary>
private static readonly Func<TContext, string, IAsyncEnumerable<TToken>> FindBySubject =
EF.CompileAsyncQuery((TContext context, string subject) =>
private static readonly
#if SUPPORTS_BCL_ASYNC_ENUMERABLE
Func<TContext, string, IAsyncEnumerable<TToken>>
#else
Func<TContext, string, AsyncEnumerable<TToken>>
#endif
FindBySubject = EF.CompileAsyncQuery((TContext context, string subject) =>
from token in context.Set<TToken>()
.Include(token => token.Application)
.Include(token => token.Authorization)
@ -502,7 +562,11 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
}
return FindBySubject(Context, subject);
return FindBySubject(Context, subject)
#if !SUPPORTS_BCL_ASYNC_ENUMERABLE
.AsAsyncEnumerable(cancellationToken)
#endif
;
}
/// <summary>

3
test/OpenIddict.EntityFrameworkCore.Tests/OpenIddict.EntityFrameworkCore.Tests.csproj

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.0;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>

Loading…
Cancel
Save