diff --git a/JsonApiDotnetCore.sln b/JsonApiDotnetCore.sln index ad130ce4b1..769230390d 100644 --- a/JsonApiDotnetCore.sln +++ b/JsonApiDotnetCore.sln @@ -41,7 +41,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore", "src\Js EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GettingStarted", "src\Examples\GettingStarted\GettingStarted.csproj", "{067FFD7A-C66B-473D-8471-37F5C95DF61C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "test\IntegrationTests\IntegrationTests.csproj", "{CEB08B86-6BF1-4227-B20F-45AE9C1CC6D9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "test\IntegrationTests\IntegrationTests.csproj", "{CEB08B86-6BF1-4227-B20F-45AE9C1CC6D9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -56,11 +56,15 @@ Global {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Debug|Any CPU.Build.0 = Debug|Any CPU {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Debug|x64.ActiveCfg = Debug|Any CPU + {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Debug|x64.Build.0 = Debug|Any CPU {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Debug|x86.ActiveCfg = Debug|Any CPU + {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Debug|x86.Build.0 = Debug|Any CPU {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Release|Any CPU.ActiveCfg = Release|Any CPU {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Release|Any CPU.Build.0 = Release|Any CPU {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Release|x64.ActiveCfg = Release|Any CPU + {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Release|x64.Build.0 = Release|Any CPU {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Release|x86.ActiveCfg = Release|Any CPU + {CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Release|x86.Build.0 = Release|Any CPU {4F15A8F8-5BC6-45A1-BC51-03F921B726A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4F15A8F8-5BC6-45A1-BC51-03F921B726A4}.Debug|Any CPU.Build.0 = Debug|Any CPU {4F15A8F8-5BC6-45A1-BC51-03F921B726A4}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -98,6 +102,7 @@ Global {03032A2F-664D-4DD8-A82F-AD8A482EDD85}.Release|x86.ActiveCfg = Release|Any CPU {03032A2F-664D-4DD8-A82F-AD8A482EDD85}.Release|x86.Build.0 = Release|Any CPU {DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C}.Debug|x64.ActiveCfg = Debug|Any CPU {DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C}.Debug|x64.Build.0 = Debug|Any CPU {DF0FCFB2-CB12-44BA-BBB5-1BE0BCFCD14C}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -111,11 +116,15 @@ Global {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Debug|Any CPU.Build.0 = Debug|Any CPU {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Debug|x64.ActiveCfg = Debug|Any CPU + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Debug|x64.Build.0 = Debug|Any CPU {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Debug|x86.ActiveCfg = Debug|Any CPU + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Debug|x86.Build.0 = Debug|Any CPU {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Release|Any CPU.ActiveCfg = Release|Any CPU {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Release|Any CPU.Build.0 = Release|Any CPU {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Release|x64.ActiveCfg = Release|Any CPU + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Release|x64.Build.0 = Release|Any CPU {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Release|x86.ActiveCfg = Release|Any CPU + {C916EBDA-3429-4FEA-AFB3-DF7CA32A8C6A}.Release|x86.Build.0 = Release|Any CPU {789085E1-048F-4996-B600-791B9CA3A663}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {789085E1-048F-4996-B600-791B9CA3A663}.Debug|Any CPU.Build.0 = Debug|Any CPU {789085E1-048F-4996-B600-791B9CA3A663}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -143,11 +152,15 @@ Global {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Debug|Any CPU.Build.0 = Debug|Any CPU {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Debug|x64.ActiveCfg = Debug|Any CPU + {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Debug|x64.Build.0 = Debug|Any CPU {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Debug|x86.ActiveCfg = Debug|Any CPU + {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Debug|x86.Build.0 = Debug|Any CPU {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|Any CPU.ActiveCfg = Release|Any CPU {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|Any CPU.Build.0 = Release|Any CPU {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|x64.ActiveCfg = Release|Any CPU + {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|x64.Build.0 = Release|Any CPU {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|x86.ActiveCfg = Release|Any CPU + {21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|x86.Build.0 = Release|Any CPU {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|Any CPU.Build.0 = Debug|Any CPU {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x64.ActiveCfg = Debug|Any CPU diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore deleted file mode 100644 index 5a3c72cbbb..0000000000 --- a/benchmarks/.gitignore +++ /dev/null @@ -1,239 +0,0 @@ -_data/ -*-report-default.md -*-report.csv -*-report.html - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -build/ -bld/ -[Bb]in/ -[Oo]bj/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Microsoft Azure ApplicationInsights config file -ApplicationInsights.config - -# Windows Store app package directory -AppPackages/ -BundleArtifacts/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe - -# FAKE - F# Make -.fake/ diff --git a/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.JsonApiContext.PathIsRelationship_Benchmarks-report-github.md b/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.JsonApiContext.PathIsRelationship_Benchmarks-report-github.md deleted file mode 100644 index 6be58e241a..0000000000 --- a/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.JsonApiContext.PathIsRelationship_Benchmarks-report-github.md +++ /dev/null @@ -1,12 +0,0 @@ -```ini -BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12 -Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4 -.NET Core SDK=2.1.4 - [Host] : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT - DefaultJob : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT -``` - -| Method | Mean | Error | StdDev | Gen 0 | Allocated | -| ---------- | --------: | ---------: | ---------: | -----: | --------: | -| UsingSplit | 421.08 ns | 19.3905 ns | 54.0529 ns | 0.4725 | 744 B | -| Current | 52.23 ns | 0.8052 ns | 0.7532 ns | - | 0 B | diff --git a/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.LinkBuilder.LinkBuilder_GetNamespaceFromPath_Benchmarks-report-github.md b/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.LinkBuilder.LinkBuilder_GetNamespaceFromPath_Benchmarks-report-github.md deleted file mode 100644 index 72951396e8..0000000000 --- a/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.LinkBuilder.LinkBuilder_GetNamespaceFromPath_Benchmarks-report-github.md +++ /dev/null @@ -1,16 +0,0 @@ -``` ini - -BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12 -Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4 -.NET Core SDK=2.1.4 - [Host] : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT - Job-XFMVNE : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT - -LaunchCount=3 TargetCount=20 WarmupCount=10 - -``` -| Method | Mean | Error | StdDev | Gen 0 | Allocated | -|--------------------------- |-----------:|----------:|----------:|-------:|----------:| -| UsingSplit | 1,197.6 ns | 11.929 ns | 25.933 ns | 0.9251 | 1456 B | -| UsingSpanWithStringBuilder | 1,542.0 ns | 15.249 ns | 33.792 ns | 0.9460 | 1488 B | -| UsingSpanWithNoAlloc | 272.6 ns | 2.265 ns | 5.018 ns | 0.0863 | 136 B | diff --git a/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.Query.QueryParser_Benchmarks-report-github.md b/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.Query.QueryParser_Benchmarks-report-github.md deleted file mode 100755 index b3ebd8a29c..0000000000 --- a/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.Query.QueryParser_Benchmarks-report-github.md +++ /dev/null @@ -1,16 +0,0 @@ -``` ini - -BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12 -Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4 -.NET Core SDK=2.0.0 - [Host] : .NET Core 1.1.4 (Framework 4.6.25714.03), 64bit RyuJIT - Job-WKDOLS : .NET Core 1.1.4 (Framework 4.6.25714.03), 64bit RyuJIT - -LaunchCount=3 TargetCount=20 WarmupCount=10 - -``` -| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Allocated | -|--------------- |-------------:|-----------:|-----------:|---------:|--------:|----------:| -| AscendingSort | 4.316 us | 1.3773 us | 3.0232 us | 0.5066 | 0.1303 | 1.08 KB | -| DescendingSort | 3.300 us | 0.0314 us | 0.0682 us | 0.5123 | 0.1318 | 1.13 KB | -| ComplexQuery | 2,041.642 us | 41.5631 us | 92.1010 us | 312.5000 | 80.2734 | 648.99 KB | diff --git a/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.RequestMiddleware.ContainsMediaTypeParameters_Benchmarks-report-github.md b/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.RequestMiddleware.ContainsMediaTypeParameters_Benchmarks-report-github.md deleted file mode 100644 index 066e7b2036..0000000000 --- a/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.RequestMiddleware.ContainsMediaTypeParameters_Benchmarks-report-github.md +++ /dev/null @@ -1,14 +0,0 @@ -``` ini - -BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12 -Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4 -.NET Core SDK=2.1.4 - [Host] : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT - DefaultJob : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT - - -``` -| Method | Mean | Error | StdDev | Gen 0 | Allocated | -|----------- |----------:|----------:|----------:|-------:|----------:| -| UsingSplit | 157.28 ns | 2.9689 ns | 5.8602 ns | 0.2134 | 336 B | -| Current | 39.96 ns | 0.6489 ns | 0.6070 ns | - | 0 B | diff --git a/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.Serialization.JsonApiDeserializer_Benchmarks-report-github.md b/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.Serialization.JsonApiDeserializer_Benchmarks-report-github.md deleted file mode 100755 index 6c8c3f2905..0000000000 --- a/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.Serialization.JsonApiDeserializer_Benchmarks-report-github.md +++ /dev/null @@ -1,13 +0,0 @@ -``` ini - -BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12 -Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4 -.NET Core SDK=2.0.0 - [Host] : .NET Core 1.1.4 (Framework 4.6.25714.03), 64bit RyuJIT - DefaultJob : .NET Core 1.1.4 (Framework 4.6.25714.03), 64bit RyuJIT - - -``` -| Method | Mean | Error | StdDev | -|------------------------ |---------:|----------:|----------:| -| DeserializeSimpleObject | 27.05 us | 0.5353 us | 0.5950 us | diff --git a/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.Serialization.JsonApiSerializer_Benchmarks-report-github.md b/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.Serialization.JsonApiSerializer_Benchmarks-report-github.md deleted file mode 100755 index f86bf0faa9..0000000000 --- a/benchmarks/BenchmarkDotNet.Artifacts/results/Benchmarks.Serialization.JsonApiSerializer_Benchmarks-report-github.md +++ /dev/null @@ -1,13 +0,0 @@ -``` ini - -BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12 -Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4 -.NET Core SDK=2.0.0 - [Host] : .NET Core 1.1.4 (Framework 4.6.25714.03), 64bit RyuJIT - DefaultJob : .NET Core 1.1.4 (Framework 4.6.25714.03), 64bit RyuJIT - - -``` -| Method | Mean | Error | StdDev | -|---------------------- |---------:|----------:|----------:| -| SerializeSimpleObject | 7.195 us | 0.1436 us | 0.1816 us | diff --git a/benchmarks/BenchmarkResource.cs b/benchmarks/BenchmarkResource.cs new file mode 100644 index 0000000000..0d3ae2c8bf --- /dev/null +++ b/benchmarks/BenchmarkResource.cs @@ -0,0 +1,25 @@ +using JsonApiDotNetCore.Models; + +namespace Benchmarks +{ + public class BenchmarkResource : Identifiable + { + [Attr(BenchmarkResourcePublicNames.NameAttr)] + public string Name { get; set; } + + [HasOne] + public SubResource Child { get; set; } + } + + public class SubResource : Identifiable + { + [Attr] + public string Value { get; set; } + } + + public static class BenchmarkResourcePublicNames + { + public const string NameAttr = "full-name"; + public const string Type = "simple-types"; + } +} diff --git a/benchmarks/Benchmarks.csproj b/benchmarks/Benchmarks.csproj index 60a9a8bf42..02bfbf378d 100644 --- a/benchmarks/Benchmarks.csproj +++ b/benchmarks/Benchmarks.csproj @@ -2,10 +2,9 @@ Exe $(NetCoreAppVersion) - Benchmarks - + diff --git a/benchmarks/DependencyFactory.cs b/benchmarks/DependencyFactory.cs new file mode 100644 index 0000000000..0c268bc7c5 --- /dev/null +++ b/benchmarks/DependencyFactory.cs @@ -0,0 +1,29 @@ +using System; +using JsonApiDotNetCore.Builders; +using JsonApiDotNetCore.Internal.Contracts; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Query; +using Moq; + +namespace Benchmarks +{ + internal static class DependencyFactory + { + public static IResourceGraph CreateResourceGraph() + { + IResourceGraphBuilder builder = new ResourceGraphBuilder(); + builder.AddResource(BenchmarkResourcePublicNames.Type); + return builder.Build(); + } + + public static IResourceDefinitionProvider CreateResourceDefinitionProvider(IResourceGraph resourceGraph) + { + var resourceDefinition = new ResourceDefinition(resourceGraph); + + var resourceDefinitionProviderMock = new Mock(); + resourceDefinitionProviderMock.Setup(provider => provider.Get(It.IsAny())).Returns(resourceDefinition); + + return resourceDefinitionProviderMock.Object; + } + } +} diff --git a/benchmarks/JsonApiContext/PathIsRelationship_Benchmarks.cs b/benchmarks/JsonApiContext/PathIsRelationship_Benchmarks.cs deleted file mode 100644 index 83fe6fc53c..0000000000 --- a/benchmarks/JsonApiContext/PathIsRelationship_Benchmarks.cs +++ /dev/null @@ -1,24 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Attributes.Exporters; - -namespace Benchmarks.JsonApiContext -{ - [MarkdownExporter, MemoryDiagnoser] - public class PathIsRelationship_Benchmarks - { - private const string PATH = "https://example.com/api/v1/namespace/articles/relationships/author/"; - - [Benchmark] - public void Current() - => JsonApiDotNetCore.Services.JsonApiContext.PathIsRelationship(PATH); - - [Benchmark] - public void UsingSplit() => UsingSplitImpl(PATH); - - private bool UsingSplitImpl(string path) - { - var split = path.Split('/'); - return split[split.Length - 2] == "relationships"; - } - } -} diff --git a/benchmarks/LinkBuilder/LinkBuilderGetNamespaceFromPathBenchmarks.cs b/benchmarks/LinkBuilder/LinkBuilderGetNamespaceFromPathBenchmarks.cs new file mode 100644 index 0000000000..5524f83e5f --- /dev/null +++ b/benchmarks/LinkBuilder/LinkBuilderGetNamespaceFromPathBenchmarks.cs @@ -0,0 +1,70 @@ +using BenchmarkDotNet.Attributes; +using System; +using System.Text; + +namespace Benchmarks.LinkBuilder +{ + [MarkdownExporter, SimpleJob(launchCount: 3, warmupCount: 10, targetCount: 20), MemoryDiagnoser] + public class LinkBuilderGetNamespaceFromPathBenchmarks + { + private const string RequestPath = "/api/some-really-long-namespace-path/resources/current/articles/?some"; + private const string EntityName = "articles"; + private const char PathDelimiter = '/'; + + [Benchmark] + public void UsingStringSplit() => GetNamespaceFromPathUsingStringSplit(RequestPath, EntityName); + + [Benchmark] + public void UsingReadOnlySpan() => GetNamespaceFromPathUsingReadOnlySpan(RequestPath, EntityName); + + public static string GetNamespaceFromPathUsingStringSplit(string path, string entityName) + { + StringBuilder namespaceBuilder = new StringBuilder(path.Length); + string[] segments = path.Split('/'); + + for (int index = 1; index < segments.Length; index++) + { + if (segments[index] == entityName) + { + break; + } + + namespaceBuilder.Append(PathDelimiter); + namespaceBuilder.Append(segments[index]); + } + + return namespaceBuilder.ToString(); + } + + public static string GetNamespaceFromPathUsingReadOnlySpan(string path, string entityName) + { + ReadOnlySpan entityNameSpan = entityName.AsSpan(); + ReadOnlySpan pathSpan = path.AsSpan(); + + for (int index = 0; index < pathSpan.Length; index++) + { + if (pathSpan[index].Equals(PathDelimiter)) + { + if (pathSpan.Length > index + entityNameSpan.Length) + { + ReadOnlySpan possiblePathSegment = pathSpan.Slice(index + 1, entityNameSpan.Length); + + if (entityNameSpan.SequenceEqual(possiblePathSegment)) + { + int lastCharacterIndex = index + 1 + entityNameSpan.Length; + + bool isAtEnd = lastCharacterIndex == pathSpan.Length; + bool hasDelimiterAfterSegment = pathSpan.Length >= lastCharacterIndex + 1 && pathSpan[lastCharacterIndex].Equals(PathDelimiter); + if (isAtEnd || hasDelimiterAfterSegment) + { + return pathSpan.Slice(0, index).ToString(); + } + } + } + } + } + + return string.Empty; + } + } +} diff --git a/benchmarks/LinkBuilder/LinkBuilder_ GetNamespaceFromPath_Benchmarks.cs b/benchmarks/LinkBuilder/LinkBuilder_ GetNamespaceFromPath_Benchmarks.cs deleted file mode 100644 index 1432afecd8..0000000000 --- a/benchmarks/LinkBuilder/LinkBuilder_ GetNamespaceFromPath_Benchmarks.cs +++ /dev/null @@ -1,70 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Attributes.Exporters; -using BenchmarkDotNet.Attributes.Jobs; -using System; - -namespace Benchmarks.LinkBuilder -{ - [MarkdownExporter, SimpleJob(launchCount: 3, warmupCount: 10, targetCount: 20), MemoryDiagnoser] - public class LinkBuilder_GetNamespaceFromPath_Benchmarks - { - private const string PATH = "/api/some-really-long-namespace-path/resources/current/articles"; - private const string ENTITY_NAME = "articles"; - - [Benchmark] - public void UsingSplit() => GetNamespaceFromPath_BySplitting(PATH, ENTITY_NAME); - - [Benchmark] - public void Current() => GetNameSpaceFromPathCurrent(PATH, ENTITY_NAME); - - public static string GetNamespaceFromPath_BySplitting(string path, string entityName) - { - var nSpace = string.Empty; - var segments = path.Split('/'); - - for (var i = 1; i < segments.Length; i++) - { - if (segments[i].ToLower() == entityName) - break; - - nSpace += $"/{segments[i]}"; - } - - return nSpace; - } - - public static string GetNameSpaceFromPathCurrent(string path, string entityName) - { - - var entityNameSpan = entityName.AsSpan(); - var pathSpan = path.AsSpan(); - const char delimiter = '/'; - for (var i = 0; i < pathSpan.Length; i++) - { - if (pathSpan[i].Equals(delimiter)) - { - var nextPosition = i + 1; - if (pathSpan.Length > i + entityNameSpan.Length) - { - var possiblePathSegment = pathSpan.Slice(nextPosition, entityNameSpan.Length); - if (entityNameSpan.SequenceEqual(possiblePathSegment)) - { - // check to see if it's the last position in the string - // or if the next character is a / - var lastCharacterPosition = nextPosition + entityNameSpan.Length; - - if (lastCharacterPosition == pathSpan.Length || pathSpan.Length >= lastCharacterPosition + 2 && pathSpan[lastCharacterPosition].Equals(delimiter)) - { - return pathSpan.Slice(0, i).ToString(); - } - } - } - } - } - - return string.Empty; - - - } - } -} diff --git a/benchmarks/Program.cs b/benchmarks/Program.cs index bd504c670f..474bb5725c 100644 --- a/benchmarks/Program.cs +++ b/benchmarks/Program.cs @@ -1,20 +1,20 @@ using BenchmarkDotNet.Running; -using Benchmarks.JsonApiContext; using Benchmarks.LinkBuilder; using Benchmarks.Query; -using Benchmarks.RequestMiddleware; using Benchmarks.Serialization; -namespace Benchmarks { - class Program { - static void Main(string[] args) { - var switcher = new BenchmarkSwitcher(new[] { - typeof(JsonApideserializer_Benchmarks), - //typeof(JsonApiSerializer_Benchmarks), - typeof(QueryParser_Benchmarks), - typeof(LinkBuilder_GetNamespaceFromPath_Benchmarks), - typeof(ContainsMediaTypeParameters_Benchmarks), - typeof(PathIsRelationship_Benchmarks) +namespace Benchmarks +{ + class Program + { + static void Main(string[] args) + { + var switcher = new BenchmarkSwitcher(new[] + { + typeof(JsonApiDeserializerBenchmarks), + typeof(JsonApiSerializerBenchmarks), + typeof(QueryParserBenchmarks), + typeof(LinkBuilderGetNamespaceFromPathBenchmarks) }); switcher.Run(args); } diff --git a/benchmarks/Query/QueryParserBenchmarks.cs b/benchmarks/Query/QueryParserBenchmarks.cs new file mode 100644 index 0000000000..6dd7e509c1 --- /dev/null +++ b/benchmarks/Query/QueryParserBenchmarks.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using BenchmarkDotNet.Attributes; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Internal.Contracts; +using JsonApiDotNetCore.Managers; +using JsonApiDotNetCore.Query; +using JsonApiDotNetCore.Services; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; + +namespace Benchmarks.Query +{ + [MarkdownExporter, SimpleJob(launchCount: 3, warmupCount: 10, targetCount: 20), MemoryDiagnoser] + public class QueryParserBenchmarks + { + private readonly QueryParameterDiscovery _queryParameterDiscoveryForSort; + private readonly QueryParameterDiscovery _queryParameterDiscoveryForAll; + + public QueryParserBenchmarks() + { + IJsonApiOptions options = new JsonApiOptions(); + IResourceGraph resourceGraph = DependencyFactory.CreateResourceGraph(); + + var currentRequest = new CurrentRequest(); + currentRequest.SetRequestResource(resourceGraph.GetResourceContext(typeof(BenchmarkResource))); + + IResourceDefinitionProvider resourceDefinitionProvider = DependencyFactory.CreateResourceDefinitionProvider(resourceGraph); + + _queryParameterDiscoveryForSort = CreateQueryParameterDiscoveryForSort(resourceGraph, currentRequest, resourceDefinitionProvider, options); + _queryParameterDiscoveryForAll = CreateQueryParameterDiscoveryForAll(resourceGraph, currentRequest, resourceDefinitionProvider, options); + } + + private static QueryParameterDiscovery CreateQueryParameterDiscoveryForSort(IResourceGraph resourceGraph, + CurrentRequest currentRequest, IResourceDefinitionProvider resourceDefinitionProvider, IJsonApiOptions options) + { + ISortService sortService = new SortService(resourceDefinitionProvider, resourceGraph, currentRequest); + + var queryServices = new List + { + sortService + }; + + return new QueryParameterDiscovery(options, queryServices); + } + + private static QueryParameterDiscovery CreateQueryParameterDiscoveryForAll(IResourceGraph resourceGraph, + CurrentRequest currentRequest, IResourceDefinitionProvider resourceDefinitionProvider, IJsonApiOptions options) + { + IIncludeService includeService = new IncludeService(resourceGraph, currentRequest); + IFilterService filterService = new FilterService(resourceDefinitionProvider, resourceGraph, currentRequest); + ISortService sortService = new SortService(resourceDefinitionProvider, resourceGraph, currentRequest); + ISparseFieldsService sparseFieldsService = new SparseFieldsService(resourceGraph, currentRequest); + IPageService pageService = new PageService(options, resourceGraph, currentRequest); + IOmitDefaultService omitDefaultService = new OmitDefaultService(options); + IOmitNullService omitNullService = new OmitNullService(options); + + var queryServices = new List + { + includeService, filterService, sortService, sparseFieldsService, pageService, omitDefaultService, + omitNullService + }; + + return new QueryParameterDiscovery(options, queryServices); + } + + [Benchmark] + public void AscendingSort() => _queryParameterDiscoveryForSort.Parse(new QueryCollection( + new Dictionary + { + {"sort", BenchmarkResourcePublicNames.NameAttr} + } + ), null); + + [Benchmark] + public void DescendingSort() => _queryParameterDiscoveryForSort.Parse(new QueryCollection( + new Dictionary + { + {"sort", $"-{BenchmarkResourcePublicNames.NameAttr}"} + } + ), null); + + [Benchmark] + public void ComplexQuery() => Run(100, () => _queryParameterDiscoveryForAll.Parse(new QueryCollection( + new Dictionary + { + {$"filter[{BenchmarkResourcePublicNames.NameAttr}]", new StringValues(new[] {"abc", "eq:abc"})}, + {"sort", $"-{BenchmarkResourcePublicNames.NameAttr}"}, + {"include", "child"}, + {"page[size]", "1"}, + {"fields", BenchmarkResourcePublicNames.NameAttr} + } + ), null)); + + private void Run(int iterations, Action action) { + for (int i = 0; i < iterations; i++) + action(); + } + } +} diff --git a/benchmarks/Query/QueryParser_Benchmarks.cs b/benchmarks/Query/QueryParser_Benchmarks.cs deleted file mode 100644 index 242df86b18..0000000000 --- a/benchmarks/Query/QueryParser_Benchmarks.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Attributes.Exporters; -using BenchmarkDotNet.Attributes.Jobs; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Internal; -using JsonApiDotNetCore.Managers.Contracts; -using JsonApiDotNetCore.Models; -using JsonApiDotNetCore.Services; -using Microsoft.AspNetCore.Http.Internal; -using Microsoft.Extensions.Primitives; -using Moq; - -namespace Benchmarks.Query { - [MarkdownExporter, SimpleJob(launchCount : 3, warmupCount : 10, targetCount : 20), MemoryDiagnoser] - public class QueryParser_Benchmarks { - private readonly BenchmarkFacade _queryParser; - - private const string ATTRIBUTE = "Attribute"; - private const string ASCENDING_SORT = ATTRIBUTE; - private const string DESCENDING_SORT = "-" + ATTRIBUTE; - - public QueryParser_Benchmarks() { - var requestMock = new Mock(); - requestMock.Setup(m => m.GetRequestResource()).Returns(new ResourceContext { - Attributes = new List { - new AttrAttribute(ATTRIBUTE) - } - }); - var options = new JsonApiOptions(); - _queryParser = new BenchmarkFacade(requestMock.Object, options); - } - - [Benchmark] - public void AscendingSort() => _queryParser._ParseSortParameters(ASCENDING_SORT); - - [Benchmark] - public void DescendingSort() => _queryParser._ParseSortParameters(DESCENDING_SORT); - - [Benchmark] - public void ComplexQuery() => Run(100, () => _queryParser.Parse( - new QueryCollection( - new Dictionary { - { $"filter[{ATTRIBUTE}]", new StringValues(new [] { "abc", "eq:abc" }) }, - { $"sort", $"-{ATTRIBUTE}" }, - { $"include", "relationship" }, - { $"page[size]", "1" }, - { $"fields[resource]", ATTRIBUTE }, - } - ) - )); - - private void Run(int iterations, Action action) { - for (int i = 0; i < iterations; i++) - action(); - } - - // this facade allows us to expose and micro-benchmark protected methods - private class BenchmarkFacade : QueryParameterDiscovery { - public BenchmarkFacade( - IRequestContext currentRequest, - JsonApiOptions options) : base(currentRequest, options) { } - - public void _ParseSortParameters(string value) => base.ParseSortParameters(value); - } - } -} diff --git a/benchmarks/RequestMiddleware/ContainsMediaTypeParameters_Benchmarks.cs b/benchmarks/RequestMiddleware/ContainsMediaTypeParameters_Benchmarks.cs deleted file mode 100644 index 2e0a5c0232..0000000000 --- a/benchmarks/RequestMiddleware/ContainsMediaTypeParameters_Benchmarks.cs +++ /dev/null @@ -1,25 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Attributes.Exporters; -using JsonApiDotNetCore.Internal; - -namespace Benchmarks.RequestMiddleware -{ - [MarkdownExporter, MemoryDiagnoser] - public class ContainsMediaTypeParameters_Benchmarks - { - private const string MEDIA_TYPE = "application/vnd.api+json; version=1"; - - [Benchmark] - public void UsingSplit() => UsingSplitImpl(MEDIA_TYPE); - - [Benchmark] - public void Current() - => JsonApiDotNetCore.Middleware.CurrentRequestMiddleware.ContainsMediaTypeParameters(MEDIA_TYPE); - - private bool UsingSplitImpl(string mediaType) - { - var mediaTypeArr = mediaType.Split(';'); - return (mediaTypeArr[0] == Constants.ContentType && mediaTypeArr.Length == 2); - } - } -} diff --git a/benchmarks/Serialization/JsonApiDeserializerBenchmarks.cs b/benchmarks/Serialization/JsonApiDeserializerBenchmarks.cs new file mode 100644 index 0000000000..30742c5440 --- /dev/null +++ b/benchmarks/Serialization/JsonApiDeserializerBenchmarks.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using BenchmarkDotNet.Attributes; +using JsonApiDotNetCore.Internal.Contracts; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Serialization; +using JsonApiDotNetCore.Serialization.Server; +using Newtonsoft.Json; + +namespace Benchmarks.Serialization +{ + [MarkdownExporter] + public class JsonApiDeserializerBenchmarks + { + private static readonly string Content = JsonConvert.SerializeObject(new Document + { + Data = new ResourceObject + { + Type = BenchmarkResourcePublicNames.Type, + Id = "1", + Attributes = new Dictionary + { + { + "name", + Guid.NewGuid().ToString() + } + } + } + }); + + private readonly IJsonApiDeserializer _jsonApiDeserializer; + + public JsonApiDeserializerBenchmarks() + { + IResourceGraph resourceGraph = DependencyFactory.CreateResourceGraph(); + var targetedFields = new TargetedFields(); + + _jsonApiDeserializer = new RequestDeserializer(resourceGraph, targetedFields); + } + + [Benchmark] + public object DeserializeSimpleObject() => _jsonApiDeserializer.Deserialize(Content); + } +} diff --git a/benchmarks/Serialization/JsonApiDeserializer_Benchmarks.cs b/benchmarks/Serialization/JsonApiDeserializer_Benchmarks.cs deleted file mode 100644 index 2dff692a31..0000000000 --- a/benchmarks/Serialization/JsonApiDeserializer_Benchmarks.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Attributes.Exporters; -using JsonApiDotNetCore.Builders; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Managers.Contracts; -using JsonApiDotNetCore.Models; -using JsonApiDotNetCore.Serialization; -using JsonApiDotNetCore.Serialization.Contracts; - -using JsonApiDotNetCore.Services; -using Moq; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -namespace Benchmarks.Serialization -{ - [MarkdownExporter] - public class JsonApideserializer_Benchmarks { - private const string TYPE_NAME = "simple-types"; - private static readonly string Content = JsonConvert.SerializeObject(new Document { - Data = new ResourceObject { - Type = TYPE_NAME, - Id = "1", - Attributes = new Dictionary { - { - "name", - Guid.NewGuid().ToString() - } - } - } - }); - - private readonly JsonApideserializer _jsonApideserializer; - - public JsonApideserializer_Benchmarks() { - var resourceGraphBuilder = new ResourceGraphBuilder(); - resourceGraphBuilder.AddResource(TYPE_NAME); - var resourceGraph = resourceGraphBuilder.Build(); - var currentRequestMock = new Mock(); - - currentRequestMock.Setup(m => m.GetUpdatedAttributes()).Returns(new Dictionary()); - - var jsonApiContextMock = new Mock(); - jsonApiContextMock.SetupAllProperties(); - jsonApiContextMock.Setup(m => m.ResourceGraph).Returns(resourceGraph); - jsonApiContextMock.Setup(m => m.RequestManager.GetUpdatedAttributes()).Returns(new Dictionary()); - - var jsonApiOptions = new JsonApiOptions(); - jsonApiOptions.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); - jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions); - - - _jsonApideserializer = new JsonApideserializer(jsonApiContextMock.Object, currentRequestMock.Object); - } - - [Benchmark] - public object DeserializeSimpleObject() => _jsonApideserializer.Deserialize(Content); - - private class SimpleType : Identifiable { - [Attr] - public string Name { get; set; } - } - } -} diff --git a/benchmarks/Serialization/JsonApiSerializerBenchmarks.cs b/benchmarks/Serialization/JsonApiSerializerBenchmarks.cs new file mode 100644 index 0000000000..f3ac7ca525 --- /dev/null +++ b/benchmarks/Serialization/JsonApiSerializerBenchmarks.cs @@ -0,0 +1,51 @@ +using System; +using BenchmarkDotNet.Attributes; +using JsonApiDotNetCore.Internal.Contracts; +using JsonApiDotNetCore.Managers; +using JsonApiDotNetCore.Query; +using JsonApiDotNetCore.Serialization; +using JsonApiDotNetCore.Serialization.Server; +using JsonApiDotNetCore.Serialization.Server.Builders; +using Moq; + +namespace Benchmarks.Serialization +{ + [MarkdownExporter] + public class JsonApiSerializerBenchmarks + { + private static readonly BenchmarkResource Content = new BenchmarkResource + { + Id = 123, + Name = Guid.NewGuid().ToString() + }; + + private readonly IJsonApiSerializer _jsonApiSerializer; + + public JsonApiSerializerBenchmarks() + { + IResourceGraph resourceGraph = DependencyFactory.CreateResourceGraph(); + IFieldsToSerialize fieldsToSerialize = CreateFieldsToSerialize(resourceGraph); + + var metaBuilderMock = new Mock>(); + var linkBuilderMock = new Mock(); + var includeBuilderMock = new Mock(); + + var resourceObjectBuilder = new ResourceObjectBuilder(resourceGraph, new ResourceObjectBuilderSettings()); + + _jsonApiSerializer = new ResponseSerializer(metaBuilderMock.Object, linkBuilderMock.Object, + includeBuilderMock.Object, fieldsToSerialize, resourceObjectBuilder, resourceGraph); + } + + private static FieldsToSerialize CreateFieldsToSerialize(IResourceGraph resourceGraph) + { + var resourceDefinitionProvider = DependencyFactory.CreateResourceDefinitionProvider(resourceGraph); + var currentRequest = new CurrentRequest(); + var sparseFieldsService = new SparseFieldsService(resourceGraph, currentRequest); + + return new FieldsToSerialize(resourceGraph, sparseFieldsService, resourceDefinitionProvider); + } + + [Benchmark] + public object SerializeSimpleObject() => _jsonApiSerializer.Serialize(Content); + } +} diff --git a/benchmarks/Serialization/JsonApiSerializer_Benchmarks.cs b/benchmarks/Serialization/JsonApiSerializer_Benchmarks.cs deleted file mode 100644 index 19e585bdc3..0000000000 --- a/benchmarks/Serialization/JsonApiSerializer_Benchmarks.cs +++ /dev/null @@ -1,51 +0,0 @@ -//using System.Collections.Generic; -//using BenchmarkDotNet.Attributes; -//using BenchmarkDotNet.Attributes.Exporters; -//using JsonApiDotNetCore.Builders; -//using JsonApiDotNetCore.Configuration; -//using JsonApiDotNetCore.Internal.Generics; -//using JsonApiDotNetCore.Models; -//using JsonApiDotNetCore.Serialization; -using JsonApiDotNetCore.Serialization.Contracts; - -//using JsonApiDotNetCore.Services; -//using Moq; -//using Newtonsoft.Json.Serialization; - -//namespace Benchmarks.Serialization { -// [MarkdownExporter] -// public class JsonApiSerializer_Benchmarks { -// private const string TYPE_NAME = "simple-types"; -// private static readonly SimpleType Content = new SimpleType(); - -// private readonly JsonApiSerializer _jsonApiSerializer; - -// public JsonApiSerializer_Benchmarks() { -// var resourceGraphBuilder = new ResourceGraphBuilder(); -// resourceGraphBuilder.AddResource(TYPE_NAME); -// var resourceGraph = resourceGraphBuilder.Build(); - -// var jsonApiContextMock = new Mock(); -// jsonApiContextMock.SetupAllProperties(); -// jsonApiContextMock.Setup(m => m.ResourceGraph).Returns(resourceGraph); -// jsonApiContextMock.Setup(m => m.AttributesToUpdate).Returns(new Dictionary()); - -// var jsonApiOptions = new JsonApiOptions(); -// jsonApiOptions.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); -// jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions); - -// var genericServiceFactoryMock = new Mock(); - -// var documentBuilder = new BaseDocumentBuilder(jsonApiContextMock.Object); -// _jsonApiSerializer = new JsonApiSerializer(jsonApiContextMock.Object, documentBuilder); -// } - -// [Benchmark] -// public object SerializeSimpleObject() => _jsonApiSerializer.Serialize(Content); - -// private class SimpleType : Identifiable { -// [Attr("name")] -// public string Name { get; set; } -// } -// } -//} diff --git a/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs b/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs index 1212a9b486..6373f92ac4 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/SortService.cs @@ -14,7 +14,6 @@ public class SortService : QueryParameterService, ISortService const char DESCENDING_SORT_OPERATOR = '-'; private readonly IResourceDefinitionProvider _resourceDefinitionProvider; private List _queries; - private bool _isProcessed; public SortService(IResourceDefinitionProvider resourceDefinitionProvider, IResourceGraph resourceGraph, @@ -29,7 +28,6 @@ public SortService(IResourceDefinitionProvider resourceDefinitionProvider, public virtual void Parse(KeyValuePair queryParameter) { EnsureNoNestedResourceRoute(); - CheckIfProcessed(); // disallow multiple sort parameters. var queries = BuildQueries(queryParameter.Value); _queries = queries.Select(BuildQueryContext).ToList(); @@ -86,14 +84,5 @@ private SortQueryContext BuildQueryContext(SortQuery query) Relationship = relationship }; } - - private void CheckIfProcessed() - { - if (_isProcessed) - throw new JsonApiException(400, "The sort query parameter occured in the URI more than once."); - - _isProcessed = true; - } - } }