diff --git a/Directory.Build.props b/Directory.Build.props index 4e8f6cd101..5fddbdf8ab 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -52,11 +52,6 @@ $(NoWarn);CA1062 - - - $(NoWarn);SYSLIB1006 - - diff --git a/JsonApiDotNetCore.sln.DotSettings b/JsonApiDotNetCore.sln.DotSettings index 5878341db6..7151bb82c8 100644 --- a/JsonApiDotNetCore.sln.DotSettings +++ b/JsonApiDotNetCore.sln.DotSettings @@ -14,6 +14,7 @@ JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$); 3000 50 False + 83FF097C-C8C6-477B-9FAB-DF99B84978B5/f:ReadOnlySet.cs SOLUTION True True diff --git a/README.md b/README.md index 3354eac45b..8bd84b3d9c 100644 --- a/README.md +++ b/README.md @@ -87,13 +87,9 @@ See also our [versioning policy](./VERSIONING_POLICY.md). | | | 7 | 7 | | | | 8 | 8, 9 | | | | 9 | 9 | -| master | Preview | 6 | 6, 7 | -| | | 7 | 7 | -| | | 8 | 8, 9 | +| master | Preview | 8 | 8, 9 | | | | 9 | 9 | -| openapi | Experimental | 6 | 6, 7 | -| | | 7 | 7 | -| | | 8 | 8, 9 | +| openapi | Experimental | 8 | 8, 9 | | | | 9 | 9 | ## Contributing diff --git a/package-versions.props b/package-versions.props index b5d2f30cb3..00e8462550 100644 --- a/package-versions.props +++ b/package-versions.props @@ -15,6 +15,7 @@ 2.4.* 2.0.* 8.0.* + 9.0.* 17.11.* 2.9.* 2.8.* @@ -28,7 +29,6 @@ 9.0.* 9.0.* 9.0.0-* - $(AspNetCoreVersion) @@ -39,18 +39,5 @@ 8.0.* 8.0.* $(EntityFrameworkCoreVersion) - $(AspNetCoreVersion) - - - - - 6.0.0 - - - 6.0.* - 2.1.* - 7.0.* - $(EntityFrameworkCoreVersion) - 8.0.* diff --git a/src/Examples/DapperExample/DapperExample.csproj b/src/Examples/DapperExample/DapperExample.csproj index 2d3fad689d..ed7bd358eb 100644 --- a/src/Examples/DapperExample/DapperExample.csproj +++ b/src/Examples/DapperExample/DapperExample.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/src/Examples/DapperExample/Program.cs b/src/Examples/DapperExample/Program.cs index 31e5814e5b..f68d480564 100644 --- a/src/Examples/DapperExample/Program.cs +++ b/src/Examples/DapperExample/Program.cs @@ -31,10 +31,10 @@ } case DatabaseProvider.MySql: { -#if NET9_0_OR_GREATER - ServerVersion serverVersion = await ServerVersion.AutoDetectAsync(connectionString); -#else +#if NET8_0 ServerVersion serverVersion = ServerVersion.AutoDetect(connectionString); +#else + ServerVersion serverVersion = await ServerVersion.AutoDetectAsync(connectionString); #endif builder.Services.AddMySql(connectionString, serverVersion, optionsAction: options => SetDbContextDebugOptions(options)); diff --git a/src/Examples/DapperExample/Repositories/ResultSetMapper.cs b/src/Examples/DapperExample/Repositories/ResultSetMapper.cs index 1b20f74dd9..89a7890fd0 100644 --- a/src/Examples/DapperExample/Repositories/ResultSetMapper.cs +++ b/src/Examples/DapperExample/Repositories/ResultSetMapper.cs @@ -3,9 +3,6 @@ using JsonApiDotNetCore.Queries.Expressions; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -#if NET6_0 -using JsonApiDotNetCore; -#endif namespace DapperExample.Repositories; diff --git a/src/Examples/DatabasePerTenantExample/DatabasePerTenantExample.csproj b/src/Examples/DatabasePerTenantExample/DatabasePerTenantExample.csproj index a22b938ba0..3edc993428 100644 --- a/src/Examples/DatabasePerTenantExample/DatabasePerTenantExample.csproj +++ b/src/Examples/DatabasePerTenantExample/DatabasePerTenantExample.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/src/Examples/GettingStarted/GettingStarted.csproj b/src/Examples/GettingStarted/GettingStarted.csproj index 22fc0529b1..611aeb37a5 100644 --- a/src/Examples/GettingStarted/GettingStarted.csproj +++ b/src/Examples/GettingStarted/GettingStarted.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoItemDefinition.cs b/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoItemDefinition.cs index cd85ccd696..aab9369618 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoItemDefinition.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoItemDefinition.cs @@ -5,28 +5,14 @@ using JsonApiDotNetCore.Queries.Expressions; using JsonApiDotNetCore.Resources; using JsonApiDotNetCoreExample.Models; -#if NET6_0 -using Microsoft.AspNetCore.Authentication; -#endif namespace JsonApiDotNetCoreExample.Definitions; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class TodoItemDefinition( - IResourceGraph resourceGraph, -#if NET6_0 - ISystemClock systemClock -#else - TimeProvider timeProvider -#endif -) +public sealed class TodoItemDefinition(IResourceGraph resourceGraph, TimeProvider timeProvider) : JsonApiResourceDefinition(resourceGraph) { -#if NET6_0 - private readonly Func _getUtcNow = () => systemClock.UtcNow; -#else private readonly Func _getUtcNow = timeProvider.GetUtcNow; -#endif public override SortExpression OnApplySort(SortExpression? existingSort) { diff --git a/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj b/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj index a22b938ba0..3edc993428 100644 --- a/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj +++ b/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/src/Examples/JsonApiDotNetCoreExample/Program.cs b/src/Examples/JsonApiDotNetCoreExample/Program.cs index 4c11a71660..2cfa1e640d 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Program.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Program.cs @@ -8,9 +8,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.DependencyInjection.Extensions; -#if NET6_0 -using Microsoft.AspNetCore.Authentication; -#endif [assembly: ExcludeFromCodeCoverage] @@ -48,11 +45,7 @@ static void ConfigureServices(WebApplicationBuilder builder) { using IDisposable _ = CodeTimingSessionManager.Current.Measure("Configure services"); -#if NET6_0 - builder.Services.TryAddSingleton(); -#else builder.Services.TryAddSingleton(TimeProvider.System); -#endif builder.Services.AddDbContext(options => { diff --git a/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj b/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj index 22fc0529b1..611aeb37a5 100644 --- a/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj +++ b/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj b/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj index c45552dc2d..15a485c08f 100644 --- a/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj +++ b/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs b/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs index 9eba0b8326..4feb370858 100644 --- a/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs +++ b/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs @@ -32,11 +32,7 @@ public Task> GetAsync(QueryLayer queryLayer, Canc IEnumerable dataSource = GetDataSource(); IEnumerable resources = _queryLayerToLinqConverter.ApplyQueryLayer(queryLayer, dataSource); -#if NET6_0 - return Task.FromResult>(Array.AsReadOnly(resources.ToArray())); -#else return Task.FromResult>(resources.ToArray().AsReadOnly()); -#endif } /// diff --git a/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs b/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs index e55b9340b6..0dcc5d9905 100644 --- a/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs +++ b/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs @@ -65,11 +65,7 @@ public Task> GetAsync(CancellationToken cancellat _paginationContext.IsPageFull = true; } -#if NET6_0 - return Task.FromResult>(Array.AsReadOnly(resources)); -#else return Task.FromResult>(resources.AsReadOnly()); -#endif } private void LogFiltersInTopScope() diff --git a/src/Examples/ReportsExample/ReportsExample.csproj b/src/Examples/ReportsExample/ReportsExample.csproj index 3f2c288b23..6ade1386be 100644 --- a/src/Examples/ReportsExample/ReportsExample.csproj +++ b/src/Examples/ReportsExample/ReportsExample.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/src/JsonApiDotNetCore.Annotations/ArgumentGuard.cs b/src/JsonApiDotNetCore.Annotations/ArgumentGuard.cs index e01b80d776..137654fb3e 100644 --- a/src/JsonApiDotNetCore.Annotations/ArgumentGuard.cs +++ b/src/JsonApiDotNetCore.Annotations/ArgumentGuard.cs @@ -30,30 +30,12 @@ public static void NotNullNorEmpty([SysNotNull] IEnumerable? value, [Calle [AssertionMethod] public static void NotNullNorEmpty([SysNotNull] string? value, [CallerArgumentExpression(nameof(value))] string? parameterName = null) { -#if !NET6_0 ArgumentException.ThrowIfNullOrEmpty(value, parameterName); -#else - ArgumentNullException.ThrowIfNull(value, parameterName); - - if (value.Length == 0) - { - throw new ArgumentException("String cannot be null or empty.", parameterName); - } -#endif } [AssertionMethod] public static void NotNullNorWhitespace([SysNotNull] string? value, [CallerArgumentExpression(nameof(value))] string? parameterName = null) { -#if !NET6_0 ArgumentException.ThrowIfNullOrWhiteSpace(value, parameterName); -#else - ArgumentNullException.ThrowIfNull(value, parameterName); - - if (string.IsNullOrWhiteSpace(value)) - { - throw new ArgumentException("String cannot be null, empty, or whitespace.", parameterName); - } -#endif } } diff --git a/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj b/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj index 04238621da..09968a0922 100644 --- a/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj +++ b/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj @@ -1,6 +1,6 @@ - net8.0;net6.0;netstandard1.0 + net8.0;netstandard1.0 true true JsonApiDotNetCore diff --git a/src/JsonApiDotNetCore.Annotations/PolyfillCollectionExtensions.cs b/src/JsonApiDotNetCore.Annotations/PolyfillCollectionExtensions.cs index 72578e5db2..efc51f4f17 100644 --- a/src/JsonApiDotNetCore.Annotations/PolyfillCollectionExtensions.cs +++ b/src/JsonApiDotNetCore.Annotations/PolyfillCollectionExtensions.cs @@ -1,10 +1,4 @@ -#if NET6_0 using System.Collections.ObjectModel; -#endif - -#if NET6_0 -#pragma warning disable AV1130 // Return type in method signature should be an interface to an unchangeable collection -#endif namespace JsonApiDotNetCore; @@ -13,22 +7,6 @@ internal static class PolyfillCollectionExtensions { public static IReadOnlySet AsReadOnly(this HashSet source) { - // We can't use ReadOnlySet yet, which is being introduced in .NET 9. - return source; - } - -#if NET6_0 - public static ReadOnlyDictionary AsReadOnly(this IDictionary source) - where TKey : notnull - { - // The AsReadOnly() extension method is unavailable in .NET 6. - return new ReadOnlyDictionary(source); - } - - public static ReadOnlyCollection AsReadOnly(this T[] source) - { - // The AsReadOnly() extension method is unavailable in .NET 6. - return Array.AsReadOnly(source); + return new ReadOnlySet(source); } -#endif } diff --git a/src/JsonApiDotNetCore.Annotations/ReadOnlySet.cs b/src/JsonApiDotNetCore.Annotations/ReadOnlySet.cs new file mode 100644 index 0000000000..1dee77aba9 --- /dev/null +++ b/src/JsonApiDotNetCore.Annotations/ReadOnlySet.cs @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if NET8_0 +#pragma warning disable + +// ReadOnlySet was introduced in .NET 9. +// This file was copied from https://github.com/dotnet/runtime/blob/release/9.0/src/libraries/System.Collections/src/System/Collections/Generic/ReadOnlySet.cs +// to enable usage on lower .NET versions. + +using System.Diagnostics; + +namespace System.Collections.ObjectModel; + +/// Represents a read-only, generic set of values. +/// The type of values in the set. +[DebuggerDisplay("Count = {Count}")] +public class ReadOnlySet : IReadOnlySet, ISet, ICollection +{ + /// The wrapped set. + private readonly ISet _set; + + /// Initializes a new instance of the class that is a wrapper around the specified set. + /// The set to wrap. + public ReadOnlySet(ISet set) + { + ArgumentNullException.ThrowIfNull(set); + _set = set; + } + + /// Gets an empty . + public static ReadOnlySet Empty { get; } = new ReadOnlySet(new HashSet()); + + /// Gets the set that is wrapped by this object. + protected ISet Set => _set; + + /// + public int Count => _set.Count; + + /// + public IEnumerator GetEnumerator() => + _set.Count == 0 ? ((IEnumerable)Array.Empty()).GetEnumerator() : + _set.GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + public bool Contains(T item) => _set.Contains(item); + + /// + public bool IsProperSubsetOf(IEnumerable other) => _set.IsProperSubsetOf(other); + + /// + public bool IsProperSupersetOf(IEnumerable other) => _set.IsProperSupersetOf(other); + + /// + public bool IsSubsetOf(IEnumerable other) => _set.IsSubsetOf(other); + + /// + public bool IsSupersetOf(IEnumerable other) => _set.IsSupersetOf(other); + + /// + public bool Overlaps(IEnumerable other) => _set.Overlaps(other); + + /// + public bool SetEquals(IEnumerable other) => _set.SetEquals(other); + + /// + void ICollection.CopyTo(T[] array, int arrayIndex) => _set.CopyTo(array, arrayIndex); + + /// + void ICollection.CopyTo(Array array, int index) => CollectionHelpers.CopyTo(_set, array, index); + + /// + bool ICollection.IsReadOnly => true; + + /// + bool ICollection.IsSynchronized => false; + + /// + object ICollection.SyncRoot => _set is ICollection c ? c.SyncRoot : this; + + /// + bool ISet.Add(T item) => throw new NotSupportedException(); + + /// + void ISet.ExceptWith(IEnumerable other) => throw new NotSupportedException(); + + /// + void ISet.IntersectWith(IEnumerable other) => throw new NotSupportedException(); + + /// + void ISet.SymmetricExceptWith(IEnumerable other) => throw new NotSupportedException(); + + /// + void ISet.UnionWith(IEnumerable other) => throw new NotSupportedException(); + + /// + void ICollection.Add(T item) => throw new NotSupportedException(); + + /// + void ICollection.Clear() => throw new NotSupportedException(); + + /// + bool ICollection.Remove(T item) => throw new NotSupportedException(); + + private static class CollectionHelpers + { + private static void ValidateCopyToArguments(int sourceCount, Array array, int index) + { + ArgumentNullException.ThrowIfNull(array); + + if (array.Rank != 1) + { + throw new ArgumentException("Only single dimensional arrays are supported for the requested action.", nameof(array)); + } + + if (array.GetLowerBound(0) != 0) + { + throw new ArgumentException("The lower bound of target array must be zero.", nameof(array)); + } + + ArgumentOutOfRangeException.ThrowIfNegative(index); + ArgumentOutOfRangeException.ThrowIfGreaterThan(index, array.Length); + + if (array.Length - index < sourceCount) + { + throw new ArgumentException("Destination array is not long enough to copy all the items in the collection. Check array index and length."); + } + } + + internal static void CopyTo(ICollection collection, Array array, int index) + { + ValidateCopyToArguments(collection.Count, array, index); + + if (collection is ICollection nonGenericCollection) + { + // Easy out if the ICollection implements the non-generic ICollection + nonGenericCollection.CopyTo(array, index); + } + else if (array is T[] items) + { + collection.CopyTo(items, index); + } + else + { + // We can't cast array of value type to object[], so we don't support widening of primitive types here. + if (array is not object?[] objects) + { + throw new ArgumentException("Target array type is not compatible with the type of items in the collection.", nameof(array)); + } + + try + { + foreach (T item in collection) + { + objects[index++] = item; + } + } + catch (ArrayTypeMismatchException) + { + throw new ArgumentException("Target array type is not compatible with the type of items in the collection.", nameof(array)); + } + } + } + } +} +#endif diff --git a/src/JsonApiDotNetCore/Configuration/PageNumber.cs b/src/JsonApiDotNetCore/Configuration/PageNumber.cs index 44732fc404..f4af725a3d 100644 --- a/src/JsonApiDotNetCore/Configuration/PageNumber.cs +++ b/src/JsonApiDotNetCore/Configuration/PageNumber.cs @@ -11,14 +11,7 @@ public sealed class PageNumber : IEquatable public PageNumber(int oneBasedValue) { -#if NET6_0 - if (oneBasedValue < 1) - { - throw new ArgumentOutOfRangeException(nameof(oneBasedValue)); - } -#else ArgumentOutOfRangeException.ThrowIfLessThan(oneBasedValue, 1); -#endif OneBasedValue = oneBasedValue; } diff --git a/src/JsonApiDotNetCore/Configuration/PageSize.cs b/src/JsonApiDotNetCore/Configuration/PageSize.cs index 46beb1419f..4581992597 100644 --- a/src/JsonApiDotNetCore/Configuration/PageSize.cs +++ b/src/JsonApiDotNetCore/Configuration/PageSize.cs @@ -9,14 +9,7 @@ public sealed class PageSize : IEquatable public PageSize(int value) { -#if NET6_0 - if (value < 1) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } -#else ArgumentOutOfRangeException.ThrowIfLessThan(value, 1); -#endif Value = value; } diff --git a/src/JsonApiDotNetCore/Diagnostics/DefaultCodeTimerSession.cs b/src/JsonApiDotNetCore/Diagnostics/DefaultCodeTimerSession.cs index 5737dbe3f2..df782bb8c0 100644 --- a/src/JsonApiDotNetCore/Diagnostics/DefaultCodeTimerSession.cs +++ b/src/JsonApiDotNetCore/Diagnostics/DefaultCodeTimerSession.cs @@ -27,14 +27,7 @@ public DefaultCodeTimerSession() private void AssertNotDisposed() { -#if NET6_0 - if (_codeTimerInContext.Value == null) - { - throw new ObjectDisposedException(nameof(DefaultCodeTimerSession)); - } -#else ObjectDisposedException.ThrowIf(_codeTimerInContext.Value == null, this); -#endif } public void Dispose() diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj index 0f395511a7..b30f89cb9f 100644 --- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj +++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj @@ -1,6 +1,6 @@ - net8.0;net6.0 + net8.0 true true diff --git a/src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs b/src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs index 1804027de4..0c704d0013 100644 --- a/src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs +++ b/src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs @@ -135,11 +135,7 @@ public QueryLayer ComposeFromConstraints(ResourceType requestResourceType) { ArgumentGuard.NotNull(requestResourceType); -#if NET6_0 - ImmutableArray constraints = _constraintProviders.SelectMany(provider => provider.GetConstraints()).ToImmutableArray(); -#else ImmutableArray constraints = [.. _constraintProviders.SelectMany(provider => provider.GetConstraints())]; -#endif QueryLayer topLayer = ComposeTopLayer(constraints, requestResourceType); topLayer.Include = ComposeChildren(topLayer, constraints); diff --git a/test/AnnotationTests/AnnotationTests.csproj b/test/AnnotationTests/AnnotationTests.csproj index 7c5e5f3ae0..885e9f769c 100644 --- a/test/AnnotationTests/AnnotationTests.csproj +++ b/test/AnnotationTests/AnnotationTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0;netstandard2.0 + net9.0;net8.0;netstandard2.0 diff --git a/test/DapperTests/DapperTests.csproj b/test/DapperTests/DapperTests.csproj index 1420e0dd60..7d41d78911 100644 --- a/test/DapperTests/DapperTests.csproj +++ b/test/DapperTests/DapperTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/test/DapperTests/IntegrationTests/DapperTestContext.cs b/test/DapperTests/IntegrationTests/DapperTestContext.cs index 9fdc330c74..01747dde1d 100644 --- a/test/DapperTests/IntegrationTests/DapperTestContext.cs +++ b/test/DapperTests/IntegrationTests/DapperTestContext.cs @@ -122,14 +122,10 @@ public async Task ClearAllTablesAsync(DbContext dbContext) _ => throw new NotSupportedException($"Unsupported database provider '{databaseProvider}'.") }; -#if !NET6_0 #pragma warning disable EF1002 // Risk of vulnerability to SQL injection. -#endif // Justification: Table names cannot be parameterized. await dbContext.Database.ExecuteSqlRawAsync($"DELETE FROM {escapedTableName}"); -#if !NET6_0 #pragma warning restore EF1002 // Risk of vulnerability to SQL injection. -#endif } } } diff --git a/test/DapperTests/IntegrationTests/SqlTextAdapter.cs b/test/DapperTests/IntegrationTests/SqlTextAdapter.cs index 3bdeab115f..1d6d45555e 100644 --- a/test/DapperTests/IntegrationTests/SqlTextAdapter.cs +++ b/test/DapperTests/IntegrationTests/SqlTextAdapter.cs @@ -5,11 +5,7 @@ namespace DapperTests.IntegrationTests; internal sealed class SqlTextAdapter(DatabaseProvider databaseProvider) { -#if NET6_0 - private const RegexOptions Options = RegexOptions.Compiled | RegexOptions.CultureInvariant; -#else private const RegexOptions Options = RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.NonBacktracking; -#endif private static readonly Dictionary SqlServerReplacements = new() { diff --git a/test/DiscoveryTests/DiscoveryTests.csproj b/test/DiscoveryTests/DiscoveryTests.csproj index 295d5340fa..abeaaa956d 100644 --- a/test/DiscoveryTests/DiscoveryTests.csproj +++ b/test/DiscoveryTests/DiscoveryTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateValidationTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateValidationTests.cs index 93cdf43453..68e7207e4a 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateValidationTests.cs @@ -3,9 +3,6 @@ using JsonApiDotNetCore.Serialization.Objects; using TestBuildingBlocks; using Xunit; -#if NET6_0 -using Microsoft.Extensions.DependencyInjection; -#endif namespace JsonApiDotNetCoreTests.IntegrationTests.InputValidation.ModelState; @@ -20,12 +17,6 @@ public ModelStateValidationTests(IntegrationTestContext(); testContext.UseController(); - -#if NET6_0 - testContext.ConfigureServices(services => - // Polyfill for missing DateOnly/TimeOnly support in .NET 6 ModelState validation. - services.AddDateOnlyTimeOnlyStringConverters()); -#endif } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/JsonKebabCaseNamingPolicy.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/JsonKebabCaseNamingPolicy.cs deleted file mode 100644 index 1d2f948068..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/JsonKebabCaseNamingPolicy.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Text; -using System.Text.Json; - -namespace JsonApiDotNetCoreTests.IntegrationTests.NamingConventions; - -// Based on https://github.com/J0rgeSerran0/JsonNamingPolicy -internal sealed class JsonKebabCaseNamingPolicy : JsonNamingPolicy -{ - private const char Separator = '-'; - - public static readonly JsonKebabCaseNamingPolicy Instance = new(); - - public override string ConvertName(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - return string.Empty; - } - - ReadOnlySpan spanName = name.Trim(); - - var stringBuilder = new StringBuilder(); - bool addCharacter = true; - - bool isNextLower = false; - bool isNextUpper = false; - bool isNextSpace = false; - - for (int position = 0; position < spanName.Length; position++) - { - if (position != 0) - { - bool isCurrentSpace = spanName[position] == 32; - bool isPreviousSpace = spanName[position - 1] == 32; - bool isPreviousSeparator = spanName[position - 1] == 95; - - if (position + 1 != spanName.Length) - { - isNextLower = spanName[position + 1] is >= 'a' and <= 'z'; - isNextUpper = spanName[position + 1] is >= 'A' and <= 'Z'; - isNextSpace = spanName[position + 1] == ' '; - } - - if (isCurrentSpace && (isPreviousSpace || isPreviousSeparator || isNextUpper || isNextSpace)) - { - addCharacter = false; - } - else - { - bool isCurrentUpper = spanName[position] is >= 'A' and <= 'Z'; - bool isPreviousLower = spanName[position - 1] is >= 'a' and <= 'z'; - bool isPreviousNumber = spanName[position - 1] is >= '0' and <= '9'; - - if (isCurrentUpper && (isPreviousLower || isPreviousNumber || isNextLower || isNextSpace)) - { - stringBuilder.Append(Separator); - } - else - { - if (isCurrentSpace) - { - stringBuilder.Append(Separator); - addCharacter = false; - } - } - } - } - - if (addCharacter) - { - stringBuilder.Append(spanName[position]); - } - else - { - addCharacter = true; - } - } - - return stringBuilder.ToString().ToLowerInvariant(); - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs index da3d3b10f9..6321943718 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs @@ -1,3 +1,4 @@ +using System.Text.Json; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using TestBuildingBlocks; @@ -16,7 +17,7 @@ protected override void SetJsonApiOptions(JsonApiOptions options) options.UseRelativeLinks = true; options.IncludeTotalResourceCount = true; - options.SerializerOptions.PropertyNamingPolicy = JsonKebabCaseNamingPolicy.Instance; - options.SerializerOptions.DictionaryKeyPolicy = JsonKebabCaseNamingPolicy.Instance; + options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower; + options.SerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.KebabCaseLower; } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/RgbColor.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/RgbColor.cs index 8eeabbee1d..28762384c9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/RgbColor.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/RgbColor.cs @@ -8,11 +8,6 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ReadWrite; [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ReadWrite")] public sealed class RgbColor : Identifiable { -#if NET6_0 - // Workaround for bug in .NET 6, see https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1153. - public override string? Id { get; set; } -#endif - [Attr] public string DisplayName { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj b/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj index 95a623d0f9..6bc5a666a1 100644 --- a/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj +++ b/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 @@ -13,7 +13,6 @@ - diff --git a/test/MultiDbContextTests/MultiDbContextTests.csproj b/test/MultiDbContextTests/MultiDbContextTests.csproj index 6466d8d75f..e80f03c69e 100644 --- a/test/MultiDbContextTests/MultiDbContextTests.csproj +++ b/test/MultiDbContextTests/MultiDbContextTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj b/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj index 968d798be3..4deb6b21cd 100644 --- a/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj +++ b/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/test/SourceGeneratorTests/SourceGeneratorTests.csproj b/test/SourceGeneratorTests/SourceGeneratorTests.csproj index 8b7d42fdca..4f487fa168 100644 --- a/test/SourceGeneratorTests/SourceGeneratorTests.csproj +++ b/test/SourceGeneratorTests/SourceGeneratorTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/test/TestBuildingBlocks/CapturingLoggerProvider.cs b/test/TestBuildingBlocks/CapturingLoggerProvider.cs index 38ec60ed3a..20503c4248 100644 --- a/test/TestBuildingBlocks/CapturingLoggerProvider.cs +++ b/test/TestBuildingBlocks/CapturingLoggerProvider.cs @@ -10,10 +10,10 @@ public sealed class CapturingLoggerProvider : ILoggerProvider private static readonly Func DefaultFilter = (_, _) => true; private readonly Func _filter; -#if NET9_0_OR_GREATER - private readonly Lock _lockObject = new(); -#else +#if NET8_0 private readonly object _lockObject = new(); +#else + private readonly Lock _lockObject = new(); #endif private readonly List _messages = []; diff --git a/test/TestBuildingBlocks/DbContextExtensions.cs b/test/TestBuildingBlocks/DbContextExtensions.cs index 1d1dbc0650..d3a8a0e8ad 100644 --- a/test/TestBuildingBlocks/DbContextExtensions.cs +++ b/test/TestBuildingBlocks/DbContextExtensions.cs @@ -44,14 +44,10 @@ private static async Task ClearTablesAsync(this DbContext dbContext, params Type } else { -#if !NET6_0 #pragma warning disable EF1002 // Risk of vulnerability to SQL injection. -#endif // Justification: Table names cannot be parameterized. await dbContext.Database.ExecuteSqlRawAsync($"DELETE FROM \"{tableName}\""); -#if !NET6_0 #pragma warning restore EF1002 // Risk of vulnerability to SQL injection. -#endif } } } diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj index a6f6ff4299..ddb6edb9dc 100644 --- a/test/TestBuildingBlocks/TestBuildingBlocks.csproj +++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0 diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index e977ac0c8c..68076a51e1 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;net6.0 + net9.0;net8.0