Post merge fixes - Including temp workaround for .net8 efcore breaking change (Should be fixed again in preview 7)

This commit is contained in:
Bjarke Berg
2023-07-07 10:55:29 +02:00
parent d991285466
commit 47d5ef88c4
13 changed files with 419 additions and 22 deletions

View File

@@ -7,11 +7,14 @@
<AssemblyName>Umbraco.Cms.Api.Common</AssemblyName>
<RootNamespace>Umbraco.Cms.Api.Common</RootNamespace>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<!-- <PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.8" />-->
<PackageReference Include="Asp.Versioning.Mvc" Version="7.0.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.8" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="OpenIddict.Abstractions" Version="4.5.0" />
<PackageReference Include="OpenIddict.AspNetCore" Version="4.5.0" />

View File

@@ -10,7 +10,6 @@
<ItemGroup>
<PackageReference Include="JsonPatch.Net" Version="2.0.4" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="4.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

View File

@@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0-preview.5.*" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0-preview.5.23280.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,4 +1,14 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.EntityFrameworkCore;
using OpenIddict.EntityFrameworkCore.Models;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
@@ -23,7 +33,7 @@ public class UmbracoEFCoreComposer : IComposer
.AddCore(options =>
{
options
.UseEntityFrameworkCore()
.UseCustomEntityFrameworkCore() // TODO Revert to this after .NET 8 Preview 7: .UseEntityFrameworkCore()
.UseDbContext<UmbracoDbContext>();
});
}
@@ -54,3 +64,394 @@ public class EFCoreCreateTablesNotificationHandler : INotificationAsyncHandler<D
await _iefCoreMigrationExecutor.ExecuteAllMigrationsAsync();
}
}
// TODO Revert to this after .NET 8 Preview 7
internal class MyOpenIddictAuthorizationStore<TAuthorization, TApplication, TToken, TContext, TKey> : OpenIddictEntityFrameworkCoreAuthorizationStore<TAuthorization, TApplication, TToken, TContext, TKey>, IOpenIddictAuthorizationStore<TAuthorization>
where TAuthorization : OpenIddictEntityFrameworkCoreAuthorization<TKey, TApplication, TToken>
where TApplication : OpenIddictEntityFrameworkCoreApplication<TKey, TAuthorization, TToken>
where TToken : OpenIddictEntityFrameworkCoreToken<TKey, TApplication, TAuthorization>
where TContext : DbContext
where TKey : notnull, IEquatable<TKey>
{
public MyOpenIddictAuthorizationStore(
IMemoryCache cache,
TContext context,
IOptionsMonitor<OpenIddictEntityFrameworkCoreOptions> options)
: base(cache, context, options)
{
}
private DbSet<TApplication> Applications => Context.Set<TApplication>();
public override async ValueTask<string?> GetApplicationIdAsync(TAuthorization authorization, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(authorization);
// If the application is not attached to the authorization, try to load it manually.
if (authorization.Application is null)
{
var reference = Context.Entry(authorization).Reference(entry => entry.Application);
if (reference.EntityEntry.State is EntityState.Detached)
{
return null;
}
await reference.LoadAsync(cancellationToken: cancellationToken);
}
if (authorization.Application is null)
{
return null;
}
return ConvertIdentifierToString(authorization.Application.Id);
}
public override async ValueTask SetApplicationIdAsync(TAuthorization authorization, string? identifier, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(authorization);
if (!string.IsNullOrEmpty(identifier))
{
var key = ConvertIdentifierFromString(identifier);
authorization.Application = await Applications.AsQueryable()
.AsTracking()
.FirstOrDefaultAsync(application => application.Id!.Equals(key), cancellationToken) ??
throw new InvalidOperationException();
}
else
{
// If the application is not attached to the authorization, try to load it manually.
if (authorization.Application is null)
{
var reference = Context.Entry(authorization).Reference(entry => entry.Application);
if (reference.EntityEntry.State is EntityState.Detached)
{
return;
}
await reference.LoadAsync(cancellationToken: cancellationToken);
}
authorization.Application = null;
}
}
}
// TODO Revert to this after .NET 8 Preview 7
internal class MyOpenIddictAuthorizationStoreResolver : IOpenIddictAuthorizationStoreResolver
{
private readonly TypeResolutionCache _cache;
private readonly IOptionsMonitor<OpenIddictEntityFrameworkCoreOptions> _options;
private readonly IServiceProvider _provider;
public MyOpenIddictAuthorizationStoreResolver(
TypeResolutionCache cache,
IOptionsMonitor<OpenIddictEntityFrameworkCoreOptions> options,
IServiceProvider provider)
{
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
_options = options ?? throw new ArgumentNullException(nameof(options));
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
}
public IOpenIddictAuthorizationStore<TAuthorization> Get<TAuthorization>() where TAuthorization : class
{
var store = _provider.GetService<IOpenIddictAuthorizationStore<TAuthorization>>();
if (store is not null)
{
return store;
}
var type = _cache.GetOrAdd(typeof(TAuthorization), key =>
{
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictEntityFrameworkCoreAuthorization<,,>)) ??
throw new InvalidOperationException();
var context = _options.CurrentValue.DbContextType ??
throw new InvalidOperationException();
return typeof(MyOpenIddictAuthorizationStore<,,,,>).MakeGenericType(
/* TAuthorization: */ key,
/* TApplication: */ root.GenericTypeArguments[1],
/* TToken: */ root.GenericTypeArguments[2],
/* TContext: */ context,
/* TKey: */ root.GenericTypeArguments[0]);
});
return (IOpenIddictAuthorizationStore<TAuthorization>)_provider.GetRequiredService(type);
}
[SuppressMessage("Design", "CA1034:Nested types should not be visible")]
public sealed class TypeResolutionCache : ConcurrentDictionary<Type, Type> { }
}
// TODO Revert to this after .NET 8 Preview 7
internal class MyOpenIddictTokenStore<TToken, TApplication, TAuthorization, TContext, TKey> : OpenIddictEntityFrameworkCoreTokenStore<TToken, TApplication, TAuthorization, TContext, TKey>, IOpenIddictTokenStore<TToken>
where TToken : OpenIddictEntityFrameworkCoreToken<TKey, TApplication, TAuthorization>
where TApplication : OpenIddictEntityFrameworkCoreApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictEntityFrameworkCoreAuthorization<TKey, TApplication, TToken>
where TContext : DbContext
where TKey : notnull, IEquatable<TKey>
{
public MyOpenIddictTokenStore(
IMemoryCache cache,
TContext context,
IOptionsMonitor<OpenIddictEntityFrameworkCoreOptions> options)
: base(cache, context, options)
{
}
private DbSet<TApplication> Applications => Context.Set<TApplication>();
private DbSet<TAuthorization> Authorizations => Context.Set<TAuthorization>();
public override async ValueTask<string?> GetApplicationIdAsync(TToken token, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(token);
// If the application is not attached to the token, try to load it manually.
if (token.Application is null)
{
var reference = Context.Entry(token).Reference(entry => entry.Application);
if (reference.EntityEntry.State is EntityState.Detached)
{
return null;
}
await reference.LoadAsync(cancellationToken: cancellationToken);
}
if (token.Application is null)
{
return null;
}
return ConvertIdentifierToString(token.Application.Id);
}
public override async ValueTask<string?> GetAuthorizationIdAsync(TToken token, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(token);
// If the authorization is not attached to the token, try to load it manually.
if (token.Authorization is null)
{
var reference = Context.Entry(token).Reference(entry => entry.Authorization);
if (reference.EntityEntry.State is EntityState.Detached)
{
return null;
}
await reference.LoadAsync(cancellationToken: cancellationToken);
}
if (token.Authorization is null)
{
return null;
}
return ConvertIdentifierToString(token.Authorization.Id);
}
public override async ValueTask SetApplicationIdAsync(TToken token, string? identifier, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(token);
if (!string.IsNullOrEmpty(identifier))
{
var key = ConvertIdentifierFromString(identifier);
// Warning: FindAsync() is deliberately not used to work around a breaking change introduced
// in Entity Framework Core 3.x (where a ValueTask instead of a Task is now returned).
token.Application = await Applications.AsQueryable()
.AsTracking()
.FirstOrDefaultAsync(application => application.Id!.Equals(key), cancellationToken) ??
throw new InvalidOperationException();
}
else
{
// If the application is not attached to the token, try to load it manually.
if (token.Application is null)
{
var reference = Context.Entry(token).Reference(entry => entry.Application);
if (reference.EntityEntry.State is EntityState.Detached)
{
return;
}
await reference.LoadAsync(cancellationToken: cancellationToken);
}
token.Application = null;
}
}
public override async ValueTask SetAuthorizationIdAsync(TToken token, string? identifier, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(token);
if (!string.IsNullOrEmpty(identifier))
{
var key = ConvertIdentifierFromString(identifier);
// Warning: FindAsync() is deliberately not used to work around a breaking change introduced
// in Entity Framework Core 3.x (where a ValueTask instead of a Task is now returned).
token.Authorization = await Authorizations.AsQueryable()
.AsTracking()
.FirstOrDefaultAsync(authorization => authorization.Id!.Equals(key), cancellationToken) ??
throw new InvalidOperationException();
}
else
{
// If the authorization is not attached to the token, try to load it manually.
if (token.Authorization is null)
{
var reference = Context.Entry(token).Reference(entry => entry.Authorization);
if (reference.EntityEntry.State is EntityState.Detached)
{
return;
}
await reference.LoadAsync(cancellationToken: cancellationToken);
}
token.Authorization = null;
}
}
}
// TODO Revert to this after .NET 8 Preview 7
internal class MyOpenIddictTokenStoreResolver : IOpenIddictTokenStoreResolver
{
private readonly TypeResolutionCache _cache;
private readonly IOptionsMonitor<OpenIddictEntityFrameworkCoreOptions> _options;
private readonly IServiceProvider _provider;
public MyOpenIddictTokenStoreResolver(
TypeResolutionCache cache,
IOptionsMonitor<OpenIddictEntityFrameworkCoreOptions> options,
IServiceProvider provider)
{
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
_options = options ?? throw new ArgumentNullException(nameof(options));
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
}
public IOpenIddictTokenStore<TToken> Get<TToken>() where TToken : class
{
var store = _provider.GetService<IOpenIddictTokenStore<TToken>>();
if (store is not null)
{
return store;
}
var type = _cache.GetOrAdd(typeof(TToken), key =>
{
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictEntityFrameworkCoreToken<,,>)) ??
throw new InvalidOperationException();
var context = _options.CurrentValue.DbContextType ??
throw new InvalidOperationException();
return typeof(MyOpenIddictTokenStore<,,,,>).MakeGenericType(
/* TToken: */ key,
/* TApplication: */ root.GenericTypeArguments[1],
/* TAuthorization: */ root.GenericTypeArguments[2],
/* TContext: */ context,
/* TKey: */ root.GenericTypeArguments[0]);
});
return (IOpenIddictTokenStore<TToken>)_provider.GetRequiredService(type);
}
[SuppressMessage("Design", "CA1034:Nested types should not be visible")]
public sealed class TypeResolutionCache : ConcurrentDictionary<Type, Type> { }
}
// TODO Revert to this after .NET 8 Preview 7
internal static class OpenIddictHelpers
{
public static Type FindGenericBaseType(Type type, Type definition)
=> FindGenericBaseTypes(type, definition).FirstOrDefault()!;
public static IEnumerable<Type> FindGenericBaseTypes(Type type, Type definition)
{
ArgumentNullException.ThrowIfNull(type);
ArgumentNullException.ThrowIfNull(definition);
if (!definition.IsGenericTypeDefinition)
{
throw new ArgumentException(null, nameof(definition));
}
if (definition.IsInterface)
{
foreach (var contract in type.GetInterfaces())
{
if (!contract.IsGenericType && !contract.IsConstructedGenericType)
{
continue;
}
if (contract.GetGenericTypeDefinition() == definition)
{
yield return contract;
}
}
}
else
{
for (var candidate = type; candidate is not null; candidate = candidate.BaseType)
{
if (!candidate.IsGenericType && !candidate.IsConstructedGenericType)
{
continue;
}
if (candidate.GetGenericTypeDefinition() == definition)
{
yield return candidate;
}
}
}
}
}
// TODO Revert to this after .NET 8 Preview 7
internal static class OpenIddictExtension
{
public static OpenIddictEntityFrameworkCoreBuilder UseCustomEntityFrameworkCore(this OpenIddictCoreBuilder builder)
{
ArgumentNullException.ThrowIfNull(builder);
// Since Entity Framework Core may be used with databases performing case-insensitive
// or culture-sensitive comparisons, ensure the additional filtering logic is enforced
// in case case-sensitive stores were registered before this extension was called.
builder.Configure(options => options.DisableAdditionalFiltering = false);
builder.SetDefaultApplicationEntity<OpenIddictEntityFrameworkCoreApplication>()
.SetDefaultAuthorizationEntity<OpenIddictEntityFrameworkCoreAuthorization>()
.SetDefaultScopeEntity<OpenIddictEntityFrameworkCoreScope>()
.SetDefaultTokenEntity<OpenIddictEntityFrameworkCoreToken>();
builder.ReplaceApplicationStoreResolver<OpenIddictEntityFrameworkCoreApplicationStoreResolver>()
.ReplaceAuthorizationStoreResolver<MyOpenIddictAuthorizationStoreResolver>()
.ReplaceScopeStoreResolver<OpenIddictEntityFrameworkCoreScopeStoreResolver>()
.ReplaceTokenStoreResolver<MyOpenIddictTokenStoreResolver>();
builder.Services.TryAddSingleton<OpenIddictEntityFrameworkCoreApplicationStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<MyOpenIddictAuthorizationStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<OpenIddictEntityFrameworkCoreScopeStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<MyOpenIddictTokenStoreResolver.TypeResolutionCache>();
builder.Services.TryAddScoped(typeof(OpenIddictEntityFrameworkCoreApplicationStore<,,,,>));
builder.Services.TryAddScoped(typeof(MyOpenIddictAuthorizationStore<,,,,>));
builder.Services.TryAddScoped(typeof(OpenIddictEntityFrameworkCoreScopeStore<,,>));
builder.Services.TryAddScoped(typeof(MyOpenIddictTokenStore<,,,,>));
return new OpenIddictEntityFrameworkCoreBuilder(builder.Services);
}
}

