Purview.DotNetProjectSdk 1.0.0-prerelease.45

This is a prerelease version of Purview.DotNetProjectSdk.
There is a newer prerelease version of this package available.
See the version list below for details.
<Sdk Name="Purview.DotNetProjectSdk" Version="1.0.0-prerelease.45" />
                    
For projects that support Sdk, copy this XML node into the project file to reference the package.
#:sdk Purview.DotNetProjectSdk@1.0.0-prerelease.45
                    
#:sdk directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.

Purview.DotNetProjectSdk

A reusable MSBuild SDK NuGet package that delivers standardised .NET project defaults, code-style enforcement, test-framework wiring, and Central Package Management integration. Install it once per repo — every project beneath the repo root inherits everything automatically.

This SDK package imposes convention over configuration, enforcing certain styles and automations based on project file names, etc.

What's included

Feature Detail
Project type detection IsCSharpProject, IsTestProject, IsSharedTestingProject, IsContainerProject, IsWebSdkProject, IsAspireHostProject, …
C# defaults net10.0 TFM (overridable), LangVersion=preview, Nullable=enable, ImplicitUsings=enable, deterministic builds
Code style .editorconfig baked into the package, applied via EditorConfigFilePath, and auto-bootstrapped to repo root if missing; EnforceCodeStyleInBuild=true, EnableNETAnalyzers=true, AnalysisLevel=latest, AnalysisMode=All
NuGet packaging AssemblyName/PackageId default to the fully evaluated RootNamespace; packable projects get GenerateDocumentationFile=true, PublishRepositoryUrl=true, IncludeSymbols=true, SymbolPackageFormat=snupkg, EmbedUntrackedSources=true, and portable PDBs delivered via .snupkg (not the .nupkg)
Repo bootstrap Missing repo-root .editorconfig and global.json are auto-copied/created by default (disable via DisableAutoCopySdkFiles=true)
CI detection ContinuousIntegrationBuild set automatically when CI, GITHUB_ACTIONS, or TF_BUILD env vars are present
SourceLink Microsoft.SourceLink.GitHub added to all packable projects (configurable via SourceLinkPackageName)
Purview Telemetry Purview.Telemetry.SourceGenerator + Microsoft.Extensions.Telemetry.Abstractions added by default (opt-out)
Assembly info Auto-generated static partial class AssemblyInfo with RootNamespace, Version, Company, etc., plus an embedded Microsoft.CodeAnalysis.EmbeddedAttribute (can be excluded via PURVIEW_SDK_EXCLUDE_EMBEDDED).
InternalsVisibleTo Generated for all TestType variants and shared testing projects, using the resolved $(AssemblyName) so explicit, generated, and default naming are all handled
Namespace management NamespacePrefix.ProjectName pattern with suffix stripping (.Core, .Shared, .EF, …)
Testing framework TestingFramework: TUnit (default), Xunit, or None
Mocking provider SubstituteFramework: TUnitMocks (default), NSubstitute, or None
Test data provider TestDataFramework: Bogus (default) or None
Version detection Reads version from package.json and applies it to Version and PackageVersion automatically; falls back to 0.0.1
CPM ManagePackageVersionsCentrally=true — versions live in your Directory.Packages.props

Quick start

1. Add the SDK to global.json

{
  "test": {
    "runner": "Microsoft.Testing.Platform"
  },
  "msbuild-sdks": {
    "Purview.DotNetProjectSdk": "1.0.0"
  }
}

2. Create Directory.Build.props at repo root

<Project>
  <PropertyGroup>
    
    <NamespacePrefix>YourCompany</NamespacePrefix>
  </PropertyGroup>

  <Import Sdk="Purview.DotNetProjectSdk" Project="Sdk.props" />
</Project>

3. Create Directory.Build.targets at repo root

<Project>
  <Import Sdk="Purview.DotNetProjectSdk" Project="Sdk.targets" />
</Project>

4. Copy Directory.Packages.props to repo root

Copy templates/Directory.Packages.props from this package to your repo root. All package versions default to * (latest at restore). Pin any package by replacing * with a specific version.

