Update .NET SDKs to LTS versions
diff --git a/csharp/buildall.sh b/csharp/buildall.sh
index 43b5ac3..ab1a6c4 100755
--- a/csharp/buildall.sh
+++ b/csharp/buildall.sh
@@ -10,8 +10,8 @@
 dotnet build -c $CONFIG $SRC/Google.Protobuf.sln
 
 echo Running tests.
-# Only test netcoreapp2.1, which uses the .NET Core runtime.
+# Only test netcoreapp3.1, which uses the .NET Core runtime.
 # If we want to test the .NET 4.5 version separately, we could
 # run Mono explicitly. However, we don't have any differences between
 # the .NET 4.5 and netstandard2.1 assemblies.
-dotnet test -c $CONFIG -f netcoreapp2.1 $SRC/Google.Protobuf.Test/Google.Protobuf.Test.csproj
+dotnet test -c $CONFIG -f netcoreapp3.1 $SRC/Google.Protobuf.Test/Google.Protobuf.Test.csproj
diff --git a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
index cbb6fee..0ecdf37 100644
--- a/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
+++ b/csharp/compatibility_tests/v3.0.0/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFrameworks>net451;netcoreapp2.1</TargetFrameworks>
+    <TargetFrameworks>net451;netcoreapp3.1</TargetFrameworks>
     <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
     <IsPackable>False</IsPackable>
diff --git a/csharp/compatibility_tests/v3.0.0/test.sh b/csharp/compatibility_tests/v3.0.0/test.sh
index f893d64..459c079 100755
--- a/csharp/compatibility_tests/v3.0.0/test.sh
+++ b/csharp/compatibility_tests/v3.0.0/test.sh
@@ -22,7 +22,7 @@
   dotnet restore src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
   dotnet build -c Release src/Google.Protobuf/Google.Protobuf.csproj
   dotnet build -c Release src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
-  dotnet run -c Release -f netcoreapp2.1 -p src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
+  dotnet run -c Release -f netcoreapp3.1 -p src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
 }
 
 set -ex
diff --git a/csharp/install_dotnet_sdk.ps1 b/csharp/install_dotnet_sdk.ps1
index c78655c..f9dc0d9 100755
--- a/csharp/install_dotnet_sdk.ps1
+++ b/csharp/install_dotnet_sdk.ps1
@@ -16,5 +16,5 @@
 
 # The SDK versions to install should be kept in sync with versions
 # installed by kokoro/linux/dockerfile/test/csharp/Dockerfile
-&$InstallScriptPath -Version 2.1.802
-&$InstallScriptPath -Version 5.0.102
+&$InstallScriptPath -Version 3.1.415
+&$InstallScriptPath -Version 6.0.100
diff --git a/csharp/src/AddressBook/AddressBook.csproj b/csharp/src/AddressBook/AddressBook.csproj
index f3268c0..9a52787 100644
--- a/csharp/src/AddressBook/AddressBook.csproj
+++ b/csharp/src/AddressBook/AddressBook.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
     <OutputType>Exe</OutputType>
     <StartupObject>Google.Protobuf.Examples.AddressBook.Program</StartupObject>
     <IsPackable>False</IsPackable>
diff --git a/csharp/src/Google.Protobuf.Benchmarks/Program.cs b/csharp/src/Google.Protobuf.Benchmarks/Program.cs
index 1f77a26..037752f 100644
--- a/csharp/src/Google.Protobuf.Benchmarks/Program.cs
+++ b/csharp/src/Google.Protobuf.Benchmarks/Program.cs
@@ -36,7 +36,7 @@
 {
     class Program
     {
-        // typical usage: dotnet run -c Release -f netcoreapp2.1
+        // typical usage: dotnet run -c Release -f netcoreapp3.1
         // (this can profile both .net core and .net framework; for some reason
         // if you start from "-f net461", it goes horribly wrong)
         public static void Main(string[] args)
diff --git a/csharp/src/Google.Protobuf.Conformance/Google.Protobuf.Conformance.csproj b/csharp/src/Google.Protobuf.Conformance/Google.Protobuf.Conformance.csproj
index ec8fb91..6277e68 100644
--- a/csharp/src/Google.Protobuf.Conformance/Google.Protobuf.Conformance.csproj
+++ b/csharp/src/Google.Protobuf.Conformance/Google.Protobuf.Conformance.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
     <OutputType>Exe</OutputType>
     <IsPackable>False</IsPackable>
   </PropertyGroup>
diff --git a/csharp/src/Google.Protobuf.JsonDump/Google.Protobuf.JsonDump.csproj b/csharp/src/Google.Protobuf.JsonDump/Google.Protobuf.JsonDump.csproj
index fee35be..b2c4272 100644
--- a/csharp/src/Google.Protobuf.JsonDump/Google.Protobuf.JsonDump.csproj
+++ b/csharp/src/Google.Protobuf.JsonDump/Google.Protobuf.JsonDump.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
     <OutputType>Exe</OutputType>
     <IsPackable>False</IsPackable>
   </PropertyGroup>
diff --git a/csharp/src/Google.Protobuf.Test/Collections/RepeatedFieldTest.cs b/csharp/src/Google.Protobuf.Test/Collections/RepeatedFieldTest.cs
index 61453e5..ff8f5cc 100644
--- a/csharp/src/Google.Protobuf.Test/Collections/RepeatedFieldTest.cs
+++ b/csharp/src/Google.Protobuf.Test/Collections/RepeatedFieldTest.cs
@@ -840,7 +840,7 @@
             var list2 = new RepeatedField<double> { SampleNaNs.Regular, SampleNaNs.PayloadFlipped };
             var list3 = new RepeatedField<double> { SampleNaNs.Regular, SampleNaNs.SignallingFlipped };
 
-            // All SampleNaNs have the same hashcode under certain targets (e.g. netcoreapp2.1)
+            // All SampleNaNs have the same hashcode under certain targets (e.g. netcoreapp3.1)
             EqualityTester.AssertInequality(list1, list2, checkHashcode: false);
             EqualityTester.AssertEquality(list1, list3);
             Assert.True(list1.Contains(SampleNaNs.SignallingFlipped));
diff --git a/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj b/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
index cdfa98e..2665cc3 100644
--- a/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
+++ b/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net451;netcoreapp2.1;net50</TargetFrameworks>
+    <TargetFrameworks>net451;netcoreapp3.1;net60</TargetFrameworks>
     <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
     <IsPackable>False</IsPackable>
diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj
index ad6f579..7e9e33a 100644
--- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj
+++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <Description>C# runtime library for Protocol Buffers - Google's data interchange format.</Description>
@@ -43,7 +43,7 @@
 
   <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
     <PackageReference Include="System.Memory" Version="4.5.3"/>
-    <!-- Needed for netcoreapp2.1 to work correctly. .NET is not able to load the assembly without this -->
+    <!-- Needed for netcoreapp3.1 to work correctly. .NET is not able to load the assembly without this -->
     <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.2"/>
   </ItemGroup>