THRIFT-5832 Drop net6 support and add net9 instead
Client: netstd
Patch: Jens Geyer
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4fcdabb..a740507 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -314,7 +314,7 @@
 #      the sdk is installed by default, but keep this step for reference
 #      - name: Set up .NET SDK
 #        run: |
-#          sudo apt-get install -y --no-install-recommends dotnet-sdk-8.0
+#          sudo apt-get install -y --no-install-recommends dotnet-sdk-9.0
 
       - name: Run bootstrap
         run: ./bootstrap.sh
diff --git a/build/docker/README.md b/build/docker/README.md
index 9e972b8..6b535df 100644
--- a/build/docker/README.md
+++ b/build/docker/README.md
@@ -188,7 +188,7 @@
 | java      | 17            | 17            |       |
 | js        | Node.js 16.20.2, npm 8.19.4 | Node.js 16.20.2, npm 8.19.4 |     |
 | lua       | 5.2.4         | 5.2.4         | Lua 5.3: see THRIFT-4386 |
-| netstd    | 8.0.200       | 8.0.200       |       |
+| netstd    | 9.0           | 9.0           |       |
 | nodejs    | 16.20.2       | 16.20.2       |       |
 | ocaml     | 4.08.1        | 4.13.1        |       |
 | perl      | 5.30.0        | 5.34.0        |       |
diff --git a/build/docker/ubuntu-focal/Dockerfile b/build/docker/ubuntu-focal/Dockerfile
index 84bbee1..02f84c6 100644
--- a/build/docker/ubuntu-focal/Dockerfile
+++ b/build/docker/ubuntu-focal/Dockerfile
@@ -139,10 +139,10 @@
 
 RUN apt-get install -y --no-install-recommends \
       `# dotnet core dependencies` \
-      dotnet-sdk-8.0 \
-      dotnet-runtime-8.0 \
-      aspnetcore-runtime-8.0 \
-      dotnet-apphost-pack-8.0
+      dotnet-sdk-9.0 \
+      dotnet-runtime-9.0 \
+      aspnetcore-runtime-9.0 \
+      dotnet-apphost-pack-9.0
 
 # Erlang dependencies
 ARG ERLANG_OTP_VERSION=23.3.4.11
diff --git a/build/docker/ubuntu-jammy/Dockerfile b/build/docker/ubuntu-jammy/Dockerfile
index 0505bf5..c6176a8 100644
--- a/build/docker/ubuntu-jammy/Dockerfile
+++ b/build/docker/ubuntu-jammy/Dockerfile
@@ -138,10 +138,10 @@
 
 RUN apt-get install -y --no-install-recommends \
   `# dotnet core dependencies` \
-  dotnet-sdk-8.0 \
-  dotnet-runtime-8.0 \
-  aspnetcore-runtime-8.0 \
-  dotnet-apphost-pack-8.0
+  dotnet-sdk-9.0 \
+  dotnet-runtime-9.0 \
+  aspnetcore-runtime-9.0 \
+  dotnet-apphost-pack-9.0
 
 # Erlang dependencies
 ARG ERLANG_OTP_VERSION=25.3.2.9
diff --git a/build/veralign.sh b/build/veralign.sh
index 8664a46..b929f1f 100755
--- a/build/veralign.sh
+++ b/build/veralign.sh
@@ -72,8 +72,8 @@
 FILES[lib/lua/Thrift.lua]=simpleReplace
 FILES[lib/netstd/Thrift/Properties/AssemblyInfo.cs]=simpleReplace
 FILES[lib/netstd/Benchmarks/Thrift.Benchmarks/Thrift.Benchmarks.csproj]=simpleReplace
-FILES[lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Thrift.Compile.net6.csproj]=simpleReplace
 FILES[lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net8/Thrift.Compile.net8.csproj]=simpleReplace
+FILES[lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Thrift.Compile.net9.csproj]=simpleReplace
 FILES[lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/Thrift.Compile.netstd2.csproj]=simpleReplace
 FILES[lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj]=simpleReplace
 FILES[lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj]=simpleReplace
diff --git a/compiler/cpp/src/thrift/generate/t_netstd_generator.cc b/compiler/cpp/src/thrift/generate/t_netstd_generator.cc
index 95c9118..c35ce56 100644
--- a/compiler/cpp/src/thrift/generate/t_netstd_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_netstd_generator.cc
@@ -82,8 +82,8 @@
         else if (iter->first.compare("no_deepcopy") == 0) {
           suppress_deepcopy = true;
         }