Note: ManagePackageVersionsCentrally=true is set by the SDK. You must have a Directory.Packages.props at your repo root for CPM to work, even if it only contains the packages the SDK adds automatically.


Project naming guide

The SDK applies several conventions automatically based on the .csproj filename and NamespacePrefix.

Defaults (no extra configuration)

RootNamespace is always derived from $(NamespacePrefix).$(ProjectName) and is the canonical default public name. By default (EnableAssemblyNameGeneration=true), AssemblyName and PackageId both follow the fully evaluated RootNamespace. Test projects retain their detected suffix so test assemblies stay distinct from the source assembly. Set EnableAssemblyNameGeneration=false (before the SDK import) to opt out and use standard .NET behaviour (the .csproj filename):

.csproj filename AssemblyName / PackageId RootNamespace Detected as
Api.csproj Acme.Api Acme.Api Source project
Api.UnitTests.csproj Acme.Api.UnitTests Acme.Api IsTestProject=true, TestingType=Unit
Api.IntegrationTests.csproj Acme.Api.IntegrationTests Acme.Api IsTestProject=true, TestingType=Integration
SharedTestingFramework.csproj Acme.SharedTestingFramework Acme IsSharedTestingProject=true

Note: InternalsVisibleTo follows $(AssemblyName) — so for Api.csproj the SDK generates Acme.Api.UnitTests, Acme.Api.IntegrationTests, etc.

Use short .csproj names — the SDK handles the prefixing:

✅  Api.csproj                        → short name, SDK resolves the rest
❌  Acme.Api.csproj                   → redundant prefix, avoid

A build-time check (PurviewProjectFileNameMismatch) enforces that the .csproj filename matches its parent directory name, preventing inconsistent naming. Set DisableProjectFileNamingConventionCheck=true to opt out.

For larger repos, separate source and test projects into src/ and tests/ folders:

MyRepo/
├── Directory.Build.props          ← NamespacePrefix=Acme
├── Directory.Build.targets
├── Directory.Packages.props
├── global.json
├── src/
│   ├── Api/
│   │   └── Api.csproj
│   ├── Core/
│   │   └── Core.csproj
│   └── SourceGenerator/
│       └── SourceGenerator.csproj
├── tests/
│   ├── Api.UnitTests/
│   │   └── Api.UnitTests.csproj    → IsTestProject=true, TestingType=Unit
│   ├── Api.IntegrationTests/
│   │   └── Api.IntegrationTests.csproj
│   └── SharedTestingFramework/
│       └── SharedTestingFramework.csproj  → IsSharedTestingProject=true
└── package.json

Flat structure: everything together

For smaller repos, source and test projects can live side-by-side:

MyRepo/
├── Directory.Build.props
├── Directory.Build.targets
├── Directory.Packages.props
├── global.json
├── Api/
│   └── Api.csproj
├── Api.UnitTests/
│   └── Api.UnitTests.csproj
├── Core/
│   └── Core.csproj
├── Core.IntegrationTests/
│   └── Core.IntegrationTests.csproj
└── package.json

Both layouts work identically — the SDK detects test projects by name suffix, not folder location.

Quick reference

# Create a source project
mkdir src/Api && cd src/Api
dotnet new classlib -n Api

# Create its unit tests
mkdir ../../tests/Api.UnitTests && cd ../../tests/Api.UnitTests
dotnet new classlib -n Api.UnitTests   # SDK wires TUnit automatically

# Or flat:
mkdir Api.UnitTests && cd Api.UnitTests
dotnet new classlib -n Api.UnitTests

Template files

The templates/ folder contains ready-to-copy starter files for new repos:

File Purpose
Directory.Build.props Bootstrapper — copy to repo root and set NamespacePrefix
Directory.Build.targets Bootstrapper — copy to repo root
Directory.Packages.props All default package versions with * floating to latest
global.json msbuild-sdks entry + Microsoft.Testing.Platform test runner
.gitignore ASP.NET Core + VS + Rider + Node combined gitignore
.gitattributes Line-ending normalisation for .cs, .json, .yml, etc.
.config/dotnet-tools.json CSharpier tool manifest

