aspnet-core
Build, debug, modernize, or review ASP.NET Core applications with correct hosting, middleware, security, configuration, logging,…
DO NOT INVOKE when the primary request explicitly asks to convert, migrate, modernize, or rewrite a legacy/old-style project to SDK style; use msbuild-modernization. Migration prompts often mention ToolsVersion, explicit Compile/Reference entries, packages.config, or
Claude Code
$ npx -y skills add managedcode/dotnet-skills --skill msbuild-antipatterns --agent claude-code
Codex
$ npx skills add managedcode/dotnet-skills --skill msbuild-antipatterns -a codex -y
Cursor
$ npx skills add managedcode/dotnet-skills --skill msbuild-antipatterns -a cursor -y
Gemini CLI
$ npx skills add managedcode/dotnet-skills --skill msbuild-antipatterns -a gemini-cli -y
GitHub Copilot
$ npx skills add managedcode/dotnet-skills --skill msbuild-antipatterns -a github-copilot -y
OpenCode
$ npx skills add managedcode/dotnet-skills --skill msbuild-antipatterns -a opencode -y
Windsurf
$ npx skills add managedcode/dotnet-skills --skill msbuild-antipatterns -a windsurf -y
name: msbuild-antipatterns description: "DO NOT INVOKE when the primary request explicitly asks to convert, migrate, modernize, or rewrite a legacy/old-style project to SDK style; use msbuild-modernization. Migration prompts often mention ToolsVersion, explicit Compile/Reference entries, packages.config, or Microsoft.CSharp.targets, but merely reviewing or auditing a file that contains those patterns remains in scope here. USE FOR broad review, audit, lint, or maintainability/correctness checks of project/build files, including custom targets; prioritized cross-cutting findings; discrete anti-patterns; F# ordering/FS0039; cross-platform paths; and focused Import safety verdicts. Review/audit is analysis-only unless fixes are requested. For concrete property/item defects use property-patterns/item-management; use target-authoring for implementation and extension-points for NuGet auto-import/layout discovery. Exclude non-MSBuild systems." license: MIT
A numbered catalog of common MSBuild anti-patterns. Each entry follows the format:
Use this catalog when scanning project files for improvements.
For review, audit, maintainability, or correctness-risk requests:
1. Do not edit files unless the user explicitly asks for fixes. 2. Return prioritized findings grounded in the supplied file, with impact and a targeted recommendation. 3. Separate confirmed defects from conditional risks and already-correct patterns. 4. Keep the final answer as the review; do not replace it with a terse summary of changes.
---
**Smell**: `<Exec Command="mkdir ..." />`, `<Exec Command="copy ..." />`, `<Exec Command="del ..." />`
**Why it's bad**: Built-in tasks are cross-platform, support incremental build, emit structured logging, and handle errors consistently. `<Exec>` is opaque to MSBuild.
<!-- BAD --> <Target Name="PrepareOutput"> <Exec Command="mkdir $(OutputPath)logs" /> <Exec Command="copy config.json $(OutputPath)" /> <Exec Command="del $(IntermediateOutputPath)*.tmp" /> </Target> <!-- GOOD --> <Target Name="PrepareOutput"> <MakeDir Directories="$(OutputPath)logs" /> <Copy SourceFiles="config.json" DestinationFolder="$(OutputPath)" /> <Delete Files="@(TempFiles)" /> </Target>
**Built-in task alternatives:**
| Shell Command | MSBuild Task | |--------------|--------------| | `mkdir` | `<MakeDir>` | | `copy` / `cp` | `<Copy>` | | `del` / `rm` | `<Delete>` | | `move` / `mv` | `<Move>` | | `echo text > file` | `<WriteLinesToFile>` | | `touch` | `<Touch>` | | `xcopy /s` | `<Copy>` with item globs |
---
**Smell**: `Condition="$(Foo) == Bar"` — either side of a comparison is unquoted.
**Why it's bad**: If the property is empty or contains spaces/special characters, the condition evaluates incorrectly or throws a parse error. MSBuild requires single-quoted strings for reliable comparisons.
<!-- BAD --> <PropertyGroup Condition="$(Configuration) == Release"> <Optimize>true</Optimize> </PropertyGroup> <!-- GOOD --> <PropertyGroup Condition="'$(Configuration)' == 'Release'"> <Optimize>true</Optimize> </PropertyGroup>
**Rule**: Always quote **both** sides of `==` and `!=` comparisons with single quotes.
---
**Smell**: Paths like `C:\tools\`, `D:\packages\`, `/usr/local/bin/` in project files.
**Why it's bad**: Breaks on other machines, CI environments, and other operating systems. Not relocatable.
<!-- BAD --> <PropertyGroup> <ToolPath>C:\tools\mytool\mytool.exe</ToolPath> </PropertyGroup> <Import Project="C:\repos\shared\common.props" /> <!-- GOOD --> <PropertyGroup> <ToolPath>$(MSBuildThisFileDirectory)tools\mytool\mytool.exe</ToolPath> </PropertyGroup> <Import Project="$(RepoRoot)eng\common.props" />
**Preferred path properties:**
| Property | Meaning | |----------|---------| | `$(MSBuildThisFileDirectory)` | Directory of the current .props/.targets file | | `$(MSBuildProjectDirectory)` | Directory of the .csproj | | `$([MSBuild]::GetDirectoryNameOfFileAbove(...))` | Walk up to find a marker file | | `$([MSBuild]::NormalizePath(...))` | Combine and normalize path segments |
---
**Smell**: Properties set to values that the .NET SDK already provides by default.
**Why it's bad**: Adds noise, hides intentional overrides, and makes it harder to identify what's actually customized. When defaults change in newer SDKs, the redundant properties may silently pin old behavior.
<!-- BAD: All of these are already the default --> <PropertyGroup> <OutputType>Library</OutputType> <EnableDefaultItems>true</EnableDefaultItems> <EnableDefaultCompileItems>true</EnableDefaultCompileItems> <RootNamespace>MyLib</RootNamespace> <!-- matches project name --> <AssemblyName>MyLib</AssemblyName> <!-- matches project name --> <AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath> </PropertyGroup> <!-- GOOD: Only non-default values --> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> </PropertyGroup>
---
**Smell**: `<Compile Include="File1.cs" />`, `<Compile Include="File2.cs" />` in SDK-style projects.
**Why it's bad**: SDK-style projects automatically glob `**/*.cs` (and other file types). Explicit listing is redundant, creates merge conflicts, and new files may be accidentally missed if not added to the list.
<!-- BAD -->
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Services\MyService.cs" />
<Compile Include="Models\User.cs" />
</ItemGroup>
<!-- GOOD: Remove entirely — SDK includes all .cs files by default.
Only use Remove/Exclude when yStop explaining .NET to your AI. Start building. We've all been there: asking Claude to use Entity Framework, only to get EF6 patterns in a .NET 8 project. Explaining to Copilot that Blazor Server and Blazor WebAssembly aren't the same thing.
Build, debug, modernize, or review ASP.NET Core applications with correct hosting, middleware, security, configuration, logging,…
Build, upgrade, and operate Aspire 13.5.x C# or TypeScript application hosts with the current CLI, AppHost, ServiceDefaults,…
Build and debug Avalonia 11/12 .NET applications with XAML, compiled bindings, MVVM, custom controls, desktop lifetimes, and…
Build, review, or migrate Azure Functions in .NET with correct execution model, isolated worker setup, bindings, DI, and Durable…
Build and review Blazor applications across server, WebAssembly, web app, and hybrid scenarios with correct component design,…
Maintain or migrate EF6-based applications with realistic guidance on what to keep, what to modernize, and when EF Core is or is…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.