Skip to content

Commit f0d638f

Browse files
author
Bart Koelman
authored
Nullable reference types (#1095)
* Renamed TryXXX resource graph lookup methods to FindXXX, throwing on incoming null (not empty). Made non-public TryXXX methods tolerant against incoming nulls. * Added directive to disable NRT in all source files * Turn on NRT globally * Annotated JsonApiDotNetCore library * Use alternate public name for attribute, to improve test coverage * Annotated example projects * Annotated benchmark project - Removed LinkBuilderGetNamespaceFromPathBenchmarks, we don't have such code anymore - Cleanup query string benchmark * Enhanced rendering of error.source.pointer on ModelState validation errors * Respect configured MaxModelValidationErrors in operations * Added docs for nullable reference types * Automated cleanup code * Fixed: do not fail when clearing a required to-many relationship * Annotated controllers in integration tests * Annotated DbContexts in integration tests * Annotated fakers in integration tests * Annotated TestBuildingBlocks * Use Should() replacements that flow nullability * Re-align similar testsets * Annotated test projects, use fakers for non-trivial models in integration tests, FluentAssertions everywhere * Fixed: make faker dates deterministic See https://giters.com/bchavez/Bogus/issues/247 * Enable ASP.NET ModelState validation by default Due to recent enhancements, this produces better error messages for missing required relationships and avoids 500 errors due to foreign key constraint violations. * Check off roadmap entry * Documented required one-to-one relationships mapping * Added missing HEAD routes * Prefer IDictionary<,>.TryGetValue() over .Contains() followed by indexer lookup * Optimized response rendering time when no sparse fieldset is requested. This relies on the fact that the resource model is frozen after building the resource graph. | Method | Mean | Error | StdDev | | ------------------------------------ | -------- | ------- | ------- | | SerializeOperationsResponse (before) | 168.6 us | 1.74 us | 1.63 us | | SerializeOperationsResponse (after) | 148.5 us | 1.06 us | 0.99 us | | SerializeResourceResponse (before) | 123.7 us | 1.12 us | 1.05 us | | SerializeResourceResponse (after) | 119.6 us | 1.05 us | 0.98 us | This makes SerializeOperationsResponse 12% faster and SerializeResourceResponse 3% faster. What the benchmark does not show is the performance improvement on subsequent requests, so in practice the gain in higher. * Fixed: handle nulls in request body * Workaround for crash in cibuild * Update to latest JetBrains tools In the past, [cibuild hung after update to the latest version](#1045). Since then, the codebase has changed and a new minor version was released. These result in the cibuild no longer hanging. Stats from my laptop (old=v2021.1.4, new=v2021.2.2): ``` master, inspectcode (old): 2:59 master, inspectcode (new): 2:00 nrt, inspectcode (old): 3:04 nrt, inspectcode (new): 2:33 master, cleanupcode (old): 11:06 master, cleanupcode (new): 5:39 nrt, cleanupcode (old): 11:59 nrt, cleanupcode (new): 6:34 ``` So the newer versions got faster. And cleanupcode still takes the most time. In this PR, 722 of 1020 files have changed, which is 70% of the total codebase. Because during PR cibuild, we run cleanupcode only on changed files (in chunks), this PR takes very long and sometimes times out. This is an exceptional case, future PRs shouldn't touch so many files. So in the end, I believe this update is the best way forward. * Package updates * Review non-public TryXXX methods * Addressed review feedback
1 parent 4a2abe9 commit f0d638f

File tree

710 files changed

+9787
-6729
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

710 files changed

+9787
-6729
lines changed

.config/dotnet-tools.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"isRoot": true,
44
"tools": {
55
"jetbrains.resharper.globaltools": {
6-
"version": "2021.1.4",
6+
"version": "2021.2.2",
77
"commands": [
88
"jb"
99
]

Build.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ function CheckLastExitCode {
88

99
function RunInspectCode {
1010
$outputPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), 'jetbrains-inspectcode-results.xml')
11-
dotnet jb inspectcode JsonApiDotNetCore.sln --output="$outputPath" --profile=WarningSeverities.DotSettings --properties:Configuration=Release --severity=WARNING --verbosity=WARN -dsl=GlobalAll -dsl=SolutionPersonal -dsl=ProjectPersonal
11+
dotnet jb inspectcode JsonApiDotNetCore.sln --no-build --output="$outputPath" --profile=WarningSeverities.DotSettings --properties:Configuration=Release --severity=WARNING --verbosity=WARN -dsl=GlobalAll -dsl=SolutionPersonal -dsl=ProjectPersonal
1212
CheckLastExitCode
1313

1414
[xml]$xml = Get-Content "$outputPath"

CSharpGuidelinesAnalyzer.config

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<cSharpGuidelinesAnalyzerSettings>
33
<setting rule="AV1561" name="MaxParameterCount" value="6" />
4-
<setting rule="AV1561" name="MaxConstructorParameterCount" value="12" />
4+
<setting rule="AV1561" name="MaxConstructorParameterCount" value="13" />
55
</cSharpGuidelinesAnalyzerSettings>

Directory.Build.props

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
<NpgsqlPostgreSQLVersion>5.0.*</NpgsqlPostgreSQLVersion>
77
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodingGuidelines.ruleset</CodeAnalysisRuleSet>
88
<WarningLevel>9999</WarningLevel>
9+
<Nullable>enable</Nullable>
910
</PropertyGroup>
1011

1112
<ItemGroup>
12-
<PackageReference Include="JetBrains.Annotations" Version="2021.1.0" PrivateAssets="All" />
13+
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" PrivateAssets="All" />
1314
<PackageReference Include="CSharpGuidelinesAnalyzer" Version="3.7.0" PrivateAssets="All" />
1415
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CSharpGuidelinesAnalyzer.config" Visible="False" />
1516
</ItemGroup>
@@ -24,9 +25,9 @@
2425
<PropertyGroup>
2526
<BogusVersion>33.1.1</BogusVersion>
2627
<CoverletVersion>3.1.0</CoverletVersion>
27-
<FluentAssertionsVersion>6.1.0</FluentAssertionsVersion>
28+
<FluentAssertionsVersion>6.2.0</FluentAssertionsVersion>
2829
<MoqVersion>4.16.1</MoqVersion>
2930
<XUnitVersion>2.4.*</XUnitVersion>
30-
<TestSdkVersion>16.11.0</TestSdkVersion>
31+
<TestSdkVersion>17.0.0</TestSdkVersion>
3132
</PropertyGroup>
3233
</Project>

README.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,13 @@ public class Startup
8787
The following chart should help you pick the best version, based on your environment.
8888
See also our [versioning policy](./VERSIONING_POLICY.md).
8989

90-
| .NET version | EF Core version | JsonApiDotNetCore version |
91-
| ------------ | --------------- | ------------------------- |
92-
| Core 2.x | 2.x | 3.x |
93-
| Core 3.1 | 3.1 | 4.x |
94-
| Core 3.1 | 5 | 4.x |
95-
| 5 | 5 | 4.x or 5.x |
96-
| 6 | 6 | 5.x |
90+
| .NET version | Entity Framework Core version | JsonApiDotNetCore version |
91+
| ------------ | ----------------------------- | ------------------------- |
92+
| Core 2.x | 2.x | 3.x |
93+
| Core 3.1 | 3.1 | 4.x |
94+
| Core 3.1 | 5 | 4.x |
95+
| 5 | 5 | 4.x or 5.x |
96+
| 6 | 6 | 5.x |
9797

9898
## Contributing
9999

ROADMAP.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The need for breaking changes has blocked several efforts in the v4.x release, s
2121
- [x] Optimized delete to-many [#1030](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1030)
2222
- [x] Support System.Text.Json [#664](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/664) [#999](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/999) [1077](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1077) [1078](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1078)
2323
- [x] Optimize IIdentifiable to ResourceObject conversion [#1028](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1028) [#1024](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1024) [#233](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/233)
24-
- [ ] Nullable reference types [#1029](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1029)
24+
- [x] Nullable reference types [#1029](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1029)
2525

2626
Aside from the list above, we have interest in the following topics. It's too soon yet to decide whether they'll make it into v5.x or in a later major version.
2727

benchmarks/BenchmarkResource.cs

-16
This file was deleted.

benchmarks/BenchmarkResourcePublicNames.cs

-10
This file was deleted.

benchmarks/Benchmarks.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</ItemGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
12+
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
1313
<PackageReference Include="Moq" Version="$(MoqVersion)" />
1414
</ItemGroup>
1515
</Project>

benchmarks/DependencyFactory.cs

-18
This file was deleted.

benchmarks/Deserialization/DeserializationBenchmarkBase.cs

+16-14
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public abstract class DeserializationBenchmarkBase
2121
protected DeserializationBenchmarkBase()
2222
{
2323
var options = new JsonApiOptions();
24-
IResourceGraph resourceGraph = new ResourceGraphBuilder(options, NullLoggerFactory.Instance).Add<ResourceA>().Build();
24+
IResourceGraph resourceGraph = new ResourceGraphBuilder(options, NullLoggerFactory.Instance).Add<IncomingResource>().Build();
2525
options.SerializerOptions.Converters.Add(new ResourceObjectConverter(resourceGraph));
2626
SerializerReadOptions = ((IJsonApiOptions)options).SerializerReadOptions;
2727

@@ -30,7 +30,9 @@ protected DeserializationBenchmarkBase()
3030
var resourceDefinitionAccessor = new ResourceDefinitionAccessor(resourceGraph, serviceContainer);
3131

3232
serviceContainer.AddService(typeof(IResourceDefinitionAccessor), resourceDefinitionAccessor);
33-
serviceContainer.AddService(typeof(IResourceDefinition<ResourceA, int>), new JsonApiResourceDefinition<ResourceA, int>(resourceGraph));
33+
34+
serviceContainer.AddService(typeof(IResourceDefinition<IncomingResource, int>),
35+
new JsonApiResourceDefinition<IncomingResource, int>(resourceGraph));
3436

3537
// ReSharper disable once VirtualMemberCallInConstructor
3638
JsonApiRequest request = CreateJsonApiRequest(resourceGraph);
@@ -56,7 +58,7 @@ protected DeserializationBenchmarkBase()
5658
protected abstract JsonApiRequest CreateJsonApiRequest(IResourceGraph resourceGraph);
5759

5860
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
59-
public sealed class ResourceA : Identifiable<int>
61+
public sealed class IncomingResource : Identifiable<int>
6062
{
6163
[Attr]
6264
public bool Attribute01 { get; set; }
@@ -74,7 +76,7 @@ public sealed class ResourceA : Identifiable<int>
7476
public float? Attribute05 { get; set; }
7577

7678
[Attr]
77-
public string Attribute06 { get; set; }
79+
public string Attribute06 { get; set; } = null!;
7880

7981
[Attr]
8082
public DateTime? Attribute07 { get; set; }
@@ -89,34 +91,34 @@ public sealed class ResourceA : Identifiable<int>
8991
public DayOfWeek Attribute10 { get; set; }
9092

9193
[HasOne]
92-
public ResourceA Single1 { get; set; }
94+
public IncomingResource Single1 { get; set; } = null!;
9395

9496
[HasOne]
95-
public ResourceA Single2 { get; set; }
97+
public IncomingResource Single2 { get; set; } = null!;
9698

9799
[HasOne]
98-
public ResourceA Single3 { get; set; }
100+
public IncomingResource Single3 { get; set; } = null!;
99101

100102
[HasOne]
101-
public ResourceA Single4 { get; set; }
103+
public IncomingResource Single4 { get; set; } = null!;
102104

103105
[HasOne]
104-
public ResourceA Single5 { get; set; }
106+
public IncomingResource Single5 { get; set; } = null!;
105107

106108
[HasMany]
107-
public ISet<ResourceA> Multi1 { get; set; }
109+
public ISet<IncomingResource> Multi1 { get; set; } = null!;
108110

109111
[HasMany]
110-
public ISet<ResourceA> Multi2 { get; set; }
112+
public ISet<IncomingResource> Multi2 { get; set; } = null!;
111113

112114
[HasMany]
113-
public ISet<ResourceA> Multi3 { get; set; }
115+
public ISet<IncomingResource> Multi3 { get; set; } = null!;
114116

115117
[HasMany]
116-
public ISet<ResourceA> Multi4 { get; set; }
118+
public ISet<IncomingResource> Multi4 { get; set; } = null!;
117119

118120
[HasMany]
119-
public ISet<ResourceA> Multi5 { get; set; }
121+
public ISet<IncomingResource> Multi5 { get; set; } = null!;
120122
}
121123
}
122124
}

0 commit comments

Comments
 (0)