The package also ships bundled agent content under .agents/**. During build, the SDK copies it into the consuming repository's .agents/ folder by default so compatible coding agents can discover repository-aware guidance automatically. The SDK also injects a .gitignore file into each second-level agent folder with the content # Ignore all files\n*\n# Don't ignore directories, so Git can traverse them\n!*/\n# Keep this file\n!.gitignore, so the copied folder is ignored by Git while keeping the folder structure discoverable.


Configuration reference

Set any of these properties before the <Import> in your Directory.Build.props:

Version detection

Property Default Description
UsePackageJsonVersion true true enables version detection, false disables it, and Strict requires version detection to succeed (build fails if no version source can be resolved).
RootPackageJson (auto-discovered) Explicit path to a package.json. Relative paths are resolved from the project directory.
EnableVersionDetectionCache true Enables local caching of auto-discovered package.json version results.
VersionDetectionLogEnabled false Emits a high-importance message showing the detected package version. Set to true to enable logging.

When UsePackageJsonVersion=true (the default) or UsePackageJsonVersion=Strict, the SDK:

  1. Explicit path — if RootPackageJson is set, reads that file directly.
  2. Auto-discovery — otherwise, walks up from the project directory looking for a .git marker to locate the repo root, then reads package.json from there.

The extracted version field is applied to both Version and PackageVersion. A build error is raised if the file can't be found or contains no version field. With UsePackageJsonVersion=Strict, the build also fails when no package.json source can be discovered (for example, no explicit RootPackageJson and no discoverable .git marker).

Version detection logging is disabled by default. Set VersionDetectionLogEnabled to true to emit a high-importance message showing the detected package version.

Important — set before the import: Both UsePackageJsonVersion and RootPackageJson must be set before the <Import Sdk="Purview.DotNetProjectSdk" Project="Sdk.props" /> line in your Directory.Build.props. The version logic runs during that import and cannot see properties set afterwards (e.g. in individual .csproj files).

<Project>
  <PropertyGroup>
    <NamespacePrefix>Acme</NamespacePrefix>
    
    <RootPackageJson>$(MSBuildThisFileDirectory)package.json</RootPackageJson>
  </PropertyGroup>

  <Import Sdk="Purview.DotNetProjectSdk" Project="Sdk.props" />
</Project>

General

Property Default Description
NamespacePrefix (required) Root namespace prefix, e.g. Acme. Results in Acme.MyProject.
DisableNamespacePrefixCheck false Set to true to suppress the build error for missing NamespacePrefix.
TargetFramework net10.0 Override the default TFM per-project or globally. Defaults to netstandard2.0 for projects declaring IsRoslynComponent=true.
IsRoslynComponent false When explicitly true, applies source-generator defaults: a single netstandard2.0 target, LangVersion=latest, Nullable=enable, TreatWarningsAsErrors=true, extended analyzer rules, SourceLink, generated-file output, dependency output, symbol packaging (IncludeSymbols=false by default), telemetry exclusion, and package build output. Packable Roslyn components automatically pack the built analyzer assembly (and its PDB) into analyzers/dotnet/cs/. Roslyn development dependencies (Microsoft.CodeAnalysis.*, Microsoft.CodeAnalysis.Analyzers) default to PrivateAssets="all".
PackProjectReferencedSourceGenerators true Automatically packs analyzer ProjectReference outputs and their runtime dependencies under analyzers/dotnet/cs/. Set to false to opt out; set Pack="false" on an individual reference to exclude only that generator.
SourceLinkPackageName Microsoft.SourceLink.GitHub SourceLink provider. Set to Microsoft.SourceLink.AzureDevOps.Git for ADO repos.
DisableSourceLink false Set to true to stop the SDK from adding the configured SourceLink package automatically.
EnableAssemblyNameGeneration true When true (default), AssemblyName and PackageId derive from the fully evaluated RootNamespace. When explicitly false, standard .NET behaviour applies ($(MSBuildProjectName)). Explicit <AssemblyName>/<PackageId> in a .csproj always take precedence.
DisableProjectFileNamingConventionCheck false Set to true to disable the validation that requires MyProject\MyProject.csproj naming alignment.
DisableGenerateAssemblyInfoClass false Set to true to disable the generated AssemblyInfo helper source.
AutoIncludeUsings true Controls SDK-added global usings for NamespacePrefix and RootNamespace.

