#
Using ReadyToRun with .NET Reactor
#
Overview
ReadyToRun (R2R) reduces startup latency by precompiling managed assemblies during publishing. Because ReadyToRun is generated from the managed IL, .NET Reactor must protect the assemblies before the ReadyToRun compiler processes them.
There are three ways to combine .NET Reactor with ReadyToRun:
#
Required Protection Settings
Required for all approaches
The following protection options must remain disabled when ReadyToRun is generated after protection:
- NecroBit
- Anti Tampering
- Inject Invalid Metadata, which is a sub-option of Anti ILDASM / Suppress Decompilation
These requirements apply to all three approaches described below.
#
Enable ReadyToRun
Enable ReadyToRun in the project file:
<PropertyGroup>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
Alternatively, enable it on the command line:
dotnet publish -c Release -r win-x64 -p:PublishReadyToRun=true
Replace win-x64 with the Runtime Identifier (RID) used by your deployment.
#
Approach 1: Protect After Compile
This is the simplest integrated approach. .NET Reactor protects the assembly in the intermediate output directory after compilation. The .NET SDK then uses the protected assembly as input for ReadyToRun.
#
Configuration
- Open Build/Development Tools → Build Integration Manager.
- Select your solution.
- Activate the required project configuration.
- Select the appropriate .NET Reactor project file.
- Set Protection Timing to After Compile.
- Click Save.
- Publish the application with
PublishReadyToRunenabled.
#
Limitation
Cross-assembly limitation
Each assembly is protected independently with this approach. Do not use it when public types or members must be renamed across multiple assemblies. Dependent assemblies would otherwise continue to reference the original names.
Use Before ReadyToRun (Cross-Assembly) when related assemblies must be protected together.
#
Approach 2: Protect Before ReadyToRun with Cross-Assembly Support
This approach protects the main assembly and all configured additional assemblies in a single .NET Reactor invocation immediately before the .NET SDK prepares its ReadyToRun inputs. It supports cross-assembly obfuscation, including public type renaming across assembly boundaries.
#
Prepare the .NET Reactor Project
Create a .NET Reactor project file that contains:
- The application entry assembly as the Main Assembly.
- Every related assembly that must participate in cross-assembly protection under Additional Assemblies.
The assembly file names must match the files produced or consumed by the publish operation. Absolute paths stored in the .NET Reactor project file are not used by this integration and do not need to match the current build directory.
Each configured additional assembly file name must be unique within the publish inputs.
#
Configure the Build Integration Manager
- Open Build/Development Tools → Build Integration Manager.
- Select your solution.
- Activate only the desired configuration of the main application project.
- Do not activate configurations belonging to the additional projects.
- Select the .NET Reactor project file containing the main and additional assemblies.
- Set Protection Timing to Before ReadyToRun (Cross-Assembly).
- Click Save.
- Publish the main project with
PublishReadyToRunenabled.
#
How Assemblies Are Resolved
For every additional assembly listed in the .NET Reactor project file, the generated MSBuild target resolves the input in this order:
- A matching
ProjectReferenceoutput. - A matching ReadyToRun publish input, such as a directly referenced or third-party assembly.
Matching is based on the assembly file name and extension and is case-insensitive.
If an assembly cannot be resolved from either source, the build fails. The build also fails when a configured name matches multiple inputs, because selecting one of them would be ambiguous.
#
Protection and Staging Process
The generated target performs the following steps:
- It creates a temporary resolved copy of the .NET Reactor project file.
- It copies every resolved additional assembly to the project's intermediate
Reactor/Inputdirectory. - It updates the temporary project file to reference these staged copies.
- It protects the main and additional assemblies together with assembly merging and embedding disabled.
- It replaces the corresponding ReadyToRun publish inputs with the protected outputs.
- The .NET SDK runs the ReadyToRun compiler over the protected assemblies.
External source assemblies are never modified. For example, if a third-party input is resolved from C:\Libraries\Vendor.dll, .NET Reactor processes a copy below the project's obj directory. C:\Libraries\Vendor.dll remains unchanged. Existing staged input files are overwritten with fresh source copies on every publish.
#
Common Errors
#
Assembly Could Not Be Resolved
The following .NET Reactor assemblies could not be resolved from ProjectReference or ReadyToRun publish inputs:
Verify that each named assembly:
- Is produced by a
ProjectReference, or - Is part of the main project's publish inputs, and
- Has the same file name and extension as the entry in the .NET Reactor project file.
#
Multiple Inputs Matched
One or more .NET Reactor assemblies matched multiple ProjectReference or ReadyToRun publish inputs.
Ensure that every configured additional assembly has a unique file name among the project's publish inputs.
#
No Additional Assemblies
The cross-assembly mode requires at least one entry under Additional Assemblies. For a project that only protects its main assembly, use After Compile instead.
#
Approach 3: Use a Custom Manual Pipeline
An advanced alternative is to manage the complete process in a custom build or CI pipeline:
- Publish or stage the application with ReadyToRun disabled.
- Protect the resulting managed assemblies with .NET Reactor.
- Protect all mutually dependent assemblies in one invocation if cross-assembly obfuscation is required.
- Run the ReadyToRun compiler over the protected assemblies.
- Assemble and validate the final deployment output.
Advanced pipeline requirements
This is not a resumable standard dotnet publish workflow. The custom pipeline must select the correct Crossgen2 tool and runtime pack for the target framework and RID, supply the complete reference closure, and preserve the SDK's expected publish metadata.
Use this approach only if you already maintain custom MSBuild or Crossgen2 infrastructure. For most applications, Before ReadyToRun (Cross-Assembly) provides the same required ordering with considerably less maintenance.
#
Verification and Troubleshooting
- Perform the first test with clean
objand publish directories. - Publish the application at least twice to verify that no previously protected intermediate file is reused as an input.
- Confirm that the original project-reference and third-party source assemblies remain unchanged.
- Test the complete published application, not only the main executable.
- If ReadyToRun is enabled only on the
dotnet publishcommand line, use the samePublishReadyToRunsetting during a separate restore when publishing with--no-restore. - If the .NET Reactor target does not run, verify the selected build configuration and confirm that
PublishReadyToRunevaluates totrue.
#
Choosing an Approach
- Use After Compile when the main assembly can be protected independently.
- Use Before ReadyToRun (Cross-Assembly) when public names may be changed across related assemblies.
- Use a custom manual pipeline only when the integrated workflows cannot satisfy a specialized build process.
#
Related topics
Single-file publishing · Verify protected output