You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by pa...@apache.org on 2026/01/12 23:37:41 UTC

(lucenenet) branch master updated: Add .NET 10 target, #1219 (#1222)

This is an automated email from the ASF dual-hosted git repository.

paulirwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/lucenenet.git


The following commit(s) were added to refs/heads/master by this push:
     new 657511fbf Add .NET 10 target, #1219 (#1222)
657511fbf is described below

commit 657511fbf314e340e5b782cf389b017c12796bf6
Author: Paul Irwin <pa...@gmail.com>
AuthorDate: Mon Jan 12 16:37:35 2026 -0700

    Add .NET 10 target, #1219 (#1222)
    
    * Remove netstandard2.1 target and test support via net6.0, #1040
    
    * Add net10.0 target
    
    * Fix merge issue
    
    * Regenerate github workflows; test OpenNLP on net10
    
    * More C# 14 field keyword changes
    
    * .NET 10 build support in Azure DevOps
    
    * Remove net9.0 tests for lucene-cli
    
    * Add back net9.0
    
    * Remove net9.0 from lucene-cli
    
    * Conditionally set IsPublishable
    
    * Regenerate github workflows
    
    * Remove net9.0 tests
    
    * PR feedback
    
    * Add .NET 9 tests to ADO
    
    * Upgrade Antlr4BuildTasks to 12.11.0
    
    * Fix net9.0 test build
    
    * Revert SetTargetFramework change
    
    * Add net9.0 tests to CLI and OpenNLP test projects
    
    * Directory.Build.props: Added section to compare Visual Studio versions (including prerelease versions) to use to drive IsLegacyVisualStudioVersion property across the solution
    
    * SWEEP: Added legacy support for Visual Studio 2022, excluding net10.0, netstandard2.0, and .NET Framework from compilation. net10.0 isn't supported at all. The others cause invalid warnings in VS 2022 and excluding them from compile is the simplest way around that.
    
    * publish-test-results-for-test-projects.yml: Upload test results for net10.0 for Lucene.Net.Tests.Cli and Lucene.Net.Tests.Analysis.OpenNLP
    
    * publish-test-results-for-test-projects.yml: Upload test results for net9.0 for Lucene.Net.Tests.Cli and Lucene.Net.Tests.Analysis.OpenNLP
    
    * .build/runbuild.ps1 + github/workflows/Generate-TestWorkflows.ps1 + Directory.Build.targets: Added a key identifier SupportedTargetFrameworks to the MSBuild query so it can be parsed using a regex rather than relying on the position in the output (which changed in .NET 9). Updated all callers to use a Get-SupportedTargetFramworks() function to encapsulate the logic of retrieving the target frameworks and parsing out the string we are interested in with an error message if matching fails.
    
    * .github/workflows/Generate-TestWorkflows.ps1: Generate workflow files with no BOM
    
    * Regenerated GitHub test workflows
    
    * Fix comment
    
    ---------
    
    Co-authored-by: Shad Storhaug <sh...@shadstorhaug.com>
---
 .../publish-test-results-for-test-projects.yml     | 53 +++++++++++++--
 .build/azure-templates/run-tests-on-os.yml         | 27 +++++++-
 .build/dependencies.props                          |  2 +-
 .build/runbuild.ps1                                | 13 +++-
 .github/workflows/Generate-TestWorkflows.ps1       | 36 ++++++----
 .github/workflows/Lucene-Net-Tests-AllProjects.yml |  9 ++-
 .../workflows/Lucene-Net-Tests-Analysis-Common.yml |  9 ++-
 .../Lucene-Net-Tests-Analysis-Kuromoji.yml         |  9 ++-
 .../Lucene-Net-Tests-Analysis-Morfologik.yml       |  9 ++-
 .../Lucene-Net-Tests-Analysis-OpenNLP.yml          |  9 ++-
 .../Lucene-Net-Tests-Analysis-Phonetic.yml         |  9 ++-
 .../Lucene-Net-Tests-Analysis-SmartCn.yml          | 10 +--
 .../Lucene-Net-Tests-Analysis-Stempel.yml          |  9 ++-
 .github/workflows/Lucene-Net-Tests-Benchmark.yml   |  9 ++-
 .../workflows/Lucene-Net-Tests-Classification.yml  |  9 ++-
 .github/workflows/Lucene-Net-Tests-Cli.yml         |  9 ++-
 .../workflows/Lucene-Net-Tests-CodeAnalysis.yml    |  9 ++-
 .github/workflows/Lucene-Net-Tests-Codecs.yml      |  9 ++-
 .github/workflows/Lucene-Net-Tests-Demo.yml        |  9 ++-
 .github/workflows/Lucene-Net-Tests-Expressions.yml |  9 ++-
 .github/workflows/Lucene-Net-Tests-Facet.yml       |  9 ++-
 .github/workflows/Lucene-Net-Tests-Grouping.yml    |  9 ++-
 .github/workflows/Lucene-Net-Tests-Highlighter.yml |  9 ++-
 .github/workflows/Lucene-Net-Tests-ICU.yml         |  9 ++-
 .github/workflows/Lucene-Net-Tests-Join.yml        |  9 ++-
 .github/workflows/Lucene-Net-Tests-Memory.yml      |  9 ++-
 .github/workflows/Lucene-Net-Tests-Misc.yml        |  9 ++-
 .github/workflows/Lucene-Net-Tests-Queries.yml     |  9 ++-
 .github/workflows/Lucene-Net-Tests-QueryParser.yml |  9 ++-
 .github/workflows/Lucene-Net-Tests-Replicator.yml  |  9 ++-
 .github/workflows/Lucene-Net-Tests-Sandbox.yml     |  9 ++-
 .github/workflows/Lucene-Net-Tests-Spatial.yml     |  9 ++-
 .github/workflows/Lucene-Net-Tests-Suggest.yml     |  9 ++-
 ...Net-Tests-TestFramework-DependencyInjection.yml |  9 ++-
 ...ene-Net-Tests-TestFramework-NUnitExtensions.yml |  9 ++-
 .../workflows/Lucene-Net-Tests-TestFramework.yml   |  9 ++-
 .github/workflows/Lucene-Net-Tests-_A-D.yml        |  9 ++-
 .github/workflows/Lucene-Net-Tests-_E-I.yml        |  9 ++-
 .github/workflows/Lucene-Net-Tests-_I-J.yml        |  9 ++-
 .github/workflows/Lucene-Net-Tests-_J-S.yml        |  9 ++-
 .github/workflows/Lucene-Net-Tests-_T-Z.yml        | 10 +--
 Directory.Build.props                              | 20 ++++++
 Directory.Build.targets                            | 48 ++++++-------
 TestTargetFramework.props                          | 13 +++-
 azure-pipelines.yml                                | 79 ++++++++++++++++++++--
 .../Lucene.Net.Analysis.Common.csproj              |  2 +-
 .../Lucene.Net.Analysis.Kuromoji.csproj            |  2 +-
 .../Lucene.Net.Analysis.Morfologik.csproj          |  2 +-
 .../Lucene.Net.Analysis.OpenNLP.csproj             |  8 ++-
 .../Language/Bm/Languages.cs                       |  6 +-
 .../Lucene.Net.Analysis.Phonetic.csproj            |  2 +-
 .../Lucene.Net.Analysis.SmartCn.csproj             |  2 +-
 .../Lucene.Net.Analysis.Stempel.csproj             |  2 +-
 .../Lucene.Net.Benchmark.csproj                    |  2 +-
 .../Lucene.Net.Classification.csproj               |  2 +-
 src/Lucene.Net.Codecs/Lucene.Net.Codecs.csproj     |  2 +-
 src/Lucene.Net.Demo/Lucene.Net.Demo.csproj         |  2 +-
 .../Lucene.Net.Expressions.csproj                  |  2 +-
 src/Lucene.Net.Facet/Lucene.Net.Facet.csproj       |  2 +-
 src/Lucene.Net.Grouping/Lucene.Net.Grouping.csproj |  2 +-
 .../Lucene.Net.Highlighter.csproj                  |  2 +-
 src/Lucene.Net.Join/Lucene.Net.Join.csproj         |  2 +-
 src/Lucene.Net.Memory/Lucene.Net.Memory.csproj     |  2 +-
 src/Lucene.Net.Misc/Lucene.Net.Misc.csproj         |  2 +-
 src/Lucene.Net.Queries/Lucene.Net.Queries.csproj   |  2 +-
 .../Lucene.Net.QueryParser.csproj                  |  2 +-
 .../Lucene.Net.Replicator.csproj                   |  2 +-
 src/Lucene.Net.Sandbox/Lucene.Net.Sandbox.csproj   |  2 +-
 src/Lucene.Net.Spatial/Lucene.Net.Spatial.csproj   |  2 +-
 src/Lucene.Net.Suggest/Lucene.Net.Suggest.csproj   |  2 +-
 .../Lucene.Net.TestFramework.csproj                |  2 +-
 .../Lucene.Net.Tests.AllProjects.csproj            |  2 +-
 .../Lucene.Net.Tests.Analysis.OpenNLP.csproj       |  8 ++-
 .../Lucene.Net.Tests.Replicator.csproj             |  4 +-
 src/Lucene.Net/Index/SegmentInfo.cs                |  3 +-
 src/Lucene.Net/Lucene.Net.csproj                   |  2 +-
 src/dotnet/Lucene.Net.ICU/Lucene.Net.ICU.csproj    |  2 +-
 .../Lucene.Net.Replicator.AspNetCore.csproj        |  2 +-
 .../Lucene.Net.TestFramework.TestData.NUnit.csproj |  2 +-
 .../Lucene.Net.Tests.CodeAnalysis.csproj           |  6 +-
 .../Lucene.Net.Tests.Cli.csproj                    |  2 +-
 src/dotnet/tools/lucene-cli/lucene-cli.csproj      |  7 +-
 82 files changed, 438 insertions(+), 281 deletions(-)

diff --git a/.build/azure-templates/publish-test-results-for-test-projects.yml b/.build/azure-templates/publish-test-results-for-test-projects.yml
index 8ceb219d4..63d4466d2 100644
--- a/.build/azure-templates/publish-test-results-for-test-projects.yml
+++ b/.build/azure-templates/publish-test-results-for-test-projects.yml
@@ -70,7 +70,7 @@ steps:
 #  displayName: 'Validate Template Parameters'
 
 
-# Special case: Only supports .NET Standard 1.3
+# Special case: Only supports .NET Standard 1.3 (we only need to test this on one framework because it is for VS)
 - template: publish-test-results.yml
   parameters:
     testProjectName: 'Lucene.Net.Tests.CodeAnalysis'
@@ -81,23 +81,64 @@ steps:
     testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
     testResultsFileName: '${{ parameters.testResultsFileName }}'
 
-# Special case: Only supports net8.0
+# Special case: Only supports net10.0, net9.0 and net8.0
+
 - template: publish-test-results.yml
   parameters:
     testProjectName: 'Lucene.Net.Tests.Cli'