Packable project defaults

For projects where IsPackable=true, the SDK provides these defaults only when the consuming project has not supplied a value — explicit values are always preserved:

Property Default Description
GenerateDocumentationFile true Emits XML documentation.
IncludeSymbols true Produces a symbol package (false for Roslyn components — their PDB ships in analyzers/dotnet/cs/ instead).
SymbolPackageFormat snupkg Symbol package format. Always the modern .snupkg; the legacy .symbols.nupkg is never produced by default.
PublishRepositoryUrl true Publishes the repository URL.
EmbedUntrackedSources true Embeds untracked sources for SourceLink.
DebugType portable Ensures portable PDBs for symbol-package delivery.
IncludeSource true Includes source files in the package.

Portable PDBs are delivered through the .snupkg; the normal .nupkg does not receive PDB files unless the project explicitly opts in (for example by adding .pdb to AllowedOutputExtensionsInPackageBuildOutputFolder).

Repository README auto-inclusion: when the repo root is discoverable (.git marker or CI workspace variable), the repository-root README.md is packed automatically for packable projects and registered via PackageReadmeFile — but only when the file exists and PackageReadmeFile has not been configured explicitly. The SDK skips the auto-inclusion if a README-named file is already being packed, so no duplicate readme items are produced. No README is required; if the file is absent the pack succeeds without readme metadata.

The SDK never forces organization/package-specific metadata — Authors, Company, PackageLicenseExpression, PackageLicenseFile, Description, PackageTags, PackageProjectUrl, and repository URLs are left to the repository or individual package. IsPackable is not set blindly: it defaults to false and only becomes true when a project explicitly opts in.

Non-packable projects (including web applications) default WarnOnPackingNonPackableProject=false, so solution-wide pack operations skip them silently. Set <WarnOnPackingNonPackableProject>true</WarnOnPackingNonPackableProject> explicitly to re-enable the "cannot be packed" warning.

Repo bootstrap

Property Default Description
DisableAutoCopySdkFiles false Master switch that disables repo-level SDK file bootstrapping.
BootstrapEditorConfigToRepoRoot true Copies the SDK .editorconfig to the repository root when missing.
RepositoryEditorConfigFilePath (auto-detected) Override the destination path for the bootstrapped .editorconfig.
BootstrapGlobalJsonToRepoRoot true Creates a global.json at the repository root when missing.
RepositoryGlobalJsonFilePath (auto-detected) Override the destination path for the bootstrapped global.json.
PurviewDotNetProjectSdkVersionForGlobalJson (auto-detected or 1.0.0 fallback) Version written to the msbuild-sdks.Purview.DotNetProjectSdk entry in a bootstrapped global.json.

Agent folder

Property Default Description
PurviewAutoSdkPack true When true, automatically packs the Sdk/ folder contents into the NuGet package with the correct root-level paths. Disable this for MSBuild SDK projects.
EnableAgentFolderInPackage true Copies the bundled .agents/** folder from the SDK NuGet package into the consuming repository's .agents/ folder (or $(AgentPackDestinationFolder)/) before build.
AgentPackDestinationFolder .agents Repo-relative destination folder that receives the copied agent folder contents when EnableAgentFolderInPackage is true.

To disable bundled agent folder copying in a consuming repo, set the opt-out property before importing the SDK:

<PropertyGroup>
  <EnableAgentFolderInPackage>false</EnableAgentFolderInPackage>
</PropertyGroup>

When a project is packable, the SDK treats any content under Sdk/ as a pack target with these rules:

Source path Package path
Sdk/.agents/** .agents/**
Sdk/.github/** .github/**
Sdk/build/** build/**
Sdk/buildTransitive/** buildTransitive/**
Sdk/buildMultiTargeting/** buildMultiTargeting/**
Sdk/*.md, Sdk/*.png, Sdk/*.jpg, etc. package root
everything else under Sdk/ Sdk/

The SDK automatically adds a .gitignore file into each second-level folder under Sdk/.agents with the content # Ignore all files\n*\n# Don't ignore directories, so Git can traverse them\n!*/\n# Keep this file\n!.gitignore. This ensures the copied folder structure remains discoverable in consuming repositories while the content itself is ignored by Git.