-        else if (iter->first.compare("net6") == 0) {
-          target_net_version = 6;
+        else if (iter->first.compare("net9") == 0) {
+          target_net_version = 9;
         }
         else if (iter->first.compare("net8") == 0) {
           target_net_version = 8;
@@ -202,12 +202,12 @@
 
 void t_netstd_generator::pragmas_and_directives(ostream& out)
 {
-    if( target_net_version >= 8) {
+    if( target_net_version >= 9) {
+        out << "// targeting net 9" << '\n';
+        out << "#if( !NET9_0_OR_GREATER)" << '\n';
+    } else if( target_net_version >= 8) {
         out << "// targeting net 8" << '\n';
-        out << "#if( !NET8_0_OR_GREATER)" << '\n';
-    } else if( target_net_version >= 6) {
-        out << "// targeting net 6" << '\n';
-        out << "#if( NET8_0_OR_GREATER || !NET6_0_OR_GREATER)" << '\n';
+        out << "#if( NET9_0_OR_GREATER || !NET8_0_OR_GREATER)" << '\n';
     } else {
         out << "// targeting netstandard 2.x" << '\n';
         out << "#if(! NETSTANDARD2_0_OR_GREATER)" << '\n';
@@ -224,12 +224,20 @@
     // this one must be first
     out << "#pragma warning disable IDE0079  // remove unnecessary pragmas" << '\n';
 
-    if( target_net_version >= 8) {
-      out << "#pragma warning disable IDE0290  // use primary CTOR" << '\n';
-    } else {
+    if( target_net_version >= 9) {
+      out << "#pragma warning disable IDE0130  // unexpected folder structure" << '\n';
+    }
+
+    if( target_net_version < 8) {
       out << "#pragma warning disable IDE0017  // object init can be simplified" << '\n';
       out << "#pragma warning disable IDE0028  // collection init can be simplified" << '\n';
+      out << "#pragma warning disable IDE0305  // collection init can be simplified" << '\n';
+      out << "#pragma warning disable IDE0034  // simplify default expression" << '\n';
+      out << "#pragma warning disable IDE0066  // use switch expression" << '\n';
+      out << "#pragma warning disable IDE0090  // simplify new expression" << '\n';
     }
+
+    out << "#pragma warning disable IDE0290  // use primary CTOR" << '\n';
     out << "#pragma warning disable IDE1006  // parts of the code use IDL spelling" << '\n';
     out << "#pragma warning disable CA1822   // empty " << DEEP_COPY_METHOD_NAME << "() methods still non-static" << '\n';
 
@@ -3992,8 +4000,8 @@
     "    serial:          Add serialization support to generated classes.\n"
     "    union:           Use new union typing, which includes a static read function for union types.\n"
     "    pascal:          Generate Pascal Case property names according to Microsoft naming convention.\n"
-    "    net6:            Enable features that require net6 and C# 8 or higher.\n"
     "    net8:            Enable features that require net8 and C# 12 or higher.\n"
+    "    net9:            Enable features that require net9 and C# 13 or higher.\n"
     "    no_deepcopy:     Suppress generation of " + DEEP_COPY_METHOD_NAME + "() method.\n"
     "    async_postfix:   Append \"Async\" to all service methods (maintains compatibility with existing code).\n"
 )
diff --git a/compiler/cpp/src/thrift/generate/t_netstd_generator.h b/compiler/cpp/src/thrift/generate/t_netstd_generator.h
index c65414a..4623b12 100644
--- a/compiler/cpp/src/thrift/generate/t_netstd_generator.h
+++ b/compiler/cpp/src/thrift/generate/t_netstd_generator.h
@@ -175,7 +175,7 @@
   bool wcf_;
   bool use_pascal_case_properties;
   bool suppress_deepcopy;
-  int  target_net_version;  // 0 = any, 6 = net6, 8 = net8
+  int  target_net_version;  // 0 = any, 6 = net6, 8 = net8, etc.
   bool add_async_postfix;
 
   const std::string CSHARP_KEYWORDS[101] = {
diff --git a/debian/copyright b/debian/copyright
index 76212b9..7245167 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -74,11 +74,10 @@
   lib/netstd/Thrift/Transport/TTransportFactory.cs
   lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/Properties/AssemblyInfo.cs
   lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/Thrift.Compile.netstd2.csproj
-  lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Properties/AssemblyInfo.cs
-  lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Thrift.Compile.net6.csproj
   lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net8/Properties/AssemblyInfo.cs
   lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net8/Thrift.Compile.net8.csproj
-  lib/rb/lib/thrift.rb
+  lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Properties/AssemblyInfo.cs
+  lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Thrift.Compile.net9.csproj
   lib/st/README.md
   lib/st/thrift.st
   lib/cpp/test/OptionalRequiredTest.cpp
diff --git a/lib/netstd/Benchmarks/Thrift.Benchmarks/Thrift.Benchmarks.csproj b/lib/netstd/Benchmarks/Thrift.Benchmarks/Thrift.Benchmarks.csproj
index 7877e6b..9d25c72 100644
--- a/lib/netstd/Benchmarks/Thrift.Benchmarks/Thrift.Benchmarks.csproj
+++ b/lib/netstd/Benchmarks/Thrift.Benchmarks/Thrift.Benchmarks.csproj
@@ -20,7 +20,7 @@
   
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <Version>0.22.0</Version>
     <LangVersion>latestMajor</LangVersion>
     <TieredCompilation>false</TieredCompilation>
@@ -29,7 +29,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/lib/netstd/Makefile.am b/lib/netstd/Makefile.am
index e7142b0..ed855e4 100644
--- a/lib/netstd/Makefile.am
+++ b/lib/netstd/Makefile.am
@@ -24,7 +24,7 @@
 
 check-local:
 	$(DOTNETCORE) test Tests/Thrift.Compile.Tests/Thrift.Compile.net8/Thrift.Compile.net8.csproj
-	$(DOTNETCORE) test Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Thrift.Compile.net6.csproj
+	$(DOTNETCORE) test Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Thrift.Compile.net9.csproj
 	$(DOTNETCORE) test Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/Thrift.Compile.netstd2.csproj
 	$(DOTNETCORE) test Tests/Thrift.Tests/Thrift.Tests.csproj
 	$(DOTNETCORE) test Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj
@@ -40,8 +40,8 @@
 	$(RM) -r Tests/Thrift.IntegrationTests/obj
 	$(RM) -r Tests/Thrift.Compile.Tests/Thrift.Compile.net8/bin
 	$(RM) -r Tests/Thrift.Compile.Tests/Thrift.Compile.net8/obj
-	$(RM) -r Tests/Thrift.Compile.Tests/Thrift.Compile.net6/bin
-	$(RM) -r Tests/Thrift.Compile.Tests/Thrift.Compile.net6/obj
+	$(RM) -r Tests/Thrift.Compile.Tests/Thrift.Compile.net9/bin
+	$(RM) -r Tests/Thrift.Compile.Tests/Thrift.Compile.net9/obj
 	$(RM) -r Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/bin
 	$(RM) -r Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/obj
 
@@ -58,7 +58,7 @@
 	Tests/Thrift.Compile.Tests/CassandraTest.thrift \
 	Tests/Thrift.Compile.Tests/optional_required_default.thrift \
 	Tests/Thrift.Compile.Tests/Thrift.Compile.net8/Thrift.Compile.net8.csproj \
-	Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Thrift.Compile.net6.csproj \
+	Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Thrift.Compile.net9.csproj \
 	Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/Thrift.Compile.netstd2.csproj \
 	Tests/Thrift.Tests/Collections \
 	Tests/Thrift.Tests/DataModel \
diff --git a/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net8/Thrift.Compile.net8.csproj b/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net8/Thrift.Compile.net8.csproj
index 49fc828..2695dde 100644
--- a/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net8/Thrift.Compile.net8.csproj
+++ b/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net8/Thrift.Compile.net8.csproj
@@ -38,7 +38,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="System.ServiceModel.Primitives" Version="8.0.0" />
+    <PackageReference Include="System.ServiceModel.Primitives" Version="8.1.0" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Impl/Thrift5253/MyService.cs b/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Impl/Thrift5253/MyService.cs
similarity index 97%
rename from lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Impl/Thrift5253/MyService.cs
rename to lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Impl/Thrift5253/MyService.cs
index 4a2f40d..86a7617 100644
--- a/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Impl/Thrift5253/MyService.cs
+++ b/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Impl/Thrift5253/MyService.cs
@@ -22,7 +22,7 @@
 using System.Threading.Tasks;
 using Thrift5253;
 
-namespace Thrift.Compile.net6.Impl.Thrift5253
+namespace Thrift.Compile.net9.Impl.Thrift5253
 {
     class MyServiceImpl : MyService.IAsync
     {
diff --git a/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Properties/AssemblyInfo.cs b/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Properties/AssemblyInfo.cs
similarity index 96%
rename from lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Properties/AssemblyInfo.cs
rename to lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Properties/AssemblyInfo.cs
index 1d612f6..5bd3c6f 100644
--- a/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Properties/AssemblyInfo.cs
+++ b/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Properties/AssemblyInfo.cs
@@ -37,4 +37,4 @@
 
 // The following GUID is for the ID of the typelib if this project is exposed to COM
 
-[assembly: Guid("dbc2c69a-93f1-45f4-8ecf-36ef13fc3482")]
\ No newline at end of file
+[assembly: Guid("1b468b7a-a53b-46de-90da-5f9ad7707ef4")]
\ No newline at end of file
diff --git a/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Thrift.Compile.net6.csproj b/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Thrift.Compile.net9.csproj
similarity index 85%
rename from lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Thrift.Compile.net6.csproj
rename to lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Thrift.Compile.net9.csproj
index 6228aab..cb6c465 100644
--- a/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net6/Thrift.Compile.net6.csproj
+++ b/lib/netstd/Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Thrift.Compile.net9.csproj
@@ -21,11 +21,11 @@
   <PropertyGroup>
     <ThriftVersion>0.22.0</ThriftVersion>
     <ThriftVersionOutput>Thrift version $(ThriftVersion)</ThriftVersionOutput>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <LangVersion>latestMajor</LangVersion>
     <Version>$(ThriftVersion).0</Version>
-    <AssemblyName>Thrift.Compile.net6</AssemblyName>
-    <PackageId>Thrift.Compile.net6</PackageId>
+    <AssemblyName>Thrift.Compile.net9</AssemblyName>
+    <PackageId>Thrift.Compile.net9</PackageId>
     <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
     <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
     <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
@@ -38,9 +38,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="System.Formats.Asn1" Version="6.0.1" />
-    <PackageReference Include="System.Security.Cryptography.Pkcs" Version="6.0.4" />
-    <PackageReference Include="System.ServiceModel.Primitives" Version="6.0.0" />
+    <PackageReference Include="System.ServiceModel.Primitives" Version="8.1.0" />
   </ItemGroup>
 
   <ItemGroup>
@@ -74,17 +72,17 @@
     <Error Condition="$('$(ThriftBinaryVersion)'::StartsWith('$(ThriftVersionOutput)')) == true" Text="Thrift version returned: '$(ThriftBinaryVersion)' is not equal to the projects version '$(ThriftVersionOutput)'." />
     <Message Importance="high" Text="Generating tests with thrift binary: '$(PathToThrift)'" />
     <!-- Generate the thrift test files -->
-    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ../CassandraTest.thrift" />
-    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ../optional_required_default.thrift" />
-    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ../name_conflicts.thrift" />
-    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ../../../../../test/ThriftTest.thrift" />
-    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ../../../../../contrib/fb303/if/fb303.thrift" />
-    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ../Thrift5253.thrift" />
-    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ../Thrift5320.thrift" />
-    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ../Thrift5382.thrift" />
-    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net6 -r ../Thrift5795.thrift" />
+    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net9 -r ../CassandraTest.thrift" />
+    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net9 -r ../optional_required_default.thrift" />
+    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net9 -r ../name_conflicts.thrift" />
+    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net9 -r ../../../../../test/ThriftTest.thrift" />
+    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net9 -r ../../../../../contrib/fb303/if/fb303.thrift" />
+    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net9 -r ../Thrift5253.thrift" />
+    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net9 -r ../Thrift5320.thrift" />
+    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net9 -r ../Thrift5382.thrift" />
+    <Exec Command="$(PathToThrift) -gen netstd:wcf,union,serial,net9 -r ../Thrift5795.thrift" />
     <!-- special options (see ticket) -->
-    <Exec Command="$(PathToThrift) -gen netstd:net6                  -r ../Thrift5794.thrift" />
+    <Exec Command="$(PathToThrift) -gen netstd:net9                  -r ../Thrift5794.thrift" />
   </Target>
 
 </Project>
diff --git a/lib/netstd/Tests/Thrift.Compile.Tests/Thrift5794.thrift b/lib/netstd/Tests/Thrift.Compile.Tests/Thrift5794.thrift
index 7263360..6a73a9b 100644
--- a/lib/netstd/Tests/Thrift.Compile.Tests/Thrift5794.thrift
+++ b/lib/netstd/Tests/Thrift.Compile.Tests/Thrift5794.thrift
@@ -19,7 +19,7 @@
 
 // Testcase for THRIFT-5794 uncompilable code generated w/o net8 option
 
-namespace * Thrift5794.net6
+namespace * Thrift5794
 
 struct foo {
   1: double a;
diff --git a/lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj b/lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj
index d7d52c8..ebf6843 100644
--- a/lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj
+++ b/lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj
@@ -19,7 +19,7 @@
   -->
 
   <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <LangVersion>latestMajor</LangVersion>
     <AssemblyName>Thrift.IntegrationTests</AssemblyName>
     <PackageId>Thrift.IntegrationTests</PackageId>
@@ -36,10 +36,10 @@
 
   <ItemGroup>
     <PackageReference Include="CompareNETObjects" Version="4.83.0" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
-    <PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
-    <PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
-    <PackageReference Include="System.ServiceModel.Primitives" Version="8.0.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
+    <PackageReference Include="MSTest.TestAdapter" Version="3.6.3" />
+    <PackageReference Include="MSTest.TestFramework" Version="3.6.3" />
+    <PackageReference Include="System.ServiceModel.Primitives" Version="8.1.0" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\Thrift\Thrift.csproj" />
diff --git a/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj b/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj
index c66558c..30e3208 100644
--- a/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj
+++ b/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj
@@ -19,7 +19,7 @@
   -->
 
   <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <LangVersion>latestMajor</LangVersion>
     <Version>0.22.0.0</Version>
     <Nullable>enable</Nullable>
@@ -27,15 +27,15 @@
 
   <ItemGroup>
     <PackageReference Include="CompareNETObjects" Version="4.83.0" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
-    <PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
-    <PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
-    <PackageReference Include="NSubstitute" Version="5.1.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
+    <PackageReference Include="MSTest.TestAdapter" Version="3.6.3" />
+    <PackageReference Include="MSTest.TestFramework" Version="3.6.3" />
+    <PackageReference Include="NSubstitute" Version="5.3.0" />
   </ItemGroup>
 
   <ItemGroup>
     <ProjectReference Include="..\..\Thrift\Thrift.csproj" />
-    <ProjectReference Include="..\Thrift.Compile.Tests\Thrift.Compile.net8\Thrift.Compile.net8.csproj" />
+    <ProjectReference Include="..\Thrift.Compile.Tests\Thrift.Compile.net9\Thrift.Compile.net9.csproj" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/lib/netstd/Thrift.sln b/lib/netstd/Thrift.sln
index dbd4b67..feae3fe 100644
--- a/lib/netstd/Thrift.sln
+++ b/lib/netstd/Thrift.sln
@@ -14,12 +14,12 @@
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.Benchmarks", "Benchmarks\Thrift.Benchmarks\Thrift.Benchmarks.csproj", "{D0559DFF-6632-446C-9EFC-C750DA20B1D9}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.Compile.net6", "Tests\Thrift.Compile.Tests\Thrift.Compile.net6\Thrift.Compile.net6.csproj", "{2AFCF575-381C-46CA-8006-D5762365F0E3}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.Compile.netstd2", "Tests\Thrift.Compile.Tests\Thrift.Compile.netstd2\Thrift.Compile.netstd2.csproj", "{58F72FB9-09F5-4D0F-B0B4-36605670BF72}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.Compile.net8", "Tests\Thrift.Compile.Tests\Thrift.Compile.net8\Thrift.Compile.net8.csproj", "{9A3E11C0-72FD-4DA0-8E61-C7746E751DF7}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.Compile.net9", "Tests\Thrift.Compile.Tests\Thrift.Compile.net9\Thrift.Compile.net9.csproj", "{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -78,18 +78,6 @@
 		{D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Release|x64.Build.0 = Release|Any CPU
 		{D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Release|x86.ActiveCfg = Release|Any CPU
 		{D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Release|x86.Build.0 = Release|Any CPU
-		{2AFCF575-381C-46CA-8006-D5762365F0E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{2AFCF575-381C-46CA-8006-D5762365F0E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{2AFCF575-381C-46CA-8006-D5762365F0E3}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{2AFCF575-381C-46CA-8006-D5762365F0E3}.Debug|x64.Build.0 = Debug|Any CPU
-		{2AFCF575-381C-46CA-8006-D5762365F0E3}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{2AFCF575-381C-46CA-8006-D5762365F0E3}.Debug|x86.Build.0 = Debug|Any CPU
-		{2AFCF575-381C-46CA-8006-D5762365F0E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{2AFCF575-381C-46CA-8006-D5762365F0E3}.Release|Any CPU.Build.0 = Release|Any CPU
-		{2AFCF575-381C-46CA-8006-D5762365F0E3}.Release|x64.ActiveCfg = Release|Any CPU
-		{2AFCF575-381C-46CA-8006-D5762365F0E3}.Release|x64.Build.0 = Release|Any CPU
-		{2AFCF575-381C-46CA-8006-D5762365F0E3}.Release|x86.ActiveCfg = Release|Any CPU
-		{2AFCF575-381C-46CA-8006-D5762365F0E3}.Release|x86.Build.0 = Release|Any CPU
 		{58F72FB9-09F5-4D0F-B0B4-36605670BF72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{58F72FB9-09F5-4D0F-B0B4-36605670BF72}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{58F72FB9-09F5-4D0F-B0B4-36605670BF72}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -114,6 +102,18 @@
 		{9A3E11C0-72FD-4DA0-8E61-C7746E751DF7}.Release|x64.Build.0 = Release|Any CPU
 		{9A3E11C0-72FD-4DA0-8E61-C7746E751DF7}.Release|x86.ActiveCfg = Release|Any CPU
 		{9A3E11C0-72FD-4DA0-8E61-C7746E751DF7}.Release|x86.Build.0 = Release|Any CPU
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}.Debug|x64.Build.0 = Debug|Any CPU
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}.Debug|x86.Build.0 = Debug|Any CPU
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}.Release|Any CPU.Build.0 = Release|Any CPU
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}.Release|x64.ActiveCfg = Release|Any CPU
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}.Release|x64.Build.0 = Release|Any CPU
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}.Release|x86.ActiveCfg = Release|Any CPU
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -122,9 +122,9 @@
 		{837F4084-AAD7-45F5-BC96-10E05A669DB4} = {ED5A45B0-07D1-4507-96B7-83FBD3D031CA}
 		{0790D388-1A3C-4423-8CF2-C97074A8B68B} = {ED5A45B0-07D1-4507-96B7-83FBD3D031CA}
 		{D0559DFF-6632-446C-9EFC-C750DA20B1D9} = {BF7B896B-8BB6-447C-84F8-26871882A14A}
-		{2AFCF575-381C-46CA-8006-D5762365F0E3} = {ED5A45B0-07D1-4507-96B7-83FBD3D031CA}
 		{58F72FB9-09F5-4D0F-B0B4-36605670BF72} = {ED5A45B0-07D1-4507-96B7-83FBD3D031CA}
 		{9A3E11C0-72FD-4DA0-8E61-C7746E751DF7} = {ED5A45B0-07D1-4507-96B7-83FBD3D031CA}
+		{967C48D1-1807-41E5-B4BF-DFA6414CC9F2} = {ED5A45B0-07D1-4507-96B7-83FBD3D031CA}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {FD20BC4A-0109-41D8-8C0C-893E784D7EF9}
diff --git a/lib/netstd/Thrift/.editorconfig b/lib/netstd/Thrift/.editorconfig
index 82ff4a3..5f43ecf 100644
--- a/lib/netstd/Thrift/.editorconfig
+++ b/lib/netstd/Thrift/.editorconfig
@@ -4,6 +4,6 @@
 dotnet_diagnostic.CS1591.severity = silent
 
 # silence certain yet unfixed false positives for net8
-dotnet_diagnostic.IDE0290.severity = silent
-dotnet_diagnostic.CA1510.severity = silent
-dotnet_diagnostic.CA1513.severity = silent
+#dotnet_diagnostic.IDE0290.severity = silent
+#dotnet_diagnostic.CA1510.severity = silent
+#dotnet_diagnostic.CA1513.severity = silent
diff --git a/lib/netstd/Thrift/GlobalSuppressions.cs b/lib/netstd/Thrift/GlobalSuppressions.cs
index eb7d3d7..cb5fc01 100644
--- a/lib/netstd/Thrift/GlobalSuppressions.cs
+++ b/lib/netstd/Thrift/GlobalSuppressions.cs
@@ -28,4 +28,7 @@
 [assembly: SuppressMessage("Style", "IDE0066", Justification = "compatibility", Scope = "module")]
 [assembly: SuppressMessage("Style", "IDE0090", Justification = "compatibility", Scope = "module")]
 [assembly: SuppressMessage("Style", "IDE0063", Justification = "compatibility", Scope = "module")]
+[assembly: SuppressMessage("Style", "IDE0130", Justification = "compatibility", Scope = "module")]
+[assembly: SuppressMessage("Style", "IDE0290", Justification = "compatibility", Scope = "module")]
+[assembly: SuppressMessage("Style", "CS0114", Justification = "known issue, see JIRA ticket", Scope = "module")]
 
diff --git a/lib/netstd/Thrift/Protocol/TJSONProtocol.cs b/lib/netstd/Thrift/Protocol/TJSONProtocol.cs
index 64308d6..02cf3ed 100644
--- a/lib/netstd/Thrift/Protocol/TJSONProtocol.cs
+++ b/lib/netstd/Thrift/Protocol/TJSONProtocol.cs
@@ -29,6 +29,8 @@
 
 #pragma warning disable IDE0079 // net20 - unneeded suppression
 #pragma warning disable IDE0290 // net8 - primary CTOR
+#pragma warning disable IDE0305 // net9 - collection init
+#pragma warning disable IDE0300 // net9 - collection init
 
 namespace Thrift.Protocol
 {
diff --git a/lib/netstd/Thrift/Thrift.csproj b/lib/netstd/Thrift/Thrift.csproj
index 8c2e6b9..9279918 100644
--- a/lib/netstd/Thrift/Thrift.csproj
+++ b/lib/netstd/Thrift/Thrift.csproj
@@ -19,7 +19,7 @@
   -->
 
   <PropertyGroup>
-    <TargetFrameworks>netstandard2.1;netstandard2.0;net6.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>netstandard2.1;netstandard2.0;net8.0;net9.0</TargetFrameworks>
     <AssemblyName>Thrift</AssemblyName>
     <PackageId>ApacheThrift</PackageId>
     <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
@@ -60,16 +60,18 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" Condition="'$(TargetFramework.StartsWith(`netstandard2.`))' == 'true'" />
-    <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
-    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
-    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
-    <PackageReference Include="System.IO.Pipes" Version="[4.3,)" />
+    <PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.0" />
     <PackageReference Include="System.IO.Pipes.AccessControl" Version="5.0.0" Condition="'$(TargetFramework.StartsWith(`netstandard2.`))' == 'true'" />
-    <PackageReference Include="System.Net.Http.WinHttpHandler" Version="8.0.1" />
-    <PackageReference Include="System.Net.NameResolution" Version="[4.3,)" />
-    <PackageReference Include="System.Net.Requests" Version="[4.3,)" />
-    <PackageReference Include="System.Net.Security" Version="4.3.2" />
-    <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
+    <PackageReference Include="System.Net.Http.WinHttpHandler" Version="9.0.0" />
+    <!--
+    <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.6.0" Condition="'$(TargetFramework.StartsWith(`netstandard2.`))' == 'true'" />
+    <PackageReference Include="System.Net.Requests" Version="4.3.0" Condition="'$(TargetFramework.StartsWith(`netstandard2.`))' == 'true'" />
+    <PackageReference Include="System.Net.NameResolution" Version="4.3.0" Condition="'$(TargetFramework.StartsWith(`netstandard2.`))' == 'true'" />
+    <PackageReference Include="System.Net.Security" Version="4.3.0" Condition="'$(TargetFramework.StartsWith(`netstandard2.`))' == 'true'" />
+    <PackageReference Include="System.IO.Pipes" Version="4.3.0" Condition="'$(TargetFramework.StartsWith(`netstandard2.`))' == 'true'" />
+    -->
   </ItemGroup>
 
   <ItemGroup>
@@ -84,18 +86,18 @@
     <None Include="..\README.md" Pack="true" PackagePath="\" />
   </ItemGroup>
 
-  <ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
-    <PackageReference Include="Microsoft.AspNetCore.Components.Web">
-      <Version>7.0.9</Version>
-    </PackageReference>
-  </ItemGroup>
-	
-	<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
+  <ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
 		<PackageReference Include="Microsoft.AspNetCore.Components.Web">
-			<Version>8.0.7</Version>
+			<Version>8.0.10</Version>
 		</PackageReference>
 	</ItemGroup>
 
+  <ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
+    <PackageReference Include="Microsoft.AspNetCore.Components.Web">
+      <Version>9.0.0</Version>
+    </PackageReference>
+  </ItemGroup>
+
   <Target Name="SetTFMAssemblyAttributesPath" BeforeTargets="GenerateTargetFrameworkMonikerAttribute">
     <PropertyGroup>
       <TargetFrameworkMonikerAssemblyAttributesPath>$(IntermediateOutputPath)$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)</TargetFrameworkMonikerAssemblyAttributesPath>
diff --git a/lib/netstd/Thrift/Transport/Client/TTlsSocketTransport.cs b/lib/netstd/Thrift/Transport/Client/TTlsSocketTransport.cs
index 0a51c9a..df18124 100644
--- a/lib/netstd/Thrift/Transport/Client/TTlsSocketTransport.cs
+++ b/lib/netstd/Thrift/Transport/Client/TTlsSocketTransport.cs
@@ -82,6 +82,10 @@
             }
         }
 
+        #if NET9_0_OR_GREATER
+        [Obsolete("SYSLIB0057: X509Certificate2 and X509Certificate constructors for binary and file content are obsolete")]
+        #pragma warning disable SYSLIB0057
+        #endif
         public TTlsSocketTransport(IPAddress host, int port, TConfiguration config,
             string certificatePath,
             RemoteCertificateValidationCallback certValidator = null,
@@ -94,6 +98,7 @@
                 sslProtocols)
         {
         }
+        #pragma warning restore SYSLIB0057
 
         public TTlsSocketTransport(IPAddress host, int port, TConfiguration config,
             X509Certificate2 certificate = null,
diff --git a/test/netstd/Client/Client.csproj b/test/netstd/Client/Client.csproj
index 78cc22a..d9f61f8 100644
--- a/test/netstd/Client/Client.csproj
+++ b/test/netstd/Client/Client.csproj
@@ -19,7 +19,7 @@
   -->
 
   <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <LangVersion>latestMajor</LangVersion>
     <AssemblyName>Client</AssemblyName>
     <PackageId>Client</PackageId>
@@ -35,9 +35,9 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="System.Net.Http.WinHttpHandler" Version="8.0.1" />
+    <PackageReference Include="System.Net.Http.WinHttpHandler" Version="9.0.0" />
     <PackageReference Include="System.Runtime.Serialization.Primitives" Version="[4.3,)" />
-    <PackageReference Include="System.ServiceModel.Primitives" Version="8.0.0" />
+    <PackageReference Include="System.ServiceModel.Primitives" Version="8.1.0" />
     <PackageReference Include="System.Threading" Version="[4.3,)" />
   </ItemGroup>
 
@@ -49,8 +49,8 @@
     <Exec Condition="'$(OS)' == 'Windows_NT'" Command="where thrift" ConsoleToMSBuild="true">
       <Output TaskParameter="ConsoleOutput" PropertyName="PathToThrift" />
     </Exec>
-    <Exec Condition="Exists('$(PathToThrift)')" Command="&quot;$(PathToThrift)&quot; -out $(ProjectDir) -gen netstd:wcf,union,serial,net8 -r ./../../ThriftTest.thrift" />
-    <Exec Condition="Exists('thrift')" Command="thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net8 -r ./../../ThriftTest.thrift" />
-    <Exec Condition="Exists('$(ProjectDir)/../../../compiler/cpp/thrift')" Command="$(ProjectDir)/../../../compiler/cpp/thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net8 -r ./../../ThriftTest.thrift" />
+    <Exec Condition="Exists('$(PathToThrift)')" Command="&quot;$(PathToThrift)&quot; -out $(ProjectDir) -gen netstd:wcf,union,serial,net9 -r ./../../ThriftTest.thrift" />
+    <Exec Condition="Exists('thrift')" Command="thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net9 -r ./../../ThriftTest.thrift" />
+    <Exec Condition="Exists('$(ProjectDir)/../../../compiler/cpp/thrift')" Command="$(ProjectDir)/../../../compiler/cpp/thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net9 -r ./../../ThriftTest.thrift" />
   </Target>
 </Project>
diff --git a/test/netstd/Client/TestClient.cs b/test/netstd/Client/TestClient.cs
index 7486712..26bd120 100644
--- a/test/netstd/Client/TestClient.cs
+++ b/test/netstd/Client/TestClient.cs
@@ -225,7 +225,8 @@
                     throw new FileNotFoundException($"Cannot find file: {clientCertName}");
                 }
 
-                var cert = new X509Certificate2(existingPath, "thrift");
+                //var cert = new X509Certificate2(existingPath, "thrift");
+                var cert = X509CertificateLoader.LoadPkcs12FromFile(existingPath, "thrift");
 
                 return cert;
             }
@@ -444,7 +445,7 @@
 
         public static string BytesToHex(byte[] data)
         {
-            return BitConverter.ToString(data).Replace("-", string.Empty);
+            return Convert.ToHexString(data);
         }
 
 
diff --git a/test/netstd/Server/Server.csproj b/test/netstd/Server/Server.csproj
index 766b7a9..130b466 100644
--- a/test/netstd/Server/Server.csproj
+++ b/test/netstd/Server/Server.csproj
@@ -19,7 +19,7 @@
   -->
 
   <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <LangVersion>latestMajor</LangVersion>
     <AssemblyName>Server</AssemblyName>
     <PackageId>Server</PackageId>
@@ -36,9 +36,9 @@
 
   <ItemGroup>
     <PackageReference Include="System.IO.Pipes" Version="4.3.0" />
-    <PackageReference Include="System.Net.Http.WinHttpHandler" Version="8.0.1" />
+    <PackageReference Include="System.Net.Http.WinHttpHandler" Version="9.0.0" />
     <PackageReference Include="System.Runtime.Serialization.Primitives" Version="[4.3,)" />
-    <PackageReference Include="System.ServiceModel.Primitives" Version="8.0.0" />
+    <PackageReference Include="System.ServiceModel.Primitives" Version="8.1.0" />
     <PackageReference Include="System.Threading" Version="[4.3,)" />
   </ItemGroup>
 
@@ -50,8 +50,8 @@
     <Exec Condition="'$(OS)' == 'Windows_NT'" Command="where thrift" ConsoleToMSBuild="true">
       <Output TaskParameter="ConsoleOutput" PropertyName="PathToThrift" />
     </Exec>
-    <Exec Condition="Exists('$(PathToThrift)')" Command="&quot;$(PathToThrift)&quot; -out $(ProjectDir) -gen netstd:wcf,union,serial,net8 -r ./../../ThriftTest.thrift" />
-    <Exec Condition="Exists('thrift')" Command="thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net8 -r ./../../ThriftTest.thrift" />
-    <Exec Condition="Exists('$(ProjectDir)/../../../compiler/cpp/thrift')" Command="$(ProjectDir)/../../../compiler/cpp/thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net8 -r ./../../ThriftTest.thrift" />
+    <Exec Condition="Exists('$(PathToThrift)')" Command="&quot;$(PathToThrift)&quot; -out $(ProjectDir) -gen netstd:wcf,union,serial,net9 -r ./../../ThriftTest.thrift" />
+    <Exec Condition="Exists('thrift')" Command="thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net9 -r ./../../ThriftTest.thrift" />
+    <Exec Condition="Exists('$(ProjectDir)/../../../compiler/cpp/thrift')" Command="$(ProjectDir)/../../../compiler/cpp/thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net9 -r ./../../ThriftTest.thrift" />
   </Target>
 </Project>
diff --git a/test/netstd/Server/TestServer.cs b/test/netstd/Server/TestServer.cs
index 2dea418..54654c8 100644
--- a/test/netstd/Server/TestServer.cs
+++ b/test/netstd/Server/TestServer.cs
@@ -37,6 +37,7 @@
 
 #pragma warning disable IDE0063  // using can be simplified, we don't
 #pragma warning disable IDE0057  // substr can be simplified, we don't
+#pragma warning disable IDE0130  // unexpected folder structure
 
 namespace ThriftTest
 {
@@ -554,9 +555,10 @@
             {
                 throw new FileNotFoundException($"Cannot find file: {serverCertName}");
             }
-                                    
-            var cert = new X509Certificate2(existingPath, "thrift");
-                        
+
+            //var cert = new X509Certificate2(existingPath, "thrift");
+            var cert = X509CertificateLoader.LoadPkcs12FromFile(existingPath, "thrift");
+
             return cert;
         }
 
diff --git a/test/netstd/ThriftTest.sln b/test/netstd/ThriftTest.sln
index d0a3fa8..45bc1f0 100644
--- a/test/netstd/ThriftTest.sln
+++ b/test/netstd/ThriftTest.sln
@@ -17,10 +17,10 @@
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.Compile.net8", "..\..\lib\netstd\Tests\Thrift.Compile.Tests\Thrift.Compile.net8\Thrift.Compile.net8.csproj", "{05FAA75C-06BE-462F-999F-63823D08C75A}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.Compile.net6", "..\..\lib\netstd\Tests\Thrift.Compile.Tests\Thrift.Compile.net6\Thrift.Compile.net6.csproj", "{761EB615-D476-4423-888E-7D7665E769B1}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.Compile.netstd2", "..\..\lib\netstd\Tests\Thrift.Compile.Tests\Thrift.Compile.netstd2\Thrift.Compile.netstd2.csproj", "{F27E60D2-23D3-4946-BB6C-A809EDBEFE2C}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thrift.Compile.net9", "..\..\lib\netstd\Tests\Thrift.Compile.Tests\Thrift.Compile.net9\Thrift.Compile.net9.csproj", "{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -115,18 +115,6 @@
 		{05FAA75C-06BE-462F-999F-63823D08C75A}.Release|x64.Build.0 = Release|Any CPU
 		{05FAA75C-06BE-462F-999F-63823D08C75A}.Release|x86.ActiveCfg = Release|Any CPU
 		{05FAA75C-06BE-462F-999F-63823D08C75A}.Release|x86.Build.0 = Release|Any CPU
-		{761EB615-D476-4423-888E-7D7665E769B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{761EB615-D476-4423-888E-7D7665E769B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{761EB615-D476-4423-888E-7D7665E769B1}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{761EB615-D476-4423-888E-7D7665E769B1}.Debug|x64.Build.0 = Debug|Any CPU
-		{761EB615-D476-4423-888E-7D7665E769B1}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{761EB615-D476-4423-888E-7D7665E769B1}.Debug|x86.Build.0 = Debug|Any CPU
-		{761EB615-D476-4423-888E-7D7665E769B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{761EB615-D476-4423-888E-7D7665E769B1}.Release|Any CPU.Build.0 = Release|Any CPU
-		{761EB615-D476-4423-888E-7D7665E769B1}.Release|x64.ActiveCfg = Release|Any CPU
-		{761EB615-D476-4423-888E-7D7665E769B1}.Release|x64.Build.0 = Release|Any CPU
-		{761EB615-D476-4423-888E-7D7665E769B1}.Release|x86.ActiveCfg = Release|Any CPU
-		{761EB615-D476-4423-888E-7D7665E769B1}.Release|x86.Build.0 = Release|Any CPU
 		{F27E60D2-23D3-4946-BB6C-A809EDBEFE2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{F27E60D2-23D3-4946-BB6C-A809EDBEFE2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{F27E60D2-23D3-4946-BB6C-A809EDBEFE2C}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -139,6 +127,18 @@
 		{F27E60D2-23D3-4946-BB6C-A809EDBEFE2C}.Release|x64.Build.0 = Release|Any CPU
 		{F27E60D2-23D3-4946-BB6C-A809EDBEFE2C}.Release|x86.ActiveCfg = Release|Any CPU
 		{F27E60D2-23D3-4946-BB6C-A809EDBEFE2C}.Release|x86.Build.0 = Release|Any CPU
+		{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}.Debug|x64.Build.0 = Debug|Any CPU
+		{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}.Debug|x86.Build.0 = Debug|Any CPU
+		{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}.Release|Any CPU.Build.0 = Release|Any CPU
+		{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}.Release|x64.ActiveCfg = Release|Any CPU
+		{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}.Release|x64.Build.0 = Release|Any CPU
+		{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}.Release|x86.ActiveCfg = Release|Any CPU
+		{BB512CA7-D013-4CDC-AD2B-1E5EA121ED59}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/tutorial/netstd/Client/Client.csproj b/tutorial/netstd/Client/Client.csproj
index 732943f..df3f6c9 100644
--- a/tutorial/netstd/Client/Client.csproj
+++ b/tutorial/netstd/Client/Client.csproj
@@ -19,12 +19,13 @@
   -->
 
   <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <LangVersion>latestMajor</LangVersion>
     <AssemblyName>Client</AssemblyName>
     <PackageId>Client</PackageId>
     <OutputType>Exe</OutputType>
     <Version>0.22.0.0</Version>
+    <Nullable>enable</Nullable>
     <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
     <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
     <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
@@ -32,7 +33,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/tutorial/netstd/Client/Program.cs b/tutorial/netstd/Client/Program.cs
index f1c5236..93175fd 100644
--- a/tutorial/netstd/Client/Program.cs
+++ b/tutorial/netstd/Client/Program.cs
@@ -15,8 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
+using Microsoft.Extensions.Logging;
 using System;
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
@@ -25,16 +25,12 @@
 using System.Security.Cryptography.X509Certificates;
 using System.Threading;
 using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.DependencyInjection;
 using Thrift;
 using Thrift.Protocol;
 using Thrift.Transport;
 using Thrift.Transport.Client;
 using tutorial;
-using shared;
 
-#pragma warning disable IDE0063  // using
 #pragma warning disable IDE0057  // substr
 
 namespace Client
@@ -58,7 +54,7 @@
     public class Program
     {
         private static readonly ILogger Logger = LoggingHelper.CreateLogger<Program>();
-        private static readonly TConfiguration Configuration = null;  // new TConfiguration() if  needed
+        private static readonly TConfiguration Configuration = new();
 
         private static void DisplayHelp()
         {
@@ -97,11 +93,12 @@
 ");
         }
 
-        public static void Main(string[] args)
+        public static async Task Main(string[] args)
         {
-            args ??= Array.Empty<string>();
+            args ??= [];
 
-            if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
+            // -help is rather unusual but we leave it for compatibility
+            if (args.Any(x => x.Equals("-help") || x.Equals("--help") || x.Equals("-h") || x.Equals("-?")))
             {
                 DisplayHelp();
                 return;
@@ -109,10 +106,8 @@
 
             Logger.LogInformation("Starting client...");
 
-            using (var source = new CancellationTokenSource())
-            {
-                RunAsync(args, source.Token).GetAwaiter().GetResult();
-            }
+            using var source = new CancellationTokenSource();
+            await RunAsync(args, source.Token);
         }
 
         
@@ -150,7 +145,7 @@
 
         private static Protocol GetProtocol(string[] args)
         {
-            var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
+            var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
             if (string.IsNullOrEmpty(protocol))
                 return Protocol.Binary;
 
@@ -163,7 +158,7 @@
 
         private static Buffering GetBuffering(string[] args)
         {
-            var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(":")?[1];
+            var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
             if (string.IsNullOrEmpty(buffering))
                 return Buffering.None;
 
@@ -176,7 +171,7 @@
 
         private static Transport GetTransport(string[] args)
         {
-            var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
+            var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
             if (string.IsNullOrEmpty(transport))
                 return Transport.Tcp;
 
@@ -191,7 +186,7 @@
         private static TTransport MakeTransport(string[] args)
         {
             // construct endpoint transport
-            TTransport transport = null;
+            TTransport? transport = null;
             Transport selectedTransport = GetTransport(args);
             {
                 switch (selectedTransport)
@@ -241,7 +236,7 @@
 
         private static int GetNumberOfClients(string[] args)
         {
-            var numClients = args.FirstOrDefault(x => x.StartsWith("-mc"))?.Split(':')?[1];
+            var numClients = args.FirstOrDefault(x => x.StartsWith("-mc"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
 
             Logger.LogInformation("Selected # of clients: {numClients}", numClients);
 
@@ -254,35 +249,42 @@
         private static X509Certificate2 GetCertificate()
         {
             // due to files location in net core better to take certs from top folder
-            var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
-            return new X509Certificate2(certFile, "ThriftTest");
+            var dir = Directory.GetParent(Directory.GetCurrentDirectory());
+            if (dir != null)
+            {
+                var certFile = GetCertPath(dir);
+                //return new X509Certificate2(certFile, "ThriftTest");
+                return X509CertificateLoader.LoadPkcs12FromFile(certFile, "ThriftTest");
+            }
+            else
+            {
+                Logger.LogError("Root path of {path} not found", Directory.GetCurrentDirectory());
+                throw new Exception($"Root path of {Directory.GetCurrentDirectory()} not found");
+            }
         }
 
-        private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
+        private static string GetCertPath(DirectoryInfo? di, int maxCount = 6)
         {
             var topDir = di;
-            var certFile =
-                topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
-                    .FirstOrDefault();
+            var certFile = topDir?.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories).FirstOrDefault();
             if (certFile == null)
             {
                 if (maxCount == 0)
                     throw new FileNotFoundException("Cannot find file in directories");
-                return GetCertPath(di.Parent, maxCount - 1);
+                return GetCertPath(di?.Parent, --maxCount);
             }
 
             return certFile.FullName;
         }
 
-        private static X509Certificate LocalCertificateSelectionCallback(object sender,
+        private static X509Certificate2 LocalCertificateSelectionCallback(object sender,
             string targetHost, X509CertificateCollection localCertificates,
-            X509Certificate remoteCertificate, string[] acceptableIssuers)
+            X509Certificate? remoteCertificate, string[] acceptableIssuers)
         {
             return GetCertificate();
         }
 
-        private static bool CertValidator(object sender, X509Certificate certificate,
-            X509Chain chain, SslPolicyErrors sslPolicyErrors)
+        private static bool CertValidator(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
         {
             return true;
         }
diff --git a/tutorial/netstd/Interfaces/Interfaces.csproj b/tutorial/netstd/Interfaces/Interfaces.csproj
index 2791dc8..9a243ea 100644
--- a/tutorial/netstd/Interfaces/Interfaces.csproj
+++ b/tutorial/netstd/Interfaces/Interfaces.csproj
@@ -19,10 +19,11 @@
   -->
 
   <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <AssemblyName>Interfaces</AssemblyName>
     <PackageId>Interfaces</PackageId>
     <Version>0.22.0.0</Version>
+    <Nullable>enable</Nullable>
     <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
     <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
     <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
@@ -34,15 +35,15 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="System.ServiceModel.Primitives" Version="8.0.0" />
+    <PackageReference Include="System.ServiceModel.Primitives" Version="8.1.0" />
   </ItemGroup>
 
   <Target Name="PreBuild" BeforeTargets="_GenerateRestoreProjectSpec;Restore;Compile">
     <Exec Condition="'$(OS)' == 'Windows_NT'" Command="where thrift" ConsoleToMSBuild="true">
       <Output TaskParameter="ConsoleOutput" PropertyName="PathToThrift" />
     </Exec>
-    <Exec Condition="Exists('$(PathToThrift)')" Command="$(PathToThrift) -out $(ProjectDir) -gen netstd:wcf,union,serial,net8 -r ./../../tutorial.thrift" />
-    <Exec Condition="Exists('thrift')" Command="thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net8 -r ./../../tutorial.thrift" />
-    <Exec Condition="Exists('./../../../compiler/cpp/thrift')" Command="./../../../compiler/cpp/thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net8 -r ./../../tutorial.thrift" />
+    <Exec Condition="Exists('$(PathToThrift)')" Command="$(PathToThrift) -out $(ProjectDir) -gen netstd:wcf,union,serial,net9 -r ./../../tutorial.thrift" />
+    <Exec Condition="Exists('thrift')" Command="thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net9 -r ./../../tutorial.thrift" />
+    <Exec Condition="Exists('./../../../compiler/cpp/thrift')" Command="./../../../compiler/cpp/thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net9 -r ./../../tutorial.thrift" />
   </Target>
 </Project>
diff --git a/tutorial/netstd/Server/Program.cs b/tutorial/netstd/Server/Program.cs
index 01e7336..a3b12fc 100644
--- a/tutorial/netstd/Server/Program.cs
+++ b/tutorial/netstd/Server/Program.cs
@@ -15,6 +15,12 @@
 // specific language governing permissions and limitations
 // under the License.
 
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using shared;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -23,20 +29,13 @@
 using System.Security.Cryptography.X509Certificates;
 using System.Threading;
 using System.Threading.Tasks;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
 using Thrift;
+using Thrift.Processor;
 using Thrift.Protocol;
 using Thrift.Server;
 using Thrift.Transport;
 using Thrift.Transport.Server;
 using tutorial;
-using shared;
-using Thrift.Processor;
-using System.Diagnostics;
 
 #pragma warning disable IDE0057  // substr
 
@@ -61,27 +60,25 @@
     public class Program
     {
         private static readonly ILogger Logger = LoggingHelper.CreateLogger<Program>();
-        private static readonly TConfiguration Configuration = null;  // new TConfiguration() if  needed
+        private static readonly TConfiguration Configuration = new();
 
-        public static void Main(string[] args)
+        public static async Task Main(string[] args)
         {
-            args ??= Array.Empty<string>();
+            args ??= [];
 
-            if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
+            // -help is rather unusual but we leave it for compatibility
+            if (args.Any(x => x.Equals("-help") || x.Equals("--help") || x.Equals("-h") || x.Equals("-?")))
             {
                 DisplayHelp();
                 return;
             }
 
-            using (var source = new CancellationTokenSource())
-            {
-                RunAsync(args, source.Token).GetAwaiter().GetResult();
+            using var source = new CancellationTokenSource();
+            await RunAsync(args, source.Token);
 
-                Logger.LogInformation("Press any key to stop...");
-
-                Console.ReadLine();
-                source.Cancel();
-            }
+            Logger.LogInformation("Press any key to stop...");
+            Console.ReadLine();
+            source.Cancel();
 
             Logger.LogInformation("Server stopped");
         }
@@ -149,7 +146,7 @@
 
         private static Protocol GetProtocol(string[] args)
         {
-            var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
+            var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
             if (string.IsNullOrEmpty(protocol))
                 return Protocol.Binary;
 
@@ -162,7 +159,7 @@
 
         private static Buffering GetBuffering(string[] args)
         {
-            var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(":")?[1];
+            var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
             if (string.IsNullOrEmpty(buffering))
                 return Buffering.None;
 
@@ -175,7 +172,7 @@
 
         private static Transport GetTransport(string[] args)
         {
-            var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
+            var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
             if (string.IsNullOrEmpty(transport))
                 return Transport.Tcp;
 
@@ -196,7 +193,7 @@
                 _ => throw new ArgumentException("unsupported value $transport", nameof(transport)),
             };
 
-            TTransportFactory transportFactory = buffering switch
+            TTransportFactory? transportFactory = buffering switch
             {
                 Buffering.Buffered => new TBufferedTransport.Factory(),
                 Buffering.Framed => new TFramedTransport.Factory(),
@@ -258,34 +255,32 @@
         {
             // due to files location in net core better to take certs from top folder
             var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
-            return new X509Certificate2(certFile, "ThriftTest");
+            //return new X509Certificate2(certFile, "ThriftTest");
+            return X509CertificateLoader.LoadPkcs12FromFile(certFile, "ThriftTest");
         }
 
-        private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
+        private static string GetCertPath(DirectoryInfo? di, int maxCount = 6)
         {
             var topDir = di;
-            var certFile =
-                topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
-                    .FirstOrDefault();
+            var certFile = topDir?.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories).FirstOrDefault();
             if (certFile == null)
             {
                 if (maxCount == 0)
                     throw new FileNotFoundException("Cannot find file in directories");
-                return GetCertPath(di.Parent, maxCount - 1);
+                return GetCertPath(di?.Parent, --maxCount);
             }
 
             return certFile.FullName;
         }
 
-        private static X509Certificate LocalCertificateSelectionCallback(object sender,
+        private static X509Certificate2 LocalCertificateSelectionCallback(object sender,
             string targetHost, X509CertificateCollection localCertificates,
-            X509Certificate remoteCertificate, string[] acceptableIssuers)
+            X509Certificate? remoteCertificate, string[] acceptableIssuers)
         {
             return GetCertificate();
         }
 
-        private static bool ClientCertValidator(object sender, X509Certificate certificate,
-            X509Chain chain, SslPolicyErrors sslPolicyErrors)
+        private static bool ClientCertValidator(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
         {
             return true;
         }
@@ -369,7 +364,7 @@
 
         public class CalculatorAsyncHandler : Calculator.IAsync
         {
-            private readonly Dictionary<int, SharedStruct> _log = new();
+            private readonly Dictionary<int, SharedStruct> _log = [];
 
             public CalculatorAsyncHandler()
             {
@@ -394,12 +389,12 @@
                 return await Task.FromResult(num1 + num2);
             }
 
-            public async Task<int> calculate(int logid, Work w, CancellationToken cancellationToken)
+            public async Task<int> calculate(int logid, Work? w, CancellationToken cancellationToken)
             {
-                Logger.LogInformation("Calculate({logid}, [{w.Op},{w.Num1},{w.Num2}])", logid, w.Op, w.Num1, w.Num2);
+                Logger.LogInformation("Calculate({logid}, [{w.Op},{w.Num1},{w.Num2}])", logid, w?.Op, w?.Num1, w?.Num2);
 
                 int val;
-                switch (w.Op)
+                switch (w?.Op)
                 {
                     case Operation.ADD:
                         val = w.Num1 + w.Num2;
@@ -431,7 +426,7 @@
                     {
                         var io = new InvalidOperation
                         {
-                            WhatOp = (int) w.Op,
+                            WhatOp = ((int?)w?.Op) ?? -1,
                             Why = "Unknown operation"
                         };
 
diff --git a/tutorial/netstd/Server/Server.csproj b/tutorial/netstd/Server/Server.csproj
index 16b72e2..b50b69c 100644
--- a/tutorial/netstd/Server/Server.csproj
+++ b/tutorial/netstd/Server/Server.csproj
@@ -19,12 +19,13 @@
   -->
 
   <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <LangVersion>latestMajor</LangVersion>
     <AssemblyName>Server</AssemblyName>
     <PackageId>Server</PackageId>
     <OutputType>Exe</OutputType>
     <Version>0.22.0.0</Version>
+    <Nullable>enable</Nullable>
     <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
     <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
     <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>