When a .NET solution contains many projects, build performance depends on more than compiling C# code. Before compilation begins, MSBuild evaluates project files, imports configuration, resolves properties, and determines which files belong to each project.
Wildcard expressions are a common part of this process. They allow developers to include files from directories without listing every filename individually. However, matching files across large directory trees can add work during project evaluation.
.NET 11 RC 2 introduces an improvement to MSBuild wildcard matching that can reduce the overhead associated with this operation. The change is particularly relevant to repositories containing many source files, generated files, resources, or projects that rely heavily on wildcard-based item definitions.
In this article, we will examine how MSBuild wildcards work, explain where the optimization can help, and explore practical ways to evaluate build performance after upgrading to .NET 11 RC 2.
What Is MSBuild Wildcard Matching?
MSBuild is the build engine used by .NET projects. It reads project files, evaluates build properties, identifies project items, and coordinates tasks such as compilation, resource generation, and publishing.
A wildcard expression allows a project file to match multiple files using a pattern.
For example, the following project-file entry includes C# files under the Services directory and its subdirectories:
<ItemGroup>
<Compile Include="Services/**/*.cs" />
</ItemGroup>
The ** pattern matches files in nested directories, while *.cs matches filenames ending in the C# source-file extension.
This is useful when a project contains many files because developers do not need to update the project file every time a new source file is added.
However, MSBuild must determine which files match the expression. In a large repository, that work can involve examining substantial directory structures.
What's New in .NET 11 RC 2?
.NET 11 RC 2 includes an optimization to MSBuild wildcard matching. The purpose is to make the matching process more efficient during project evaluation.
This matters because project evaluation can occur repeatedly throughout a development workflow. IDEs may evaluate projects while loading a solution, and command-line builds may evaluate several projects before compilation begins.
The optimization targets wildcard processing rather than changing the meaning of existing wildcard expressions.
Developers should not assume that every build will become noticeably faster. The effect depends on how much time the project spends evaluating wildcard patterns and how much time is consumed by other work, such as compilation, analyzers, code generation, and external tools.
Why Wildcard Matching Matters in Large Solutions
Consider an enterprise solution containing dozens of projects. Several projects may include source files, configuration files, templates, and static assets using wildcard expressions.
A project might use patterns such as:
<ItemGroup>
<Content Include="Templates/**/*.json" />
<EmbeddedResource Include="Resources/**/*.resx" />
<None Include="Configuration/**/*.yaml" />
</ItemGroup>
Each expression identifies files that should be included in the project with a particular item type.
As the repository grows, these patterns can cover more directories and files. Repeated project evaluation can make the cost of discovering matching files more noticeable.
The important point is that wildcard matching is only one part of the build process. A project with expensive source generators or a large compilation workload may see little overall improvement even if wildcard evaluation becomes faster.
How to Evaluate the Improvement
The most reliable way to assess the optimization is to compare equivalent builds before and after upgrading the SDK.
Step 1: Identify Projects That Use Wildcards
Search the relevant project files for expressions such as:
<Compile Include="**/*.cs" />
<Content Include="wwwroot/**/*" />
<EmbeddedResource Include="Resources/**/*.resx" />
These examples illustrate common wildcard patterns. Modern SDK-style .NET projects already include default item patterns for many source files and resources, so do not add these expressions unnecessarily.
Check whether the project explicitly includes files that the SDK already includes by default. Duplicate item definitions can create warnings or other unexpected behavior.
Step 2: Install and Select the RC 2 SDK
Install the .NET 11 RC 2 SDK in an isolated development environment or build agent.
Confirm the SDK version:
dotnet --info
dotnet --list-sdks
If the repository contains a global.json file, inspect it to determine whether it selects a particular SDK version.
Without checking SDK selection, a comparison may accidentally use the same SDK for both builds.
Step 3: Capture a Baseline
Before changing the SDK, run a representative build and record its duration.
For example:
dotnet build MySolution.sln --configuration Release
Use the same solution, configuration, machine, and relevant build settings for each comparison.
A clean build and an incremental build measure different workloads. Record them separately rather than comparing a clean build under one SDK with an incremental build under another.
Step 4: Repeat the Build With .NET 11 RC 2
After selecting the new SDK, run the equivalent command again:
dotnet build MySolution.sln --configuration Release
Compare the results across several runs where practical. Environmental factors such as filesystem caching, antivirus scanning, background processes, and package restoration can affect build times.
For a more detailed investigation, capture an MSBuild diagnostic log:
dotnet build MySolution.sln \
--configuration Release \
--verbosity diagnostic \
-bl:build.binlog
The binary log can help identify expensive project evaluation and build tasks. Because diagnostic logs may contain paths, properties, and environment details, review them before sharing them outside your organization.
Step 5: Investigate the Actual Bottleneck
If build time does not improve, inspect the build log to determine where time is spent.
The project may be dominated by C# compilation, source generators, analyzers, package restoration, or custom build targets rather than wildcard matching.
Optimizing an unrelated part of the build will not address those bottlenecks.
Wildcard Patterns and Explicit File Lists
Both wildcard expressions and explicit file lists can be appropriate, depending on the project.
Approach | Advantages | Disadvantages |
|---|
Wildcard expressions | Less manual maintenance; new matching files can be included automatically | May require more file discovery during evaluation |
Explicit file lists | Makes inclusion decisions visible in the project file | Requires maintenance as files are added or removed |
SDK default items | Reduces boilerplate for common project structures | May require explicit exclusions for unusual layouts |
For most SDK-style .NET projects, the default item inclusion rules are a good starting point.
Replacing every wildcard with an explicit file list simply to improve performance is usually unnecessary. It can also increase maintenance work and introduce errors when files are added or removed.
Instead, profile the build first and change project structure only when the evidence supports doing so.
Best Practices for Faster MSBuild Projects
1. Keep Wildcard Patterns Focused
Avoid patterns that search directories unrelated to the project.
For example, a project should not scan a repository-wide directory tree when it only needs files from its own resource directory.
Narrow patterns make project intent clearer and can reduce unnecessary file discovery.
2. Avoid Duplicate Default Items
SDK-style projects automatically include many common file types. Adding another broad Compile Include or Content Include pattern can duplicate existing items.
Use exclusions or targeted item definitions when customization is necessary.
3. Separate Generated Files From Source Files
Generated files may be numerous and change frequently. Keep generated output in an appropriate directory and avoid including it through broad patterns that unintentionally capture intermediate build artifacts.
The correct approach depends on the generator and project type, but source files and generated output should have clearly defined ownership.
4. Measure Before Refactoring Project Files
Record build times and inspect diagnostic logs before rewriting wildcard expressions.
If wildcard matching is not a significant part of the workload, changing project files may provide little benefit while increasing maintenance complexity.
5. Keep CI Environments Consistent
Use an explicitly selected SDK version for controlled build comparisons. Ensure that CI agents use the intended SDK and that build configuration remains consistent across runs.
This makes it easier to distinguish the effects of an SDK update from unrelated environment changes.
Advantages and Disadvantages
Advantages
Potentially faster project evaluation: The optimization targets wildcard matching during MSBuild evaluation.
Less need for manual project maintenance: Existing wildcard-based project files can retain their current structure.
Benefits for larger repositories: Projects with extensive wildcard matching have a reason to evaluate the updated build engine.
Improved development workflow: Faster project evaluation can benefit command-line builds and other workflows that repeatedly evaluate projects.
Disadvantages
Workload-dependent results: Projects that spend little time matching wildcards may see minimal improvement.
No guaranteed build-time reduction: Compilation, analyzers, and external tools may dominate the total build duration.
Benchmarking requires care: Cache state, background processes, and build configuration can distort comparisons.
Project configuration still matters: Inefficient or overly broad wildcard patterns can remain problematic even with an improved matching implementation.
Common Mistakes and Troubleshooting
The Build Is Not Faster After Upgrading
Check whether wildcard matching is actually a significant part of project evaluation. Inspect diagnostic logs and compare equivalent clean and incremental builds.
Do not conclude that the optimization is ineffective based on a single run.
New Duplicate-Item Warnings Appear
Inspect explicit wildcard expressions and compare them with the SDK's default item inclusion rules. Remove unnecessary duplicate inclusions or use targeted exclusions.
The Expected SDK Is Not Being Used
Check dotnet --info, the installed SDK list, and any repository-level global.json configuration.
Ensure that the shell, IDE, and CI agent select the intended SDK.
The Binary Log Contains Sensitive Information
Build logs can expose internal directory structures and configuration details. Store them securely and inspect them before sharing them with external support teams.
Summary
.NET 11 RC 2 improves MSBuild wildcard matching, targeting part of the project evaluation process that can become expensive in large solutions.
The change is relevant to projects that use wildcard expressions to include source files, resources, content, and other project items. However, the overall performance benefit depends on how much time the build spends matching files compared with compilation and other build operations.
Developers should begin by identifying wildcard-heavy projects, capturing a baseline, and comparing equivalent builds under the new SDK. Keep existing project files unless profiling identifies a specific problem, and avoid making broad configuration changes based on assumptions.
With consistent benchmarking and focused project configuration, teams can determine whether the MSBuild improvement benefits their build workflow without adding unnecessary complexity.
Join the conversation! Your thoughts help the community grow.