Telemetry

Property Default Description
ExcludePurviewTelemetry false Set to true to exclude Purview.Telemetry.SourceGenerator from all projects.
ExcludeMSTelemetryExtension false Set to true to exclude Microsoft.Extensions.Telemetry.Abstractions. Note, when ExcludePurviewTelemetry is false this is excluded anyway.

Testing

Property Default Description
TestingFramework TUnit Testing framework. Supported values: TUnit, Xunit, None.
SubstituteFramework TUnitMocks Mocking provider. Supported values: TUnitMocks, NSubstitute, None.
TestDataFramework Bogus Test data provider. Supported values: Bogus, None.
DisableAutoInternalsVisibleTo false Set to true to disable automatic InternalsVisibleTo generation for test types and shared testing projects.

Compiler-visible SDK properties

The SDK now exports its properties via CompilerVisibleProperty, so analyzers and source generators can read them through build_property.<PropertyName>.

Property Description
UsePackageJsonVersion Whether version detection from package.json is active.
RootPackageJson Resolved path to the package.json used for version detection.
RepoRoot Repo root directory found via .git auto-discovery.
Version Package/assembly version, sourced from package.json when detection is enabled.
PackageVersion NuGet package version, sourced from package.json when detection is enabled.
NamespacePrefix Required namespace prefix used to derive RootNamespace.
DisableNamespacePrefixCheck Disables the build error for missing NamespacePrefix.
TestingFramework Selected testing framework (TUnit, Xunit, or None).
SubstituteFramework Selected mocking provider (TUnitMocks, NSubstitute, or None).
TestDataFramework Selected test data provider (Bogus or None).
SourceLinkPackageName SourceLink package ID added by the SDK.
ExcludePurviewTelemetry Opt-out for Purview.Telemetry.SourceGenerator.
ExcludeMSTelemetryExtension Opt-out for Microsoft.Extensions.Telemetry.Abstractions.
DisableGenerateAssemblyInfoClass Disables generated AssemblyInfo helper source.
EnableAssemblyNameGeneration When true (default), AssemblyName derives from RootNamespace.
DisableAutoInternalsVisibleTo Disables automatic InternalsVisibleTo generation.
AutoIncludeUsings Controls SDK-added global usings.
IsCSharpProject True when the project is a .csproj.
IsTestProject True when project name ends with a supported test suffix.
IsSharedTestingProject True for known shared testing helper project names.
TestingType Detected test category suffix from project name.
TargetProjectName Inferred target project name for test projects.
IsContainerProject True when Dockerfile markers indicate container defaults.
IsSdkProject True when an SDK value is detected from project/import declaration.
SdkProjectName Detected SDK name (e.g. Microsoft.NET.Sdk.Web).
IsWebProject Marker used in SDK web-project behaviour.
IsWebSdkProject True when SdkProjectName is Microsoft.NET.Sdk.Web.
IsWorkerSdkProject True when SdkProjectName is Microsoft.NET.Sdk.Worker.
IsAspireHostProject True when SDK starts with Aspire.Sdk.Host.
EditorConfigFilePath Path to the SDK-provided .editorconfig that is injected into @(EditorConfigFiles).
RepositoryEditorConfigFilePath Destination path for bootstrapping a physical repo-level .editorconfig (defaults to git repo root; falls back to Directory.Build.props directory).
BootstrapEditorConfigToRepoRoot When true (default), copies the SDK .editorconfig to RepositoryEditorConfigFilePath if missing.
RepositoryGlobalJsonFilePath Destination path for bootstrapping a physical repo-level global.json (defaults to git repo root; falls back to Directory.Build.props directory).
BootstrapGlobalJsonToRepoRoot When true (default), creates global.json at RepositoryGlobalJsonFilePath if missing.
PurviewDotNetProjectSdkVersionForGlobalJson Version used for msbuild-sdks.Purview.DotNetProjectSdk when bootstrapping global.json (auto-detected from SDK package path, fallback 1.0.0).
DisableAutoCopySdkFiles When true, disables SDK auto-copy/bootstrap for repo files (.editorconfig, global.json).
PurviewAutoSdkPack When true, automatically packs the Sdk/ folder contents into the NuGet package with the correct root-level paths.
CurrentYear Current year used in generated assembly metadata.
AutoGeneratedAssemblyInfoFile Relative path to generated AssemblyInfo source file.
Example: switch a repo to Xunit + NSubstitute and disable Bogus
<Project>
  <PropertyGroup>
    <NamespacePrefix>Acme</NamespacePrefix>
    <TestingFramework>Xunit</TestingFramework>
    <SubstituteFramework>NSubstitute</SubstituteFramework>
    <TestDataFramework>None</TestDataFramework>
  </PropertyGroup>

  <Import Sdk="Purview.DotNetProjectSdk" Project="Sdk.props" />