-    framework: 'net8.0' # Since condtions are not supported for templates, we check for the file existence within publish-test-results.yml
+    framework: 'net10.0' # Since conditions are not supported for templates, we check for the file existence within publish-test-results.yml
+    vsTestPlatform: '${{ parameters.vsTestPlatform }}'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Cli'
+    framework: 'net9.0' # Since conditions are not supported for templates, we check for the file existence within publish-test-results.yml
+    vsTestPlatform: '${{ parameters.vsTestPlatform }}'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Cli'
+    framework: 'net8.0' # Since conditions are not supported for templates, we check for the file existence within publish-test-results.yml
     vsTestPlatform: '${{ parameters.vsTestPlatform }}'
     osName: '${{ parameters.osName }}'
     testResultsFormat: '${{ parameters.testResultsFormat }}'
     testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
     testResultsFileName: '${{ parameters.testResultsFileName }}'
 
-# Special case: Only supports net8.0 and net48
+# Special case: Only supports net10.0, net9.0, net8.0 and net48
 
 - template: publish-test-results.yml
   parameters:
     testProjectName: 'Lucene.Net.Tests.Analysis.OpenNLP'
-    framework: 'net8.0' # Since condtions are not supported for templates, we check for the file existence within publish-test-results.yml
+    framework: 'net10.0' # Since conditions are not supported for templates, we check for the file existence within publish-test-results.yml
+    vsTestPlatform: '${{ parameters.vsTestPlatform }}'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Analysis.OpenNLP'
+    framework: 'net9.0' # Since conditions are not supported for templates, we check for the file existence within publish-test-results.yml
+    vsTestPlatform: '${{ parameters.vsTestPlatform }}'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Analysis.OpenNLP'
+    framework: 'net8.0' # Since conditions are not supported for templates, we check for the file existence within publish-test-results.yml
     vsTestPlatform: '${{ parameters.vsTestPlatform }}'
     osName: '${{ parameters.osName }}'
     testResultsFormat: '${{ parameters.testResultsFormat }}'
@@ -107,7 +148,7 @@ steps:
 - template: publish-test-results.yml
   parameters:
     testProjectName: 'Lucene.Net.Tests.Analysis.OpenNLP'
-    framework: 'net48' # Since condtions are not supported for templates, we check for the file existence within publish-test-results.yml
+    framework: 'net48' # Since conditions are not supported for templates, we check for the file existence within publish-test-results.yml
     vsTestPlatform: '${{ parameters.vsTestPlatform }}'
     osName: '${{ parameters.osName }}'
     testResultsFormat: '${{ parameters.testResultsFormat }}'
diff --git a/.build/azure-templates/run-tests-on-os.yml b/.build/azure-templates/run-tests-on-os.yml
index 4e9ec97b1..32b5a8c62 100644
--- a/.build/azure-templates/run-tests-on-os.yml
+++ b/.build/azure-templates/run-tests-on-os.yml
@@ -105,7 +105,7 @@ steps:
     & $installScriptPath -Version $sdkVersion -Architecture $architecture -InstallDir $installPath
     Write-Host "##vso[task.setvariable variable=DOTNET_ROOT_X86;]$installPath"
   displayName: 'Use .NET SDK ${{ parameters.dotNetSdkVersion }} (x86)'
-  condition: and(succeeded(), contains('${{ parameters.framework }}', 'net9.'), eq('${{ parameters.vsTestPlatform }}', 'x86'))
+  condition: and(succeeded(), contains('${{ parameters.framework }}', 'net10.'), eq('${{ parameters.vsTestPlatform }}', 'x86'))
 
 - task: UseDotNet@2
   displayName: 'Use .NET SDK 8.0.404'
@@ -132,6 +132,31 @@ steps:
   displayName: 'Use .NET SDK 8.0.404 (x86)'
   condition: and(succeeded(), contains('${{ parameters.framework }}', 'net8.'), eq('${{ parameters.vsTestPlatform }}', 'x86'))
 
+- task: UseDotNet@2
+  displayName: 'Use .NET SDK 9.0.308'
+  inputs:
+    packageType: 'sdk'
+    version: '9.0.308'
+    performMultiLevelLookup: '${{ variables.PerformMultiLevelLookup }}'
+  condition: and(succeeded(), contains('${{ parameters.framework }}', 'net9.'))
+
+    # Hack: .NET 8+ no longer installs the x86 bits and they must be installed separately. However, it is not
+    # trivial to get it into the path and to get it to pass the minimum SDK version check in runbuild.ps1.
+    # So, we install it afterward and set the environment variable so the above SDK can delegate to it.
+    # This code only works on Windows.
+- pwsh: |
+    $sdkVersion = '9.0.308'
+    $architecture = '${{ parameters.vsTestPlatform }}'
+    $installScriptPath = "${env:AGENT_TEMPDIRECTORY}/dotnet-install.ps1"
+    $installScriptUrl = "https://raw.githubusercontent.com/dotnet/install-scripts/main/src/dotnet-install.ps1"
+    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+    Invoke-WebRequest $installScriptUrl -OutFile $installScriptPath -TimeoutSec 60
+    $installPath = "${env:ProgramFiles(x86)}/dotnet"
+    & $installScriptPath -Version $sdkVersion -Architecture $architecture -InstallDir $installPath
+    Write-Host "##vso[task.setvariable variable=DOTNET_ROOT_X86;]$installPath"
+  displayName: 'Use .NET SDK 9.0.308 (x86)'
+  condition: and(succeeded(), contains('${{ parameters.framework }}', 'net9.'), eq('${{ parameters.vsTestPlatform }}', 'x86'))
+
 
 #- template: 'show-all-files.yml' # Uncomment for debugging
 - pwsh: |
diff --git a/.build/dependencies.props b/.build/dependencies.props
index 68843f2f3..1a8dbb7b9 100644
--- a/.build/dependencies.props
+++ b/.build/dependencies.props
@@ -22,7 +22,7 @@
   <PropertyGroup Label="NuGet Package Reference Versions">
     <Antlr4CodeGeneratorPackageVersion>4.6.6</Antlr4CodeGeneratorPackageVersion>
     <Antlr4RuntimeStandardPackageVersion>4.13.1</Antlr4RuntimeStandardPackageVersion>
-    <Antlr4BuildTasksPackageVersion>12.8.0</Antlr4BuildTasksPackageVersion>
+    <Antlr4BuildTasksPackageVersion>12.11.0</Antlr4BuildTasksPackageVersion>
     <!-- LUCENENET TODO: When ICU4N is released to production,
         be sure to lock down the version range below. The resource
         files in Lucene.Net.ICU are not compatible with any other
diff --git a/.build/runbuild.ps1 b/.build/runbuild.ps1
index a33889365..e22b2a4f8 100644
--- a/.build/runbuild.ps1
+++ b/.build/runbuild.ps1
@@ -270,7 +270,7 @@ task Test -depends CheckSDK, UpdateLocalSDKVersion, Restore -description "This t
         $testName = $testProject.Directory.Name
 
         # Call the target to get the configured test frameworks for this project. We only read the first line because MSBuild adds extra output.
-        $frameworksString = $(dotnet build "$testProject" --verbosity minimal --nologo --no-restore /t:PrintTargetFrameworks /p:TestProjectsOnly=true /p:TestFrameworks=true)[0].Trim()
+        $frameworksString = Get-SupportedTargetFrameworksString $testProject
 
         Write-Host ""
         Write-Host "Frameworks To Test for ${testProject}: $frameworksString" -ForegroundColor Yellow
