
Property Patterns
- 9 installs
- 466 repo stars
- Updated July 25, 2026
- managedcode/dotnet-skills
Helps with ai & agent building tasks.
About
property-patterns is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- property-patterns
- AI & Agent Building
- AI-coding skill
Property Patterns by the numbers
- 9 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #12,133 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/managedcode/dotnet-skills --skill property-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 9 |
|---|---|
| repo stars | ★ 466 |
| Last updated | July 25, 2026 |
| Repository | managedcode/dotnet-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
MSBuild Property Patterns
Canonical property definition and manipulation patterns from the MSBuild repository.
Conditional Defaults — The Foundational Pattern
Set a property only if not already set, allowing callers to override:
<PropertyGroup>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">AnyCPU</Platform>
<BuildInParallel Condition="'$(BuildInParallel)' == ''">true</BuildInParallel>
</PropertyGroup>Rules
- Always quote both sides:
'$(Prop)' == '' - In
.props: creates overridable defaults. In.targets: creates fallbacks. - Properties without the condition cannot be overridden by earlier imports.
Nested Conditional Groups
Group related properties under a shared condition:
<PropertyGroup Condition="$(TargetFramework.StartsWith('net4'))">
<DefineConstants>$(DefineConstants);FEATURE_APARTMENT_STATE</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_APM</DefineConstants>
<FeatureAppDomain>true</FeatureAppDomain>
</PropertyGroup>
<PropertyGroup Condition="'$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)'))' == '.NETCoreApp'">
<NetCoreBuild>true</NetCoreBuild>
<DefineConstants>$(DefineConstants);RUNTIME_TYPE_NETCORE</DefineConstants>
</PropertyGroup>Use the outer Condition on PropertyGroup to avoid repeating the same condition on every property.
Warning:$(TargetFramework)is empty in.propsfiles for single-targeting projects until the project body is evaluated. PlaceTargetFramework-conditioned property groups in.targetsfiles (or the project file itself), where the value is always available.
Composition — Semicolon Concatenation
Properties that hold lists use semicolons. Always include the existing value when appending:
<PropertyGroup>
<DefineConstants>$(DefineConstants);MY_FEATURE</DefineConstants>
<NoWarn>$(NoWarn);NU5131;IDE0005</NoWarn>
<LibraryTargetFrameworks>$(FullFrameworkTFM);$(LatestDotNetCoreForMSBuild);netstandard2.0</LibraryTargetFrameworks>
</PropertyGroup>Path Normalization and Trailing Slashes
<!-- Ensure trailing slash on directories -->
<PropertyGroup>
<OutDir Condition="'$(OutDir)' != '' and !HasTrailingSlash('$(OutDir)')">$(OutDir)\</OutDir>
</PropertyGroup>
<!-- Normalize paths for cross-platform -->
<PropertyGroup>
<TargetRefPath>$([MSBuild]::NormalizePath('$(TargetDir)', 'ref', '$(TargetFileName)'))</TargetRefPath>
</PropertyGroup>
<!-- Make relative path absolute -->
<PropertyGroup>
<MSBuildProjectExtensionsPath
Condition="'$([System.IO.Path]::IsPathRooted('$(MSBuildProjectExtensionsPath)'))' == 'false'">
$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(MSBuildProjectExtensionsPath)'))
</MSBuildProjectExtensionsPath>
</PropertyGroup>Preferred path functions
| Function | Purpose |
|---|---|
$([MSBuild]::NormalizePath(...)) | Combine and normalize (cross-platform) |
$([System.IO.Path]::Combine(...)) | Combine path segments |
$([System.IO.Path]::IsPathRooted(...)) | Check if absolute |
HasTrailingSlash(...) | Check for trailing slash |
$([MSBuild]::GetDirectoryNameOfFileAbove(...)) | Walk up directory tree |
$(MSBuildThisFileDirectory) | Directory of current file |
Target Framework Detection Helpers
<!-- Get TFM identifier -->
<PropertyGroup Condition="'$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)'))' == '.NETCoreApp'">
<NetCoreBuild>true</NetCoreBuild>
</PropertyGroup>
<!-- Check TFM compatibility -->
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net472'))">
<UseFrozenVersions>true</UseFrozenVersions>
</PropertyGroup>
<!-- OS detection -->
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('windows'))">
<DefineConstants>$(DefineConstants);TEST_ISWINDOWS</DefineConstants>
</PropertyGroup>Guard Properties
Mark that a file has been imported to prevent double-imports:
<!-- At the end of MySDK.props -->
<PropertyGroup>
<MySDKPropsImported>true</MySDKPropsImported>
</PropertyGroup>
<!-- At the top of MySDK.targets -->
<Import Project="MySDK.props" Condition="'$(MySDKPropsImported)' != 'true'" />Feature Gating by MSBuild Version
<PropertyGroup Condition="$([MSBuild]::AreFeaturesEnabled('17.10'))">
<UseNewBehavior>true</UseNewBehavior>
</PropertyGroup>Fallback Chains
Set via primary source first, then fall back:
<PropertyGroup>
<TlbExpPath>$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPathToDotNetFrameworkSdkFile('tlbexp.exe'))</TlbExpPath>
<TlbExpPath Condition="'$(TlbExpPath)' == ''">$(_NetFxToolsDir)TlbExp.exe</TlbExpPath>
</PropertyGroup>Last Write Wins — Evaluation Order
MSBuild evaluates properties top-to-bottom. The last assignment wins:
<!-- File 1 (imported first) -->
<MyProp>value1</MyProp> <!-- set to value1 -->
<!-- File 2 (imported second) -->
<MyProp>value2</MyProp> <!-- overwritten to value2 -->
<!-- File 3 (imported third) -->
<MyProp Condition="'$(MyProp)' == ''">value3</MyProp> <!-- NOT set — already value2 -->Properties in .targets (imported late) override properties in .props (imported early) and the project file.
Common Pitfalls
- Unquoted conditions (
$(X)==true) fail when the property is empty. Always quote both sides. - Overwriting DefineConstants (
<DefineConstants>MY_CONST</DefineConstants>) drops all prior constants. Always append with$(DefineConstants);. - Hardcoded absolute paths break portability. Use
$(MSBuildThisFileDirectory)or$([MSBuild]::NormalizePath(...)). - Missing `Condition` on defaults makes properties non-overridable. Add
Condition="'$(Prop)' == ''"for values meant to be defaults.
{
"version": "0.1.0",
"category": "Core",
"compatibility": "Requires a .NET repository with MSBuild project or solution files."
}