</Project>

Test project naming conventions

Test projects are automatically detected by their suffix. Supported patterns:

MyProject.UnitTests       → IsTestProject=true, TestingType=Unit
MyProject.IntegrationTests→ IsTestProject=true, TestingType=Integration
MyProject.E2ETests        → IsTestProject=true, TestingType=E2E

Any suffix from the full list is recognised: Unit, Integration, E2E, EndToEnd, Acceptance, Functional, Performance, Load, Smoke, Stress, Regression, Security, Chaos, Scenario, System, Threat, BlackBox, WhiteBox, Accessibility, Interactive, Environment.

Shared testing projects

Projects named SharedTestingFramework, SharedTestingInfrastructure, SharedTestingInfra, SharedTestingUtilities, SharedTestingLibrary, SharedTestingLib, or SharedTestingHelpers are treated as shared testing helpers — they get test package references but not the test runner or coverage settings.


InternalsVisibleTo

The SDK automatically generates [assembly: InternalsVisibleTo("…")] attributes for every non-test C# project. The friend assembly name is derived from the source project's resolved $(AssemblyName), so all naming modes are handled correctly:

  • Explicit <AssemblyName> — if a project sets <AssemblyName>Custom.Assembly</AssemblyName>, the generated attributes use Custom.Assembly.UnitTests, Custom.Assembly.IntegrationTests, etc.
  • DefaultAssemblyName is RootNamespace-derived, so fully-qualified names are used (e.g. Acme.MyProject.UnitTests).
  • EnableAssemblyNameGeneration=false — standard .NET behaviour: $(MSBuildProjectName) (e.g. MyProject.UnitTests).

Two categories of friend assemblies are generated:

  1. TestType variants — one InternalsVisibleTo per defined TestType (Unit, Integration, Architecture, Contract, Functional, …), formatted as $(AssemblyName).{TestType}Tests.
  2. SharedTesting projects — one per known shared testing project name (SharedTestingFramework, SharedTestingInfrastructure, etc.). By default (EnableAssemblyNameGeneration=true) with a NamespacePrefix set, these are prefixed (e.g. Acme.SharedTestingFramework); with EnableAssemblyNameGeneration=false the raw name is used.

Disabling automatic InternalsVisibleTo

To disable automatic InternalsVisibleTo generation, set DisableAutoInternalsVisibleTo=true in your project or Directory.Build.props:

<PropertyGroup>
  <DisableAutoInternalsVisibleTo>true</DisableAutoInternalsVisibleTo>
</PropertyGroup>

EmbeddedAttribute generation

When GenerateAssemblyInfoClassTarget writes the SDK-generated AssemblyInfo source, it also emits:

namespace Microsoft.CodeAnalysis
{
    sealed partial class EmbeddedAttribute : System.Attribute { }
}

This block is guarded by:

#if !PURVIEW_SDK_EXCLUDE_EMBEDDED

AssemblyInfo is emitted with [Microsoft.CodeAnalysis.Embedded], so the project must have a matching Microsoft.CodeAnalysis.EmbeddedAttribute type available at compile time. The SDK emits that attribute to satisfy the reference and to keep generated metadata/source-generator-facing symbols marked as embedded.

Define PURVIEW_SDK_EXCLUDE_EMBEDDED only when your build already provides Microsoft.CodeAnalysis.EmbeddedAttribute from another source; otherwise compilation will fail because the attribute used by generated AssemblyInfo cannot be resolved.