@@ -417,12 +417,21 @@ function Get-Version() {
 
 function Get-FrameworksToTest() {
     # Call the target to get the configured test frameworks for a project known to contain all of them. We only read the first line because MSBuild adds extra output.
-    $frameworksString = $(dotnet build "$projectWithAllTestFrameworks" --verbosity minimal --nologo --no-restore /t:PrintTargetFrameworks /p:TestProjectsOnly=true /p:TestFrameworks=true)[0].Trim()
+    $frameworksString = Get-SupportedTargetFrameworksString -ProjectPath $projectWithAllTestFrameworks
     $frameworksToTest = $frameworksString -split '\s*;\s*'
     Assert($frameworksToTest.Length -gt 0) "The project at $(Normalize-FileSystemSlashes "$projectWithAllTestFrameworks") contains no target frameworks. Please configure a project that includes all testable target frameworks."
     return $frameworksToTest
 }
 
+function Get-SupportedTargetFrameworksString([Parameter(Mandatory)][string] $ProjectPath) {
+    # NOTE: This will not appear when run directly in the console with minimal verbosity. MSBuild only produces the output when using a pipe, which is what we are doing here.
+    $output = dotnet build "$ProjectPath" --verbosity minimal --nologo --no-restore /t:PrintTargetFrameworks /p:TestProjectsOnly=true /p:TestFrameworks=true 2>&1 | Out-String
+    if ($output -match 'SupportedTargetFrameworks=([^\s]+)') {
+        return $matches[1]
+    }
+    throw "Failed to determine supported target frameworks for project: $ProjectPath"
+}
+
 function Prepare-For-Build() {
     #Use only the major version as the assembly version.
     #This ensures binary compatibility unless the major version changes.
diff --git a/.github/workflows/Generate-TestWorkflows.ps1 b/.github/workflows/Generate-TestWorkflows.ps1
index d01d5e650..79201d9f3 100644
--- a/.github/workflows/Generate-TestWorkflows.ps1
+++ b/.github/workflows/Generate-TestWorkflows.ps1
@@ -38,7 +38,7 @@
 
  .PARAMETER TestFrameworks
     A string array of Dotnet target framework monikers to run the tests on. The default is
-    @('net9.0','net8.0','net6.0','net472','net48').
+    @('net10.0','net8.0','net472','net48').
 
  .PARAMETER OperatingSystems
     A string array of Github Actions operating system monikers to run the tests on.
@@ -51,9 +51,9 @@
  .PARAMETER Configurations
     A string array of build configurations to run the tests on. The default is @('Release').
 
- .PARAMETER DotNet9SDKVersion
-    The SDK version of .NET 9.x to install on the build agent to be used for building and
-    testing. This SDK is always installed on the build agent. The default is 9.0.x.
+ .PARAMETER DotNet10SDKVersion
+    The SDK version of .NET 10.x to install on the build agent to be used for building and
+    testing. This SDK is always installed on the build agent. The default is 10.0.x.
 
  .PARAMETER DotNet8SDKVersion
     The SDK version of .NET 8.x to install on the build agent to be used for building and
@@ -65,7 +65,7 @@ param(
 
     [string]$RepoRoot = (Split-Path (Split-Path $PSScriptRoot)),
 
-    [string[]]$TestFrameworks = @('net9.0','net8.0','net472','net48'), # targets under test: net8.0, net8.0, netstandard2.0, net462
+    [string[]]$TestFrameworks = @('net10.0', 'net8.0', 'net472', 'net48'), # targets under test: net10.0, net8.0, netstandard2.0, net462
 
     [string[]]$OperatingSystems = @('windows-latest', 'ubuntu-latest'),
 
@@ -73,7 +73,7 @@ param(
 
     [string[]]$Configurations = @('Release'),
 
-    [string]$DotNet9SDKVersion = '9.0.x',
+    [string]$DotNet10SDKVersion = '10.0.x',
 
     [string]$DotNet8SDKVersion = '8.0.x'
 )
@@ -141,6 +141,15 @@ function Get-ProjectPathDirectories([string]$ProjectPath, [string]$RelativeRoot,
     }
 }
 
+function Get-SupportedTargetFrameworksString([Parameter(Mandatory)][string] $ProjectPath) {
+    # NOTE: This will not appear when run directly in the console with minimal verbosity. MSBuild only produces the output when using a pipe, which is what we are doing here.
+    $output = dotnet build "$ProjectPath" --verbosity minimal --nologo --no-restore /t:PrintTargetFrameworks /p:TestProjectsOnly=true /p:TestFrameworks=true 2>&1 | Out-String
+    if ($output -match 'SupportedTargetFrameworks=([^\s]+)') {
+        return $matches[1]
+    }
+    throw "Failed to determine supported target frameworks for project: $ProjectPath"
+}
+
 function Ensure-Directory-Exists([string] $path) {
     if (!(Test-Path $path)) {
         New-Item $path -ItemType Directory
@@ -155,7 +164,7 @@ function Write-TestWorkflow(
     [string[]]$TestFrameworks = @('net6.0', 'net48'),
     [string[]]$TestPlatforms = @('x64'),
     [string[]]$OperatingSystems = @('windows-latest', 'ubuntu-latest', 'macos-latest'),
-    [string]$DotNet9SDKVersion = $DotNet9SDKVersion,
+    [string]$DotNet10SDKVersion = $DotNet10SDKVersion,
     [string]$DotNet8SDKVersion = $DotNet8SDKVersion) {
 
     $dependencies = New-Object System.Collections.Generic.HashSet[string]
@@ -280,10 +289,10 @@ jobs:
           dotnet-version: '$DotNet8SDKVersion'
         if: `${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '$DotNet9SDKVersion'
+          dotnet-version: '$DotNet10SDKVersion'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -353,7 +362,10 @@ jobs:
     Ensure-Directory-Exists $OutputDirectory
 
     Write-Host "Generating workflow file: $FilePath"
-    Out-File -filePath $FilePath -encoding UTF8 -inputObject $fileText
+
+    # Ensure the file does not get generated with a BOM
+    $utf8NoBom = New-Object System.Text.UTF8Encoding($false)
+    [System.IO.File]::WriteAllText($FilePath, $fileText, $utf8NoBom)
 
     #Write-Host $fileText
 }
@@ -373,8 +385,8 @@ try {
 foreach ($testProject in $TestProjects) {
     $projectName = [System.IO.Path]::GetFileNameWithoutExtension($testProject)
 
-     # Call the target to get the configured test frameworks for this project. We only read the first line because MSBuild adds extra output.
-    $frameworksString = $(dotnet build "$testProject" --verbosity minimal --nologo --no-restore /t:PrintTargetFrameworks /p:TestProjectsOnly=true /p:TestFrameworks=true)[0].Trim()
+     # Call the target to get the configured test frameworks for this project.
+    $frameworksString = Get-SupportedTargetFrameworksString $testProject
 
     if ($frameworksString -eq 'none') {
         Write-Host "WARNING: Skipping project '$projectName' because it is not marked with `<IsTestProject`>true`<`/IsTestProject`> and/or it contains no test frameworks for the current environment." -ForegroundColor Yellow
diff --git a/.github/workflows/Lucene-Net-Tests-AllProjects.yml b/.github/workflows/Lucene-Net-Tests-AllProjects.yml
index 1e190ab0d..b85aba175 100644
--- a/.github/workflows/Lucene-Net-Tests-AllProjects.yml
+++ b/.github/workflows/Lucene-Net-Tests-AllProjects.yml
@@ -73,7 +73,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -102,13 +102,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -158,4 +158,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Analysis-Common.yml b/.github/workflows/Lucene-Net-Tests-Analysis-Common.yml
index 2b0b362ce..ba01aeaf1 100644
--- a/.github/workflows/Lucene-Net-Tests-Analysis-Common.yml
+++ b/.github/workflows/Lucene-Net-Tests-Analysis-Common.yml
@@ -57,7 +57,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -86,13 +86,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -142,4 +142,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Analysis-Kuromoji.yml b/.github/workflows/Lucene-Net-Tests-Analysis-Kuromoji.yml
index 577d9de3b..fef9acb97 100644
--- a/.github/workflows/Lucene-Net-Tests-Analysis-Kuromoji.yml
+++ b/.github/workflows/Lucene-Net-Tests-Analysis-Kuromoji.yml
@@ -54,7 +54,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -83,13 +83,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -139,4 +139,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Analysis-Morfologik.yml b/.github/workflows/Lucene-Net-Tests-Analysis-Morfologik.yml
index b71d9288f..c05f71d30 100644
--- a/.github/workflows/Lucene-Net-Tests-Analysis-Morfologik.yml
+++ b/.github/workflows/Lucene-Net-Tests-Analysis-Morfologik.yml
@@ -54,7 +54,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -83,13 +83,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -139,4 +139,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Analysis-OpenNLP.yml b/.github/workflows/Lucene-Net-Tests-Analysis-OpenNLP.yml
index 2e91c23c4..24a7db67f 100644
--- a/.github/workflows/Lucene-Net-Tests-Analysis-OpenNLP.yml
+++ b/.github/workflows/Lucene-Net-Tests-Analysis-OpenNLP.yml
@@ -57,7 +57,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48]
+        framework: [net10.0, net8.0, net48]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -86,13 +86,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -142,4 +142,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Analysis-Phonetic.yml b/.github/workflows/Lucene-Net-Tests-Analysis-Phonetic.yml
index 5e1a5482b..42703c59a 100644
--- a/.github/workflows/Lucene-Net-Tests-Analysis-Phonetic.yml
+++ b/.github/workflows/Lucene-Net-Tests-Analysis-Phonetic.yml
@@ -51,7 +51,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -80,13 +80,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -136,4 +136,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Analysis-SmartCn.yml b/.github/workflows/Lucene-Net-Tests-Analysis-SmartCn.yml
index 9ff86161c..737ced4fe 100644
--- a/.github/workflows/Lucene-Net-Tests-Analysis-SmartCn.yml
+++ b/.github/workflows/Lucene-Net-Tests-Analysis-SmartCn.yml
@@ -33,6 +33,7 @@ on:
     - 'src/Lucene.Net.Tests.Analysis.SmartCn/Directory.Build.*'
     - 'src/Directory.Build.*'
     - 'Directory.Build.*'
+    - 'src/Lucene.Net.Tests.Analysis.SmartCn/**/*.zip'
 
     # Dependencies
     - 'src/Lucene.Net/**/*'
@@ -55,7 +56,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -84,13 +85,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -140,4 +141,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Analysis-Stempel.yml b/.github/workflows/Lucene-Net-Tests-Analysis-Stempel.yml
index ea629e425..e2d4e6d65 100644
--- a/.github/workflows/Lucene-Net-Tests-Analysis-Stempel.yml
+++ b/.github/workflows/Lucene-Net-Tests-Analysis-Stempel.yml
@@ -52,7 +52,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -81,13 +81,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -137,4 +137,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Benchmark.yml b/.github/workflows/Lucene-Net-Tests-Benchmark.yml
index 09f0992b4..4cc1e69af 100644
--- a/.github/workflows/Lucene-Net-Tests-Benchmark.yml
+++ b/.github/workflows/Lucene-Net-Tests-Benchmark.yml
@@ -64,7 +64,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -93,13 +93,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -149,4 +149,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Classification.yml b/.github/workflows/Lucene-Net-Tests-Classification.yml
index 8b14fc69a..e198c65af 100644
--- a/.github/workflows/Lucene-Net-Tests-Classification.yml
+++ b/.github/workflows/Lucene-Net-Tests-Classification.yml
@@ -52,7 +52,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -81,13 +81,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -137,4 +137,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Cli.yml b/.github/workflows/Lucene-Net-Tests-Cli.yml
index 5b287101e..f1ea74182 100644
--- a/.github/workflows/Lucene-Net-Tests-Cli.yml
+++ b/.github/workflows/Lucene-Net-Tests-Cli.yml
@@ -71,7 +71,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0]
+        framework: [net10.0, net8.0]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -101,13 +101,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -159,4 +159,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-CodeAnalysis.yml b/.github/workflows/Lucene-Net-Tests-CodeAnalysis.yml
index ebc5c6b1f..6f0a64fb8 100644
--- a/.github/workflows/Lucene-Net-Tests-CodeAnalysis.yml
+++ b/.github/workflows/Lucene-Net-Tests-CodeAnalysis.yml
@@ -50,7 +50,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0]
+        framework: [net8.0]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -79,13 +79,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -135,4 +135,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Codecs.yml b/.github/workflows/Lucene-Net-Tests-Codecs.yml
index 060bcbe10..8805eb01c 100644
--- a/.github/workflows/Lucene-Net-Tests-Codecs.yml
+++ b/.github/workflows/Lucene-Net-Tests-Codecs.yml
@@ -50,7 +50,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -79,13 +79,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -135,4 +135,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Demo.yml b/.github/workflows/Lucene-Net-Tests-Demo.yml
index 91c4a20ec..cef36feee 100644
--- a/.github/workflows/Lucene-Net-Tests-Demo.yml
+++ b/.github/workflows/Lucene-Net-Tests-Demo.yml
@@ -59,7 +59,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -88,13 +88,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -144,4 +144,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Expressions.yml b/.github/workflows/Lucene-Net-Tests-Expressions.yml
index 3791d9b9b..c7e7ebb8c 100644
--- a/.github/workflows/Lucene-Net-Tests-Expressions.yml
+++ b/.github/workflows/Lucene-Net-Tests-Expressions.yml
@@ -52,7 +52,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -81,13 +81,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -137,4 +137,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Facet.yml b/.github/workflows/Lucene-Net-Tests-Facet.yml
index 8ac6e894c..3108bf05a 100644
--- a/.github/workflows/Lucene-Net-Tests-Facet.yml
+++ b/.github/workflows/Lucene-Net-Tests-Facet.yml
@@ -54,7 +54,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -83,13 +83,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -139,4 +139,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Grouping.yml b/.github/workflows/Lucene-Net-Tests-Grouping.yml
index 6c4f3ae7c..b6884fe16 100644
--- a/.github/workflows/Lucene-Net-Tests-Grouping.yml
+++ b/.github/workflows/Lucene-Net-Tests-Grouping.yml
@@ -52,7 +52,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -81,13 +81,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -137,4 +137,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Highlighter.yml b/.github/workflows/Lucene-Net-Tests-Highlighter.yml
index 597ff57ad..baab498ea 100644
--- a/.github/workflows/Lucene-Net-Tests-Highlighter.yml
+++ b/.github/workflows/Lucene-Net-Tests-Highlighter.yml
@@ -54,7 +54,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -83,13 +83,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -139,4 +139,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-ICU.yml b/.github/workflows/Lucene-Net-Tests-ICU.yml
index 87d5b930f..80970d6f8 100644
--- a/.github/workflows/Lucene-Net-Tests-ICU.yml
+++ b/.github/workflows/Lucene-Net-Tests-ICU.yml
@@ -67,7 +67,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -96,13 +96,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -152,4 +152,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Join.yml b/.github/workflows/Lucene-Net-Tests-Join.yml
index ab2691faf..19bc60fb1 100644
--- a/.github/workflows/Lucene-Net-Tests-Join.yml
+++ b/.github/workflows/Lucene-Net-Tests-Join.yml
@@ -53,7 +53,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -82,13 +82,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -138,4 +138,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Memory.yml b/.github/workflows/Lucene-Net-Tests-Memory.yml
index c04f54847..4cc5f290e 100644
--- a/.github/workflows/Lucene-Net-Tests-Memory.yml
+++ b/.github/workflows/Lucene-Net-Tests-Memory.yml
@@ -55,7 +55,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -84,13 +84,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -140,4 +140,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Misc.yml b/.github/workflows/Lucene-Net-Tests-Misc.yml
index bc191603d..0208111bf 100644
--- a/.github/workflows/Lucene-Net-Tests-Misc.yml
+++ b/.github/workflows/Lucene-Net-Tests-Misc.yml
@@ -51,7 +51,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -80,13 +80,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -136,4 +136,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Queries.yml b/.github/workflows/Lucene-Net-Tests-Queries.yml
index 9502671d3..161b7a7e1 100644
--- a/.github/workflows/Lucene-Net-Tests-Queries.yml
+++ b/.github/workflows/Lucene-Net-Tests-Queries.yml
@@ -51,7 +51,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -80,13 +80,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -136,4 +136,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-QueryParser.yml b/.github/workflows/Lucene-Net-Tests-QueryParser.yml
index fe5d8b254..cd1f76b45 100644
--- a/.github/workflows/Lucene-Net-Tests-QueryParser.yml
+++ b/.github/workflows/Lucene-Net-Tests-QueryParser.yml
@@ -57,7 +57,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -86,13 +86,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -142,4 +142,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Replicator.yml b/.github/workflows/Lucene-Net-Tests-Replicator.yml
index 362084b89..122a36cda 100644
--- a/.github/workflows/Lucene-Net-Tests-Replicator.yml
+++ b/.github/workflows/Lucene-Net-Tests-Replicator.yml
@@ -56,7 +56,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -85,13 +85,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -141,4 +141,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Sandbox.yml b/.github/workflows/Lucene-Net-Tests-Sandbox.yml
index fc3a50374..b02a3beb9 100644
--- a/.github/workflows/Lucene-Net-Tests-Sandbox.yml
+++ b/.github/workflows/Lucene-Net-Tests-Sandbox.yml
@@ -52,7 +52,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -81,13 +81,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -137,4 +137,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Spatial.yml b/.github/workflows/Lucene-Net-Tests-Spatial.yml
index b1fb2d227..6ab03d1d4 100644
--- a/.github/workflows/Lucene-Net-Tests-Spatial.yml
+++ b/.github/workflows/Lucene-Net-Tests-Spatial.yml
@@ -53,7 +53,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -82,13 +82,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -138,4 +138,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-Suggest.yml b/.github/workflows/Lucene-Net-Tests-Suggest.yml
index 477e02dbb..b2bdf9b7d 100644
--- a/.github/workflows/Lucene-Net-Tests-Suggest.yml
+++ b/.github/workflows/Lucene-Net-Tests-Suggest.yml
@@ -54,7 +54,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -83,13 +83,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -139,4 +139,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-TestFramework-DependencyInjection.yml b/.github/workflows/Lucene-Net-Tests-TestFramework-DependencyInjection.yml
index fd6403554..2db6fffe5 100644
--- a/.github/workflows/Lucene-Net-Tests-TestFramework-DependencyInjection.yml
+++ b/.github/workflows/Lucene-Net-Tests-TestFramework-DependencyInjection.yml
@@ -50,7 +50,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -79,13 +79,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -135,4 +135,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-TestFramework-NUnitExtensions.yml b/.github/workflows/Lucene-Net-Tests-TestFramework-NUnitExtensions.yml
index 0a5742cfd..a89d43939 100644
--- a/.github/workflows/Lucene-Net-Tests-TestFramework-NUnitExtensions.yml
+++ b/.github/workflows/Lucene-Net-Tests-TestFramework-NUnitExtensions.yml
@@ -51,7 +51,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -80,13 +80,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -136,4 +136,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-TestFramework.yml b/.github/workflows/Lucene-Net-Tests-TestFramework.yml
index 0acf43a7c..f6d5dfd55 100644
--- a/.github/workflows/Lucene-Net-Tests-TestFramework.yml
+++ b/.github/workflows/Lucene-Net-Tests-TestFramework.yml
@@ -52,7 +52,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -81,13 +81,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -137,4 +137,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-_A-D.yml b/.github/workflows/Lucene-Net-Tests-_A-D.yml
index 028c55444..c6d3fc8ef 100644
--- a/.github/workflows/Lucene-Net-Tests-_A-D.yml
+++ b/.github/workflows/Lucene-Net-Tests-_A-D.yml
@@ -61,7 +61,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -90,13 +90,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -146,4 +146,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-_E-I.yml b/.github/workflows/Lucene-Net-Tests-_E-I.yml
index 82d755be9..b11a6f9ef 100644
--- a/.github/workflows/Lucene-Net-Tests-_E-I.yml
+++ b/.github/workflows/Lucene-Net-Tests-_E-I.yml
@@ -71,7 +71,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -100,13 +100,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -156,4 +156,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-_I-J.yml b/.github/workflows/Lucene-Net-Tests-_I-J.yml
index 3aaae40de..0efeffe72 100644
--- a/.github/workflows/Lucene-Net-Tests-_I-J.yml
+++ b/.github/workflows/Lucene-Net-Tests-_I-J.yml
@@ -76,7 +76,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -105,13 +105,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -161,4 +161,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-_J-S.yml b/.github/workflows/Lucene-Net-Tests-_J-S.yml
index d7e6268c9..e94bce4c3 100644
--- a/.github/workflows/Lucene-Net-Tests-_J-S.yml
+++ b/.github/workflows/Lucene-Net-Tests-_J-S.yml
@@ -63,7 +63,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -92,13 +92,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -148,4 +148,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/.github/workflows/Lucene-Net-Tests-_T-Z.yml b/.github/workflows/Lucene-Net-Tests-_T-Z.yml
index 825509c95..c723521fa 100644
--- a/.github/workflows/Lucene-Net-Tests-_T-Z.yml
+++ b/.github/workflows/Lucene-Net-Tests-_T-Z.yml
@@ -35,6 +35,7 @@ on:
     - 'Directory.Build.*'
     - 'src/Lucene.Net.Tests/Properties/AssemblyInfo.cs'
     - 'src/Lucene.Net.Tests/Util/**/*.cs'
+    - 'src/Lucene.Net.Tests/Support/TestHelpers.cs'
 
     # Dependencies
     - 'src/Lucene.Net/**/*'
@@ -58,7 +59,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [windows-latest, ubuntu-latest]
-        framework: [net9.0, net8.0, net48, net472]
+        framework: [net10.0, net8.0, net48, net472]
         platform: [x64]
         configuration: [Release]
         exclude:
@@ -87,13 +88,13 @@ jobs:
       - name: Setup .NET 8 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '8.0.415'
+          dotnet-version: '8.0.x'
         if: ${{ startswith(matrix.framework, 'net8.') }}
 
-      - name: Setup .NET 9 SDK
+      - name: Setup .NET 10 SDK
         uses: actions/setup-dotnet@v5
         with:
-          dotnet-version: '9.0.306'
+          dotnet-version: '10.0.x'
 
       - name: Cache NuGet Packages
         uses: actions/cache@v5
@@ -143,4 +144,3 @@ jobs:
           if (Test-Path $md_file) {
               Get-Content $md_file | Add-Content $env:GITHUB_STEP_SUMMARY
           }
-
diff --git a/Directory.Build.props b/Directory.Build.props
index ab0ecca57..fbf1d96e0 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -31,6 +31,26 @@
     <GitHubProject>lucenenet</GitHubProject>
   </PropertyGroup>
 
+  <PropertyGroup Label="Legacy Visual Studio Support">
+    <VisualStudioMajorVersion>0</VisualStudioMajorVersion>
+    <!-- Extract major version (works for preview builds too) -->
+    <VisualStudioMajorVersion Condition="'$(VisualStudioVersion)' != ''">$([System.Version]::Parse($([System.Text.RegularExpressions.Regex]::Match('$(VisualStudioVersion)', '^\d+(\.\d+)*').Value)).Major)</VisualStudioMajorVersion>
+    <IsLegacyVisualStudioVersion>false</IsLegacyVisualStudioVersion>
+    <!-- Visual Studio 2026 == Version 18.x - anything below that major version is legacy -->
+    <IsLegacyVisualStudioVersion Condition="'$(BuildingInsideVisualStudio)' == 'true' And '$(VisualStudioMajorVersion)' &lt; '18'">true</IsLegacyVisualStudioVersion>
+  </PropertyGroup>
+
+  <PropertyGroup Label="Centralized Target Frameworks">
+    <!-- net10.0 is conditional on VS version (VS2024+ = 18.0) -->
+    <!-- .NET Framework and netstandard2.0 are conditional on VS version (VS2024+ = 18.0) due to invalid build warnings with C# 14.0 build features -->
+
+    <LibraryTargetFrameworks>net10.0;net8.0;netstandard2.0;net462</LibraryTargetFrameworks>
+    <LibraryTargetFrameworks Condition=" '$(IsLegacyVisualStudioVersion)' == 'true' ">net8.0</LibraryTargetFrameworks>
+
+    <NetCoreOnlyTargetFrameworks>net10.0;net8.0</NetCoreOnlyTargetFrameworks>
+    <NetCoreOnlyTargetFrameworks Condition=" '$(IsLegacyVisualStudioVersion)' == 'true' ">net8.0</NetCoreOnlyTargetFrameworks>
+  </PropertyGroup>
+
   <!-- IMPORTANT: When these values are changed, the CI counter number should also be reset. -->
   <PropertyGroup Label="Version of Builds">
     <!-- IMPORTANT: VersionPrefix must always be the same as the Lucene version this is based on.
diff --git a/Directory.Build.targets b/Directory.Build.targets
index 8d73b7030..e090c18b8 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -38,22 +38,22 @@
     <PublishDir Condition="'$(AlternatePublishRootDirectory)' != ''">$(AlternatePublishRootDirectory)/$(TargetFramework)/$(MSBuildProjectName)/</PublishDir>
   </PropertyGroup>
 
-  <!-- Features in .NET 9.x only -->
-  <PropertyGroup Condition=" $(TargetFramework.StartsWith('net9.')) ">
+  <!-- Features in .NET 9.x+ only -->
+  <PropertyGroup Condition=" $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.')) ">
 
     <DefineConstants>$(DefineConstants);FEATURE_STREAM_READEXACTLY</DefineConstants>
 
   </PropertyGroup>
 
-  <!-- Features in .NET 8.x and .NET 9.x only -->
-  <PropertyGroup Condition=" $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) ">
+  <!-- Features in .NET 8.x+ only -->
+  <PropertyGroup Condition=" $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.')) ">
 
     <DefineConstants>$(DefineConstants);FEATURE_ASPNETCORE_TESTHOST</DefineConstants>
 
   </PropertyGroup>
 
-  <!-- Features in .NET 6.x, .NET 7.x, .NET 8.x, and .NET 9.x only -->
-  <PropertyGroup Condition=" $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) ">
+  <!-- Features in .NET 6.x+ only -->
+  <PropertyGroup Condition=" $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.')) ">
 
     <DefineConstants>$(DefineConstants);FEATURE_RANDOM_NEXTINT64_NEXTSINGLE</DefineConstants>
     <DefineConstants>$(DefineConstants);FEATURE_RANDOMACCESS_READ</DefineConstants>
@@ -62,24 +62,24 @@
 
   </PropertyGroup>
 
-  <!-- Features in .NET 5.x, .NET 6.x, .NET 7.x, .NET 8.x, and .NET 9.x only -->
-  <PropertyGroup Condition=" $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) ">
+  <!-- Features in .NET 5.x+ only -->
+  <PropertyGroup Condition=" $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.')) ">
 
     <DefineConstants>$(DefineConstants);FEATURE_MEMORYMARSHAL_GETARRAYDATAREFERENCE</DefineConstants>
     <DefineConstants>$(DefineConstants);FEATURE_READONLYSET</DefineConstants>
 
   </PropertyGroup>
 
-  <!-- Features in .NET Core 3.x, .NET 5.x, .NET 6.x, .NET 7.x, .NET 8.x, and .NET 9.x only -->
-  <PropertyGroup Condition=" $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) ">
+  <!-- Features in .NET Core 3.x and .NET 5.x+ only -->
+  <PropertyGroup Condition=" $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.')) ">
 
     <DefineConstants>$(DefineConstants);FEATURE_PROCESS_KILL_ENTIREPROCESSTREE</DefineConstants>
     <DefineConstants>$(DefineConstants);FEATURE_STRING_CONCAT_READONLYSPAN</DefineConstants>
 
   </PropertyGroup>
 
-  <!-- Features in .NET Standard, .NET Core, .NET 5.x, .NET 6.x, .NET 7.x, .NET 8.x, and .NET 9.x only (no .NET Framework support) -->
-  <PropertyGroup Condition=" $(TargetFramework.StartsWith('netstandard')) Or $(TargetFramework.StartsWith('netcoreapp')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) ">
+  <!-- Features in .NET Standard, .NET Core, and .NET 5.x+ (no .NET Framework support) -->
+  <PropertyGroup Condition=" $(TargetFramework.StartsWith('netstandard')) Or $(TargetFramework.StartsWith('netcoreapp')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.')) ">
 
     <DefineConstants>$(DefineConstants);NETSTANDARD</DefineConstants>
     <DefineConstants>$(DefineConstants);FEATURE_CULTUREINFO_CURRENTCULTURE_SETTER</DefineConstants>
@@ -88,8 +88,8 @@
     <DebugType>portable</DebugType>
   </PropertyGroup>
 
-  <!-- Features in .NET Standard 2.1, .NET Core 3.x, .NET 5.x, .NET 6.x, .NET 7.x, .NET 8.x, and .NET 9.x only -->
-  <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' Or $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) ">
+  <!-- Features in .NET Standard 2.1, .NET Core 3.x, and .NET 5.x+ only -->
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' Or $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.')) ">
 
     <DefineConstants>$(DefineConstants);FEATURE_ARRAY_FILL</DefineConstants>
     <DefineConstants>$(DefineConstants);FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR</DefineConstants>
