# 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:

Approach Cross-assembly obfuscation Integration Recommended use
After Compile No Build Integration Manager A single assembly, or multiple assemblies that do not require public type renaming across assembly boundaries
Before ReadyToRun (Cross-Assembly) Yes Build Integration Manager Applications whose related assemblies must be protected together
Manual publish, protect, and ReadyToRun pipeline Yes Custom build pipeline Advanced scenarios that require complete control over every build stage

# Required Protection Settings

# 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

  1. Open Build/Development Tools → Build Integration Manager.
  2. Select your solution.
  3. Activate the required project configuration.
  4. Select the appropriate .NET Reactor project file.
  5. Set Protection Timing to After Compile.
  6. Click Save.
  7. Publish the application with PublishReadyToRun enabled.

# Limitation

# 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

  1. Open Build/Development Tools → Build Integration Manager.
  2. Select your solution.
  3. Activate only the desired configuration of the main application project.
  4. Do not activate configurations belonging to the additional projects.
  5. Select the .NET Reactor project file containing the main and additional assemblies.
  6. Set Protection Timing to Before ReadyToRun (Cross-Assembly).
  7. Click Save.
  8. Publish the main project with PublishReadyToRun enabled.

# 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:

  1. A matching ProjectReference output.
  2. 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:

  1. It creates a temporary resolved copy of the .NET Reactor project file.
  2. It copies every resolved additional assembly to the project's intermediate Reactor/Input directory.
  3. It updates the temporary project file to reference these staged copies.
  4. It protects the main and additional assemblies together with assembly merging and embedding disabled.
  5. It replaces the corresponding ReadyToRun publish inputs with the protected outputs.
  6. 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:

  1. Publish or stage the application with ReadyToRun disabled.
  2. Protect the resulting managed assemblies with .NET Reactor.
  3. Protect all mutually dependent assemblies in one invocation if cross-assembly obfuscation is required.
  4. Run the ReadyToRun compiler over the protected assemblies.
  5. Assemble and validate the final deployment output.

# Verification and Troubleshooting

  • Perform the first test with clean obj and 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 publish command line, use the same PublishReadyToRun setting 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 PublishReadyToRun evaluates to true.

# 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

Publishing · Debugging