Namespace stripping

Certain suffixes are automatically stripped from RootNamespace to avoid awkward namespace names like Acme.MyProject.Core.Something:

Stripped suffixes: Core, EF, Shared, ClientShared, ServiceDefaults, and all shared testing project names.

Extensions namespace rule (PDS0002)

When a file is placed under a project-root Extensions/ folder, the analyzer intentionally treats that folder as a namespace reset point.

  • Scope: only files where the first project-relative segment is exactly Extensions
  • Expected namespace: derived from subfolders under Extensions/ (file name is ignored)
  • RootNamespace is deliberately ignored for these files

Examples:

Project-relative file path Expected namespace
Extensions/System/StringExtensions.cs System
Extensions/Microsoft/Extensions/Configuration/ConfigurationExtensions.cs Microsoft.Extensions.Configuration
Extensions/TopLevel.cs (global namespace)

To avoid conflicting guidance, IDE0130 is suppressed for files in this root Extensions/ scope. Outside this scope, normal IDE0130 behaviour remains unchanged.


Assembly name generation

By default (EnableAssemblyNameGeneration=true), the SDK treats RootNamespace as the canonical public name: AssemblyName and PackageId both default to the fully evaluated RootNamespace. The defaults are applied during Sdk.props evaluation — before the Microsoft SDK computes TargetName and before the project body — so compilation, output paths, project references, restore, and packing all agree on the same identities. Set EnableAssemblyNameGeneration=false before the SDK import to opt out and fall back to standard .NET behaviour ($(MSBuildProjectName)).

With the default enabled:

Project name NamespacePrefix RootNamespace Resolved AssemblyName / PackageId
Api Acme Acme.Api Acme.Api
Acme.Api Acme Acme.Api Acme.Api (no double-prefix)
Core.Infrastructure Acme Acme.Infrastructure Acme.Infrastructure (.Core suffix stripped)
Acme Acme Acme Acme

Test projects keep their detected suffix: Api.UnitTestsAssemblyName/PackageId = Acme.Api.UnitTests, while RootNamespace remains Acme.Api.

Explicit <AssemblyName> or <PackageId> in a .csproj (or Directory.Build.props) always takes precedence. Because the defaults run before the project body, project-authored values set in the body are evaluated later and win.

Note: set EnableAssemblyNameGeneration=false before the SDK import (for example in Directory.Build.props) — it is consumed during Sdk.props evaluation.


Central Package Management

The SDK sets ManagePackageVersionsCentrally=true. The templates/Directory.Packages.props file contains PackageVersion entries for all packages the SDK auto-adds — all set to Version="*" (floating to latest).

To pin a package:

<PackageVersion Include="TUnit" Version="1.45.29" />

To add project-specific packages, just append PackageVersion entries to your Directory.Packages.props.


Building the SDK

dotnet build src/DotNetProjectSdk.slnx -c Release
dotnet test src/DotNetProjectSdk.slnx -c Release
dotnet pack src/src/DotNetProjectSdk/DotNetProjectSdk.csproj -o ./artifacts

License

MIT

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-prerelease.47 0 9/3/2026
1.0.0-prerelease.46 0 9/3/2026
1.0.0-prerelease.45 58 9/2/2026
1.0.0-prerelease.44 107 8/31/2026
1.0.0-prerelease.43 54 8/31/2026
1.0.0-prerelease.42 71 8/28/2026
1.0.0-prerelease.41 49 8/28/2026
1.0.0-prerelease.40 103 8/19/2026
1.0.0-prerelease.39 95 8/16/2026
1.0.0-prerelease.38 63 8/15/2026
1.0.0-prerelease.37 62 8/14/2026
1.0.0-prerelease.36 63 8/12/2026
1.0.0-prerelease.35 61 8/6/2026
1.0.0-prerelease.34 68 8/6/2026
1.0.0-prerelease.33 54 8/5/2026
1.0.0-prerelease.32 51 8/5/2026
1.0.0-prerelease.31 51 8/5/2026
1.0.0-prerelease.30 56 8/5/2026
1.0.0-prerelease.29 73 8/2/2026
1.0.0-prerelease.28 65 7/31/2026
Loading failed