@@ -102,22 +102,22 @@
 
   </PropertyGroup>
 
-  <!-- Features in .NET Standard 2.1, .NET Core 2.1-2.2, .NET Core 3.x, .NET 5.x, .NET 6.x, .NET 7.x, .NET 8.x, and .NET 9.x only -->
-  <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' Or '$(TargetFramework)' == 'netcoreapp2.1' Or '$(TargetFramework)' == 'netcoreapp2.2' Or $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) ">
+  <!-- Features in .NET Standard 2.1, .NET Core 2.1-2.2, .NET Core 3.x, and .NET 5.x+ only -->
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' Or '$(TargetFramework)' == 'netcoreapp2.1' Or '$(TargetFramework)' == 'netcoreapp2.2' Or $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.')) ">
 
     <DefineConstants>$(DefineConstants);FEATURE_STRING_CONTAINS_STRINGCOMPARISON</DefineConstants>
 
   </PropertyGroup>
 
-  <!-- Features in .NET Standard 2.x, .NET Core 2.x, .NET Core 3.x, .NET 5.x, .NET 6.x, .NET 7.x, .NET 8.x, and .NET 9.x -->
-  <PropertyGroup Condition=" $(TargetFramework.StartsWith('netstandard2.')) Or $(TargetFramework.StartsWith('netcoreapp2.')) Or $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) ">
+  <!-- Features in .NET Standard 2.x, .NET Core 2.x, .NET Core 3.x, and .NET 5.x+ -->
+  <PropertyGroup Condition=" $(TargetFramework.StartsWith('netstandard2.')) Or $(TargetFramework.StartsWith('netcoreapp2.')) Or $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.')) ">
 
     <DefineConstants>$(DefineConstants);FEATURE_ICONFIGURATIONROOT_PROVIDERS</DefineConstants>
 
   </PropertyGroup>
 