View File

@@ -48,9 +48,6 @@ public static class UmbracoEFCoreServiceCollectionExtensions
private static ConnectionStrings GetConnectionStringAndProviderName(IServiceProvider serviceProvider)
{
string? connectionString = null;
string? providerName = null;
ConnectionStrings connectionStrings = serviceProvider.GetRequiredService<IOptionsMonitor<ConnectionStrings>>().CurrentValue;
// Replace data directory

View File

@@ -8,7 +8,6 @@
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0-preview.5.*" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0-preview.5.*" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0-preview.5.*" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="4.5.0" />
</ItemGroup>

View File

@@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.0-preview.5.*" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.0-preview.5.23280.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -24,7 +24,7 @@
<ItemGroup>
<!-- Update implicit dependencies to fix security issues, even though we do not use them explicitly and they are taken from the shared framework instead of NuGet -->
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="7.0.1" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="8.0.0-preview.5.*" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
</ItemGroup>

View File

@@ -26,7 +26,6 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NPoco" Version="5.7.1" />
<PackageReference Include="OpenIddict.Abstractions" Version="4.2.0" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog" Version="3.0.0" />
<PackageReference Include="Serilog.Enrichers.Process" Version="2.0.2" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />

View File

@@ -11,7 +11,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.7">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0-preview.5.23280.1">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

View File

@@ -24,7 +24,6 @@
<AdditionalFiles Include="umbraco\UmbracoWebsite\Maintenance.cshtml" />
<AdditionalFiles Include="umbraco\UmbracoWebsite\NoNodes.cshtml" />
<AdditionalFiles Include="umbraco\UmbracoWebsite\NotFound.cshtml" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.7">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0-preview.5.*">
<PrivateAssets>all</PrivateAssets>
</PackageReference>

View File

@@ -29,13 +29,13 @@
"datatype": "choice",
"choices": [
{
"displayName": ".NET 7.0",
"description": "Target net7.0",
"choice": "net7.0"
"displayName": ".NET 8.0",
"description": "Target net8.0",
"choice": "net8.0"
}
],
"defaultValue": "net7.0",
"replaces": "net7.0"
"defaultValue": "net8.0",
"replaces": "net8.0"
},
"UmbracoVersion": {
"displayName": "Umbraco version",

View File

@@ -10,10 +10,10 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="AngleSharp" Version="1.0.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="System.Data.Odbc" Version="7.0.0" />
<PackageReference Include="System.Data.OleDb" Version="7.0.0" />
<PackageReference Include="System.Data.Odbc" Version="8.0.0-preview.5.*" />
<PackageReference Include="System.Data.OleDb" Version="8.0.0-preview.5.*" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Umbraco.Cms.Api.Delivery\Umbraco.Cms.Api.Delivery.csproj" />
<ProjectReference Include="..\..\src\Umbraco.Cms.Persistence.Sqlite\Umbraco.Cms.Persistence.Sqlite.csproj" />