-  <!-- Features in .NET Framework 4.5+, .NET Standard 2.x, .NET Core 2.x, .NET Core 3.x, .NET 5.x, .NET 6.x, .NET 7.x, .NET 8.x, and .NET 9.x  -->
-  <PropertyGroup Condition=" $(TargetFramework.StartsWith('net4')) Or $(TargetFramework.StartsWith('netstandard2.')) Or $(TargetFramework.StartsWith('netcoreapp2.')) Or $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) ">
+  <!-- Features in .NET Framework 4.5+, .NET Standard 2.x, .NET Core 2.x, .NET Core 3.x, .NET 5.x+  -->
+  <PropertyGroup Condition=" $(TargetFramework.StartsWith('net4')) Or $(TargetFramework.StartsWith('netstandard2.')) Or $(TargetFramework.StartsWith('netcoreapp2.')) Or $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.')) ">
 
     <DefineConstants>$(DefineConstants);FEATURE_ASSEMBLY_GETCALLINGASSEMBLY</DefineConstants>
     <DefineConstants>$(DefineConstants);FEATURE_FILESTREAM_LOCK</DefineConstants>
@@ -126,7 +126,7 @@
 
   </PropertyGroup>
 
-  <!-- Features in .NET Framework 4.5+, .NET Standard 2.x, .NET Core 2.x, .NET Core 3.x, .NET 5.x, .NET 6.x, .NET 7.x, and .NET 8.x (No .NET 9.x support)  -->
+  <!-- Features in .NET Framework 4.5+, .NET Standard 2.x, .NET Core 2.x, .NET Core 3.x, .NET 5.x, .NET 6.x, .NET 7.x, and .NET 8.x (No .NET 9.x+ support)  -->
   <PropertyGroup Condition=" $(TargetFramework.StartsWith('net4')) Or $(TargetFramework.StartsWith('netstandard2.')) Or $(TargetFramework.StartsWith('netcoreapp2.')) Or $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) Or $(TargetFramework.StartsWith('net8.')) ">
     <DefineConstants>$(DefineConstants);FEATURE_SERIALIZABLE_EXCEPTIONS</DefineConstants>
     <DefineConstants>$(DefineConstants);FEATURE_SERIALIZABLE</DefineConstants>
@@ -163,8 +163,8 @@
   </PropertyGroup>
 
   <!-- Features in .NET Framework 4.5+ and .NET 8.0+ but not in .NET Standard 2.0 or .NET Standard 2.1 -->
-  <!-- net472 is used to test .NET Standard 2.0, and .NET 6.0 for .NET Standard 2.1, so we treat them like it is not part of this group -->
-  <PropertyGroup Condition=" ($(TargetFramework.StartsWith('net4')) And '$(TargetFramework)' != 'net472') Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) ">
+  <!-- net472 is used to test .NET Standard 2.0, so we treat them like it is not part of this group -->
+  <PropertyGroup Condition=" ($(TargetFramework.StartsWith('net4')) And '$(TargetFramework)' != 'net472') Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.')) ">
 
     <DefineConstants>$(DefineConstants);FEATURE_OPENNLP</DefineConstants>
 
@@ -194,7 +194,7 @@
       <OutputTargetFrameworks Condition=" '$(DoOutputTargetFrameworks)' == 'true' And '$(OutputTargetFrameworks)' == '' ">$(TargetFrameworks)</OutputTargetFrameworks>
       <OutputTargetFrameworks Condition=" '$(OutputTargetFrameworks)' == '' ">none</OutputTargetFrameworks>
     </PropertyGroup>
-    <Message Text="$(OutputTargetFrameworks)" Importance="high"/>
+    <Message Text="SupportedTargetFrameworks=$(OutputTargetFrameworks)" Importance="high"/>
   </Target>
 
 
diff --git a/TestTargetFramework.props b/TestTargetFramework.props
index f1c2126f0..4a5b57dee 100644
--- a/TestTargetFramework.props
+++ b/TestTargetFramework.props
@@ -29,7 +29,12 @@
     <!--<TargetFramework>net472</TargetFramework>-->
     <!--<TargetFramework>net48</TargetFramework>-->
     <!--<TargetFramework>net8.0</TargetFramework>-->
-    <TargetFramework>net9.0</TargetFramework>
+    <!--<TargetFramework>net9.0</TargetFramework>-->
+    <TargetFramework>net10.0</TargetFramework>
+
+    <!-- Downgrade when using VS 2022 due to lack of support for net10.0. Testing .NET Fraemework on VS 2022
+        is possible, but be warned that it may produce several misleading build warnings due to lack of analyzer support for C# 14.0 features. -->
+    <TargetFramework Condition=" '$(TargetFramework)' == 'net10.0' And '$(IsLegacyVisualStudioVersion)' == 'true' ">net8.0</TargetFramework>
 
     <!-- Allow the build script to pass in the test frameworks to build for.
       This overrides the above TargetFramework setting.
@@ -37,14 +42,16 @@
 
     <!-- Test Client to DLL target works as follows:
       Test Client       | Target Under Test
+      net10.0           | net10.0
       net9.0            | net8.0
       net8.0            | net8.0
       net48             | net462
       net472            | netstandard2.0
     -->
 
-    <TargetFrameworks Condition=" '$(TestFrameworks)' == 'true' ">net9.0;net8.0</TargetFrameworks>
-    <TargetFrameworks Condition=" '$(TestFrameworks)' == 'true' AND $([MSBuild]::IsOsPlatform('Windows')) ">$(TargetFrameworks);net48;net472</TargetFrameworks>
+    <!-- CI builds - always build all test frameworks (CI installs required SDKs) -->
+    <TargetFrameworks Condition=" '$(TestFrameworks)' == 'true' ">net10.0;net9.0;net8.0</TargetFrameworks>
+    <TargetFrameworks Condition=" '$(TestFrameworks)' == 'true' And $([MSBuild]::IsOsPlatform('Windows'))  And '$(IsLegacyVisualStudioVersion)' != 'true' ">$(TargetFrameworks);net48;net472</TargetFrameworks>
     <TargetFramework Condition=" '$(TargetFrameworks)' != '' "></TargetFramework>
   </PropertyGroup>
 
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index eccd6aecf..b1ee26a20 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -57,7 +57,7 @@ variables:
 - name: BuildCounter
   value: $[counter(variables['VersionSuffix'],coalesce(variables['BuildCounterSeed'], 1250))]
 - name: DotNetSDKVersion
-  value: '9.0.300'
+  value: '10.0.100'
 - name: DocumentationArtifactName
   value: 'docs'
 - name: DocumentationArtifactZipFileName
@@ -233,6 +233,15 @@ stages:
       displayName: 'Delete temp publish location: $(NuGetArtifactDirectory)'
       condition: and(succeeded(), ne(variables['RunPack'], 'false'))
 
+    - template: '.build/azure-templates/publish-test-binaries.yml'
+      parameters:
+        publishDirectory: $(PublishDirectory)
+        framework: 'net10.0'
+        binaryArtifactName: '$(BinaryArtifactName)'
+        testSettingsFilePath: '$(Build.ArtifactStagingDirectory)/$(TestSettingsFileName)'
+        configuration: '$(BuildConfiguration)'
+        platform: '$(BuildPlatform)'
+
     - template: '.build/azure-templates/publish-test-binaries.yml'
       parameters:
         publishDirectory: $(PublishDirectory)
@@ -330,7 +339,7 @@ stages:
   displayName: 'Test Stage:'
   jobs:
 
-  - job: Test_net9_0_x64
+  - job: Test_net10_0_x64
     condition: and(succeeded(), ne(variables['RunTests'], 'false'))
     strategy:
       matrix:
@@ -349,14 +358,14 @@ stages:
           imageName: 'macOS-latest'
           maximumParallelJobs: 7
           maximumAllowedFailures: 0 # Maximum allowed failures for a successful build
-    displayName: 'Test net9.0,x64 on'
+    displayName: 'Test net10.0,x64 on'
     pool:
       vmImage: $(imageName)
     steps:
       - template: '.build/azure-templates/run-tests-on-os.yml'
         parameters:
           osName: $(osName)
-          framework: 'net9.0'
+          framework: 'net10.0'
           vsTestPlatform: 'x64'
           testBinariesArtifactName: '$(TestBinariesArtifactName)'
           nugetArtifactName: '$(NuGetArtifactName)'
@@ -365,6 +374,66 @@ stages:
           maximumAllowedFailures: $(maximumAllowedFailures)
           dotNetSdkVersion: '$(DotNetSDKVersion)'
 
+  - job: Test_net10_0_x86 # Only run Nightly or if explicitly enabled with RunX86Tests
+    condition: and(succeeded(), ne(variables['RunTests'], 'false'), or(eq(variables['IsNightly'], 'true'), eq(variables['RunX86Tests'], 'true')))
+    strategy:
+      matrix:
+        Windows:
+          osName: 'Windows'
+          imageName: 'windows-latest'
+          maximumParallelJobs: 8
+          maximumAllowedFailures: 0 # Maximum allowed failures for a successful build
+    displayName: 'Test net10.0,x86 on'
+    pool:
+      vmImage: $(imageName)
+    steps:
+      - template: '.build/azure-templates/run-tests-on-os.yml'
+        parameters:
+          osName: $(osName)
+          framework: 'net10.0'
+          vsTestPlatform: 'x86'
+          testBinariesArtifactName: '$(TestBinariesArtifactName)'
+          nugetArtifactName: '$(NuGetArtifactName)'
+          testResultsArtifactName: '$(TestResultsArtifactName)'
+          maximumParallelJobs: $(maximumParallelJobs)
+          maximumAllowedFailures: $(maximumAllowedFailures)
+          dotNetSdkVersion: '$(DotNetSDKVersion)'
+
+  - job: Test_net9_0_x64
+    condition: and(succeeded(), ne(variables['RunTests'], 'false'))
+    strategy:
+      matrix:
+        Windows:
+          osName: 'Windows'
+          imageName: 'windows-latest'
+          maximumParallelJobs: 8
+          maximumAllowedFailures: 0 # Maximum allowed failures for a successful build
+        Linux:
+          osName: 'Linux'
+          imageName: 'ubuntu-latest'
+          maximumParallelJobs: 7
+          maximumAllowedFailures: 0 # Maximum allowed failures for a successful build
+        macOS:
+          osName: 'macOS'
+          imageName: 'macOS-latest'
+          maximumParallelJobs: 7
+          maximumAllowedFailures: 0 # Maximum allowed failures for a successful build
+    displayName: 'Test net9.0,x64 on'
+    pool:
+      vmImage: $(imageName)
+    steps:
+    - template: '.build/azure-templates/run-tests-on-os.yml'
+      parameters:
+        osName: $(osName)
+        framework: 'net9.0'
+        vsTestPlatform: 'x64'
+        testBinariesArtifactName: '$(TestBinariesArtifactName)'
+        nugetArtifactName: '$(NuGetArtifactName)'
+        testResultsArtifactName: '$(TestResultsArtifactName)'
+        maximumParallelJobs: $(maximumParallelJobs)
+        maximumAllowedFailures: $(maximumAllowedFailures)
+        dotNetSdkVersion: '9.0.308'
+
   - job: Test_net9_0_x86 # Only run Nightly or if explicitly enabled with RunX86Tests
     condition: and(succeeded(), ne(variables['RunTests'], 'false'), or(eq(variables['IsNightly'], 'true'), eq(variables['RunX86Tests'], 'true')))
     strategy:
@@ -388,7 +457,7 @@ stages:
           testResultsArtifactName: '$(TestResultsArtifactName)'
           maximumParallelJobs: $(maximumParallelJobs)
           maximumAllowedFailures: $(maximumAllowedFailures)
-          dotNetSdkVersion: '$(DotNetSDKVersion)'
+          dotNetSdkVersion: '9.0.308'
 
   - job: Test_net8_0_x64
     condition: and(succeeded(), ne(variables['RunTests'], 'false'))
diff --git a/src/Lucene.Net.Analysis.Common/Lucene.Net.Analysis.Common.csproj b/src/Lucene.Net.Analysis.Common/Lucene.Net.Analysis.Common.csproj
index b6c45073e..64a503e2b 100644
--- a/src/Lucene.Net.Analysis.Common/Lucene.Net.Analysis.Common.csproj
+++ b/src/Lucene.Net.Analysis.Common/Lucene.Net.Analysis.Common.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Analysis.Common</AssemblyTitle>
     <PackageTags>$(PackageTags);analysis</PackageTags>
diff --git a/src/Lucene.Net.Analysis.Kuromoji/Lucene.Net.Analysis.Kuromoji.csproj b/src/Lucene.Net.Analysis.Kuromoji/Lucene.Net.Analysis.Kuromoji.csproj
index 9fb9d57ef..32ca2bc2e 100644
--- a/src/Lucene.Net.Analysis.Kuromoji/Lucene.Net.Analysis.Kuromoji.csproj
+++ b/src/Lucene.Net.Analysis.Kuromoji/Lucene.Net.Analysis.Kuromoji.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Analysis.Kuromoji</AssemblyTitle>
     <PackageTags>$(PackageTags);analysis;japanese</PackageTags>
diff --git a/src/Lucene.Net.Analysis.Morfologik/Lucene.Net.Analysis.Morfologik.csproj b/src/Lucene.Net.Analysis.Morfologik/Lucene.Net.Analysis.Morfologik.csproj
index cd002dbc1..907c39794 100644
--- a/src/Lucene.Net.Analysis.Morfologik/Lucene.Net.Analysis.Morfologik.csproj
+++ b/src/Lucene.Net.Analysis.Morfologik/Lucene.Net.Analysis.Morfologik.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Analysis.Morfologik</AssemblyTitle>
     <RootNamespace>Lucene.Net.Analysis</RootNamespace>
diff --git a/src/Lucene.Net.Analysis.OpenNLP/Lucene.Net.Analysis.OpenNLP.csproj b/src/Lucene.Net.Analysis.OpenNLP/Lucene.Net.Analysis.OpenNLP.csproj
index 74d3b0c90..4f2adbf98 100644
--- a/src/Lucene.Net.Analysis.OpenNLP/Lucene.Net.Analysis.OpenNLP.csproj
+++ b/src/Lucene.Net.Analysis.OpenNLP/Lucene.Net.Analysis.OpenNLP.csproj
@@ -32,9 +32,11 @@
   <PropertyGroup>
     <!-- Currently, IKVM doesn't officially support building NetFX on anything but Windows, so we skip it for contributors who may be on various platforms.
         We can remove the condition once that has been addressed. See: https://github.com/ikvmnet/ikvm-maven/issues/49 -->
-    <!--suppress MsbuildTargetFrameworkTagInspection - even though this only has one target right now, we need to use the plural version for the line below -->
-    <TargetFrameworks>net8.0</TargetFrameworks>
-    <TargetFrameworks Condition="$([MSBuild]::IsOsPlatform('Windows'))">$(TargetFrameworks);net472</TargetFrameworks>
+    <!-- net10.0 is conditional on VS version (VS2024+ = 18.0) -->
+    <!-- .NET Framework is conditional on VS version (VS2024+ = 18.0) due to invalid build warnings with C# 14.0 build features -->
+    <TargetFrameworks>net10.0;net8.0</TargetFrameworks>
+    <TargetFrameworks Condition=" '$(IsLegacyVisualStudioVersion)' == 'true' ">net8.0</TargetFrameworks>
+    <TargetFrameworks Condition=" $([MSBuild]::IsOsPlatform('Windows')) And '$(IsLegacyVisualStudioVersion)' != 'true' ">$(TargetFrameworks);net472</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Analysis.OpenNLP</AssemblyTitle>
     <PackageTags>$(PackageTags);analysis;natural;language;processing;opennlp</PackageTags>
diff --git a/src/Lucene.Net.Analysis.Phonetic/Language/Bm/Languages.cs b/src/Lucene.Net.Analysis.Phonetic/Language/Bm/Languages.cs
index 6eb9afc5c..f92fcd0a2 100644
--- a/src/Lucene.Net.Analysis.Phonetic/Language/Bm/Languages.cs
+++ b/src/Lucene.Net.Analysis.Phonetic/Language/Bm/Languages.cs
@@ -125,7 +125,8 @@ public static Languages GetInstance(string languagesResourceName)
                 }
             }
 
-            return new Languages(ls.AsReadOnly());
+            // ReSharper disable once InvokeAsExtensionMethod - net10.0 has a conflict with the ambiguous overloads
+            return new Languages(SetExtensions.AsReadOnly(ls));
         }
 
         private static string LangResourceName(NameType nameType)
@@ -241,7 +242,8 @@ public sealed class SomeLanguages : LanguageSet
 
         internal SomeLanguages(ISet<string> languages)
         {
-            this.languages = languages.AsReadOnly();
+            // ReSharper disable once InvokeAsExtensionMethod - net10.0 has a conflict with the ambiguous overloads
+            this.languages = SetExtensions.AsReadOnly(languages);
         }
 
         public override bool Contains(string language)
diff --git a/src/Lucene.Net.Analysis.Phonetic/Lucene.Net.Analysis.Phonetic.csproj b/src/Lucene.Net.Analysis.Phonetic/Lucene.Net.Analysis.Phonetic.csproj
index 3be3c4c72..5ff60dd7f 100644
--- a/src/Lucene.Net.Analysis.Phonetic/Lucene.Net.Analysis.Phonetic.csproj
+++ b/src/Lucene.Net.Analysis.Phonetic/Lucene.Net.Analysis.Phonetic.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Analysis.Phonetic</AssemblyTitle>
     <PackageTags>$(PackageTags);analysis;soundex;double;metaphone;sounds;like;beider;morse;cologne;caverphone;nysiis;match;rating</PackageTags>
diff --git a/src/Lucene.Net.Analysis.SmartCn/Lucene.Net.Analysis.SmartCn.csproj b/src/Lucene.Net.Analysis.SmartCn/Lucene.Net.Analysis.SmartCn.csproj
index 8f9835d10..11f2f5b18 100644
--- a/src/Lucene.Net.Analysis.SmartCn/Lucene.Net.Analysis.SmartCn.csproj
+++ b/src/Lucene.Net.Analysis.SmartCn/Lucene.Net.Analysis.SmartCn.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Analysis.SmartCn</AssemblyTitle>
     <PackageTags>$(PackageTags);analysis;chinese;smart</PackageTags>
diff --git a/src/Lucene.Net.Analysis.Stempel/Lucene.Net.Analysis.Stempel.csproj b/src/Lucene.Net.Analysis.Stempel/Lucene.Net.Analysis.Stempel.csproj
index 1ce52b776..9a650c622 100644
--- a/src/Lucene.Net.Analysis.Stempel/Lucene.Net.Analysis.Stempel.csproj
+++ b/src/Lucene.Net.Analysis.Stempel/Lucene.Net.Analysis.Stempel.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Analysis.Stempel</AssemblyTitle>
     <PackageTags>$(PackageTags);analysis;polish</PackageTags>
diff --git a/src/Lucene.Net.Benchmark/Lucene.Net.Benchmark.csproj b/src/Lucene.Net.Benchmark/Lucene.Net.Benchmark.csproj
index 506a22856..d69218bfe 100644
--- a/src/Lucene.Net.Benchmark/Lucene.Net.Benchmark.csproj
+++ b/src/Lucene.Net.Benchmark/Lucene.Net.Benchmark.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Benchmark</AssemblyTitle>
     <PackageTags>$(PackageTags);benchmark</PackageTags>
diff --git a/src/Lucene.Net.Classification/Lucene.Net.Classification.csproj b/src/Lucene.Net.Classification/Lucene.Net.Classification.csproj
index 996801966..b4da787b4 100644
--- a/src/Lucene.Net.Classification/Lucene.Net.Classification.csproj
+++ b/src/Lucene.Net.Classification/Lucene.Net.Classification.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Classification</AssemblyTitle>
     <PackageTags>$(PackageTags);classification</PackageTags>
diff --git a/src/Lucene.Net.Codecs/Lucene.Net.Codecs.csproj b/src/Lucene.Net.Codecs/Lucene.Net.Codecs.csproj
index 0ea23e4b1..0c4fcf4d7 100644
--- a/src/Lucene.Net.Codecs/Lucene.Net.Codecs.csproj
+++ b/src/Lucene.Net.Codecs/Lucene.Net.Codecs.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Codecs</AssemblyTitle>
     <PackageTags>$(PackageTags);codec</PackageTags>
diff --git a/src/Lucene.Net.Demo/Lucene.Net.Demo.csproj b/src/Lucene.Net.Demo/Lucene.Net.Demo.csproj
index 4e6169364..6a016dea8 100644
--- a/src/Lucene.Net.Demo/Lucene.Net.Demo.csproj
+++ b/src/Lucene.Net.Demo/Lucene.Net.Demo.csproj
@@ -24,7 +24,7 @@
   <!-- Demo is deployed through the dotnet/tools/lucene-cli package -->
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Demo</AssemblyTitle>
     <Description>Simple example code for the Lucene.Net full-text search engine library from The Apache Software Foundation.</Description>
diff --git a/src/Lucene.Net.Expressions/Lucene.Net.Expressions.csproj b/src/Lucene.Net.Expressions/Lucene.Net.Expressions.csproj
index 6ef545793..ff91d268c 100644
--- a/src/Lucene.Net.Expressions/Lucene.Net.Expressions.csproj
+++ b/src/Lucene.Net.Expressions/Lucene.Net.Expressions.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Expressions</AssemblyTitle>
     <PackageTags>$(PackageTags);expression</PackageTags>
diff --git a/src/Lucene.Net.Facet/Lucene.Net.Facet.csproj b/src/Lucene.Net.Facet/Lucene.Net.Facet.csproj
index 78b09cca2..ee01a5098 100644
--- a/src/Lucene.Net.Facet/Lucene.Net.Facet.csproj
+++ b/src/Lucene.Net.Facet/Lucene.Net.Facet.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Facet</AssemblyTitle>
     <PackageTags>$(PackageTags);facet;faceted</PackageTags>
diff --git a/src/Lucene.Net.Grouping/Lucene.Net.Grouping.csproj b/src/Lucene.Net.Grouping/Lucene.Net.Grouping.csproj
index 32bc7715b..b2c60f90f 100644
--- a/src/Lucene.Net.Grouping/Lucene.Net.Grouping.csproj
+++ b/src/Lucene.Net.Grouping/Lucene.Net.Grouping.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Grouping</AssemblyTitle>
     <PackageTags>$(PackageTags);grouping;group</PackageTags>
diff --git a/src/Lucene.Net.Highlighter/Lucene.Net.Highlighter.csproj b/src/Lucene.Net.Highlighter/Lucene.Net.Highlighter.csproj
index 16d5ca2e3..686bda916 100644
--- a/src/Lucene.Net.Highlighter/Lucene.Net.Highlighter.csproj
+++ b/src/Lucene.Net.Highlighter/Lucene.Net.Highlighter.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Highlighter</AssemblyTitle>
     <PackageTags>$(PackageTags);highlight;highlighter</PackageTags>
diff --git a/src/Lucene.Net.Join/Lucene.Net.Join.csproj b/src/Lucene.Net.Join/Lucene.Net.Join.csproj
index 80574bd23..d33c703fe 100644
--- a/src/Lucene.Net.Join/Lucene.Net.Join.csproj
+++ b/src/Lucene.Net.Join/Lucene.Net.Join.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <RootNamespace>Lucene.Net.Search.Join</RootNamespace>
 
diff --git a/src/Lucene.Net.Memory/Lucene.Net.Memory.csproj b/src/Lucene.Net.Memory/Lucene.Net.Memory.csproj
index b3800fdbb..b03f5334d 100644
--- a/src/Lucene.Net.Memory/Lucene.Net.Memory.csproj
+++ b/src/Lucene.Net.Memory/Lucene.Net.Memory.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Memory</AssemblyTitle>
     <PackageTags>$(PackageTags);memory</PackageTags>
diff --git a/src/Lucene.Net.Misc/Lucene.Net.Misc.csproj b/src/Lucene.Net.Misc/Lucene.Net.Misc.csproj
index 78dafb625..688a05ae4 100644
--- a/src/Lucene.Net.Misc/Lucene.Net.Misc.csproj
+++ b/src/Lucene.Net.Misc/Lucene.Net.Misc.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Misc</AssemblyTitle>
     <PackageTags>$(PackageTags);miscellaneous</PackageTags>
diff --git a/src/Lucene.Net.Queries/Lucene.Net.Queries.csproj b/src/Lucene.Net.Queries/Lucene.Net.Queries.csproj
index 9708b6857..57f872efd 100644
--- a/src/Lucene.Net.Queries/Lucene.Net.Queries.csproj
+++ b/src/Lucene.Net.Queries/Lucene.Net.Queries.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Queries</AssemblyTitle>
     <PackageTags>$(PackageTags);query;queries</PackageTags>
diff --git a/src/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj b/src/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
index 6f272b8b2..cc1ac3ab1 100644
--- a/src/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
+++ b/src/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <RootNamespace>Lucene.Net.QueryParsers</RootNamespace>
 
diff --git a/src/Lucene.Net.Replicator/Lucene.Net.Replicator.csproj b/src/Lucene.Net.Replicator/Lucene.Net.Replicator.csproj
index 16e8a9692..9d1d695b9 100644
--- a/src/Lucene.Net.Replicator/Lucene.Net.Replicator.csproj
+++ b/src/Lucene.Net.Replicator/Lucene.Net.Replicator.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Replicator</AssemblyTitle>
     <PackageTags>$(PackageTags);files;replication;replicate</PackageTags>
diff --git a/src/Lucene.Net.Sandbox/Lucene.Net.Sandbox.csproj b/src/Lucene.Net.Sandbox/Lucene.Net.Sandbox.csproj
index 500930c14..116c02148 100644
--- a/src/Lucene.Net.Sandbox/Lucene.Net.Sandbox.csproj
+++ b/src/Lucene.Net.Sandbox/Lucene.Net.Sandbox.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Sandbox</AssemblyTitle>
     <PackageTags>$(PackageTags);sandbox</PackageTags>
diff --git a/src/Lucene.Net.Spatial/Lucene.Net.Spatial.csproj b/src/Lucene.Net.Spatial/Lucene.Net.Spatial.csproj
index 180441b00..c3368efe3 100644
--- a/src/Lucene.Net.Spatial/Lucene.Net.Spatial.csproj
+++ b/src/Lucene.Net.Spatial/Lucene.Net.Spatial.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Spatial</AssemblyTitle>
     <PackageTags>$(PackageTags);spatial;geo;geospatial;2d</PackageTags>
diff --git a/src/Lucene.Net.Suggest/Lucene.Net.Suggest.csproj b/src/Lucene.Net.Suggest/Lucene.Net.Suggest.csproj
index 025987312..7bb770226 100644
--- a/src/Lucene.Net.Suggest/Lucene.Net.Suggest.csproj
+++ b/src/Lucene.Net.Suggest/Lucene.Net.Suggest.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Suggest</AssemblyTitle>
     <PackageTags>$(PackageTags);suggest;suggestion</PackageTags>
diff --git a/src/Lucene.Net.TestFramework/Lucene.Net.TestFramework.csproj b/src/Lucene.Net.TestFramework/Lucene.Net.TestFramework.csproj
index 86e9c6b2a..1f89c94e1 100644
--- a/src/Lucene.Net.TestFramework/Lucene.Net.TestFramework.csproj
+++ b/src/Lucene.Net.TestFramework/Lucene.Net.TestFramework.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.TestFramework</AssemblyTitle>
     <RootNamespace>Lucene.Net</RootNamespace>
diff --git a/src/Lucene.Net.Tests.AllProjects/Lucene.Net.Tests.AllProjects.csproj b/src/Lucene.Net.Tests.AllProjects/Lucene.Net.Tests.AllProjects.csproj
index 455ae0608..bb75f3b8e 100644
--- a/src/Lucene.Net.Tests.AllProjects/Lucene.Net.Tests.AllProjects.csproj
+++ b/src/Lucene.Net.Tests.AllProjects/Lucene.Net.Tests.AllProjects.csproj
@@ -118,7 +118,7 @@
 
   </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net48' Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) ">
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net48' Or $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.'))">
     <ProjectReference Include="..\Lucene.Net.Analysis.OpenNLP\Lucene.Net.Analysis.OpenNLP.csproj" />
   </ItemGroup>
 
diff --git a/src/Lucene.Net.Tests.Analysis.OpenNLP/Lucene.Net.Tests.Analysis.OpenNLP.csproj b/src/Lucene.Net.Tests.Analysis.OpenNLP/Lucene.Net.Tests.Analysis.OpenNLP.csproj
index 946db66d7..516364ce2 100644
--- a/src/Lucene.Net.Tests.Analysis.OpenNLP/Lucene.Net.Tests.Analysis.OpenNLP.csproj
+++ b/src/Lucene.Net.Tests.Analysis.OpenNLP/Lucene.Net.Tests.Analysis.OpenNLP.csproj
@@ -25,9 +25,11 @@
 
   <PropertyGroup>
     <!-- Allow specific target framework to flow in from TestTargetFrameworks.props -->
+    <!-- net10.0 is conditional on VS version (VS2024+ = 18.0) -->
     <!--suppress MsbuildTargetFrameworkTagInspection - even though this only has one target right now, we need to use the plural version for the line below -->
-    <TargetFrameworks Condition=" '$(TargetFramework)' == '' ">net9.0;net8.0</TargetFrameworks>
-    <TargetFrameworks Condition=" '$(TargetFramework)' == '' AND $([MSBuild]::IsOsPlatform('Windows')) ">$(TargetFrameworks);net48</TargetFrameworks>
+    <TargetFrameworks Condition=" '$(TargetFramework)' == '' ">net10.0;net9.0;net8.0</TargetFrameworks>
+    <TargetFrameworks Condition=" '$(TargetFramework)' == '' And '$(IsLegacyVisualStudioVersion)' == 'true' ">net9.0;net8.0</TargetFrameworks>
+    <TargetFrameworks Condition=" '$(TargetFramework)' == '' And $([MSBuild]::IsOsPlatform('Windows')) And '$(IsLegacyVisualStudioVersion)' != 'true' ">$(TargetFrameworks);net48</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Tests.Analysis.OpenNLP</AssemblyTitle>
     <RootNamespace>Lucene.Net.Analysis.OpenNlp</RootNamespace>
@@ -39,7 +41,7 @@
     <IsPublishable>false</IsPublishable>
     <IsTestProject>false</IsTestProject>
     <!-- For the IDE, the above doesn't work. Redirect to a supported test framework instead. -->
-    <TargetFramework>net48</TargetFramework>
+    <TargetFramework Condition=" '$(TargetFramework)' == 'net472' ">net48</TargetFramework>
   </PropertyGroup>
 
   <ItemGroup>
diff --git a/src/Lucene.Net.Tests.Replicator/Lucene.Net.Tests.Replicator.csproj b/src/Lucene.Net.Tests.Replicator/Lucene.Net.Tests.Replicator.csproj
index 51e37c3a2..b14b1b137 100644
--- a/src/Lucene.Net.Tests.Replicator/Lucene.Net.Tests.Replicator.csproj
+++ b/src/Lucene.Net.Tests.Replicator/Lucene.Net.Tests.Replicator.csproj
@@ -57,13 +57,13 @@
     </ProjectReference>
   </ItemGroup>
 
-  <ItemGroup Condition="$(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.'))">
+  <ItemGroup Condition="$(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.'))">
     <ProjectReference Include="..\dotnet\Lucene.Net.Replicator.AspNetCore\Lucene.Net.Replicator.AspNetCore.csproj" />
   </ItemGroup>
 
   <Import Project="$(SolutionDir).build/TestReferences.Common.targets" />
 
-  <ItemGroup Condition="$(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.'))">
+  <ItemGroup Condition="$(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net9.')) Or $(TargetFramework.StartsWith('net10.'))">
     <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftAspNetCoreTestHostPackageVersion)" />
   </ItemGroup>
 
diff --git a/src/Lucene.Net/Index/SegmentInfo.cs b/src/Lucene.Net/Index/SegmentInfo.cs
index 23473867d..f9eb9401b 100644
--- a/src/Lucene.Net/Index/SegmentInfo.cs
+++ b/src/Lucene.Net/Index/SegmentInfo.cs
@@ -174,7 +174,8 @@ public ISet<string> GetFiles()
             {
                 throw IllegalStateException.Create("files were not computed yet");
             }
-            return setFiles.AsReadOnly();
+            // ReSharper disable once InvokeAsExtensionMethod - net10.0 has a conflict with the ambiguous overloads
+            return SetExtensions.AsReadOnly(setFiles);
         }
 
         public override string ToString()
diff --git a/src/Lucene.Net/Lucene.Net.csproj b/src/Lucene.Net/Lucene.Net.csproj
index 8b2e5ba5d..0a9fee95f 100644
--- a/src/Lucene.Net/Lucene.Net.csproj
+++ b/src/Lucene.Net/Lucene.Net.csproj
@@ -24,7 +24,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net</AssemblyTitle>
     <Description>Lucene.Net is a full-text search engine library capable of advanced text analysis, indexing, and searching. It can be used to easily add search capabilities to applications. Lucene.Net is a C# port of the popular Java Lucene search engine framework from The Apache Software Foundation, targeted at .NET Framework and .NET Core users.</Description>
diff --git a/src/dotnet/Lucene.Net.ICU/Lucene.Net.ICU.csproj b/src/dotnet/Lucene.Net.ICU/Lucene.Net.ICU.csproj
index fa0805820..11334ff35 100644
--- a/src/dotnet/Lucene.Net.ICU/Lucene.Net.ICU.csproj
+++ b/src/dotnet/Lucene.Net.ICU/Lucene.Net.ICU.csproj
@@ -30,7 +30,7 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.ICU</AssemblyTitle>
     <PackageTags>$(PackageTags);icu;international;unicode</PackageTags>
diff --git a/src/dotnet/Lucene.Net.Replicator.AspNetCore/Lucene.Net.Replicator.AspNetCore.csproj b/src/dotnet/Lucene.Net.Replicator.AspNetCore/Lucene.Net.Replicator.AspNetCore.csproj
index 2513492c4..99e98d605 100644
--- a/src/dotnet/Lucene.Net.Replicator.AspNetCore/Lucene.Net.Replicator.AspNetCore.csproj
+++ b/src/dotnet/Lucene.Net.Replicator.AspNetCore/Lucene.Net.Replicator.AspNetCore.csproj
@@ -25,7 +25,7 @@
   <!--<Import Project="$(SolutionDir).build/nuget.props" />-->
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0</TargetFrameworks>
+    <TargetFrameworks>$(NetCoreOnlyTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.Replicator.AspNetCore</AssemblyTitle>
     <Description>AspNetCore integration of Lucene.Net.Replicator for the Lucene.Net full-text search engine library from The Apache Software Foundation.</Description>
diff --git a/src/dotnet/Lucene.Net.TestFramework.TestData.NUnit/Lucene.Net.TestFramework.TestData.NUnit.csproj b/src/dotnet/Lucene.Net.TestFramework.TestData.NUnit/Lucene.Net.TestFramework.TestData.NUnit.csproj
index 2b4ce04b9..9d9ca72fe 100644
--- a/src/dotnet/Lucene.Net.TestFramework.TestData.NUnit/Lucene.Net.TestFramework.TestData.NUnit.csproj
+++ b/src/dotnet/Lucene.Net.TestFramework.TestData.NUnit/Lucene.Net.TestFramework.TestData.NUnit.csproj
@@ -22,7 +22,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0;netstandard2.0;net462</TargetFrameworks>
+    <TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
 
     <AssemblyTitle>Lucene.Net.TestFramework.TestData.NUnit</AssemblyTitle>
     <RootNamespace>Lucene.Net</RootNamespace>
diff --git a/src/dotnet/Lucene.Net.Tests.CodeAnalysis/Lucene.Net.Tests.CodeAnalysis.csproj b/src/dotnet/Lucene.Net.Tests.CodeAnalysis/Lucene.Net.Tests.CodeAnalysis.csproj
index 2ede07dbe..aca1c34a7 100644
--- a/src/dotnet/Lucene.Net.Tests.CodeAnalysis/Lucene.Net.Tests.CodeAnalysis.csproj
+++ b/src/dotnet/Lucene.Net.Tests.CodeAnalysis/Lucene.Net.Tests.CodeAnalysis.csproj
@@ -22,11 +22,13 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>net9.0</TargetFramework>
+    <!-- We don't really care which target framework we test with - this is just to emulate Visual Studio 2017+ all which can load netstandard1.3 analyzers -->
+    <!-- Given that VS 2022 doesn't support net10.0 we will use net8.0. We will need to adjust this when we stop testing on net8.0. -->
+    <TargetFramework>net8.0</TargetFramework>
     <RootNamespace>Lucene.Net.CodeAnalysis</RootNamespace>
 
     <IsPublishable>false</IsPublishable>
-    <IsPublishable Condition=" '$(TargetFramework)' == 'net9.0' ">true</IsPublishable>
+    <IsPublishable Condition=" '$(TargetFramework)' == 'net8.0' ">true</IsPublishable>
     <IsTestProject>true</IsTestProject>
   </PropertyGroup>
 
diff --git a/src/dotnet/tools/Lucene.Net.Tests.Cli/Lucene.Net.Tests.Cli.csproj b/src/dotnet/tools/Lucene.Net.Tests.Cli/Lucene.Net.Tests.Cli.csproj
index 3ac1c1e2b..5292ceee5 100644
--- a/src/dotnet/tools/Lucene.Net.Tests.Cli/Lucene.Net.Tests.Cli.csproj
+++ b/src/dotnet/tools/Lucene.Net.Tests.Cli/Lucene.Net.Tests.Cli.csproj
@@ -25,7 +25,7 @@
 
   <PropertyGroup>
     <!-- Allow specific target framework to flow in from TestTargetFrameworks.props -->
-    <TargetFrameworks Condition=" '$(TargetFramework)' == '' ">net9.0;net8.0</TargetFrameworks>
+    <TargetFrameworks Condition=" '$(TargetFramework)' == '' ">net10.0;net9.0;net8.0</TargetFrameworks>
     <AssemblyTitle>Lucene.Net.Tests.Cli</AssemblyTitle>
   </PropertyGroup>
 
diff --git a/src/dotnet/tools/lucene-cli/lucene-cli.csproj b/src/dotnet/tools/lucene-cli/lucene-cli.csproj
index d3e4764a4..f9c89de72 100644
--- a/src/dotnet/tools/lucene-cli/lucene-cli.csproj
+++ b/src/dotnet/tools/lucene-cli/lucene-cli.csproj
@@ -24,11 +24,10 @@
   <Import Project="$(SolutionDir).build/nuget.props" />
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0</TargetFrameworks>
-    <RollForward Condition=" $(TargetFramework.StartsWith('net8.')) ">Major</RollForward>
+    <TargetFrameworks>$(NetCoreOnlyTargetFrameworks)</TargetFrameworks>
+    <RollForward>Major</RollForward>
 
-    <IsPublishable>false</IsPublishable>
-    <IsPublishable Condition="'$(TargetFramework)' == 'net8.0'">true</IsPublishable>
+    <IsPublishable Condition=" $(TargetFramework.StartsWith('net8.')) Or $(TargetFramework.StartsWith('net10.')) ">true</IsPublishable>
     <PackAsTool>true</PackAsTool>
     <ToolCommandName>lucene</ToolCommandName>