This is the app to develop the extension dotnet-project.
It exists to make app-level app.csproj files much easier to set up for IntelliSense and related editor support in VS Code.
- Check out the extensions docs on https://go.2sxc.org/ext-csproj
- Find out more on https://github.com/2sxc-apps/app-extension-dotnet-project
This extension was created to restore C# IntelliSense in Razor files in VS Code for legacy DNN net48 2sxc apps, after newer C# tooling stopped behaving like the older working version 2.63.52.
What is included:
- a reusable
app.csprojhelper for 2sxc apps - automatic platform resolution for DNN and Oqtane
- DNN-specific legacy Razor design-time support
- Oqtane support for both release and source-code layouts
- a validator script to verify that the helper still evaluates and compiles correctly in design-time mode
Supported scenarios:
- DNN 9
- DNN 10
- Oqtane 10 release
- Oqtane 10 source
Recommended minimum:
- 2sxc
21.05.00
Quick install:
- install the
dotnet-projectApp Extension into the target app - add this import to the app-root
app.csproj:
<Import Project="extensions\dotnet-project\all-in-one.import.csproj" />- open the app folder in VS Code
- run:
pwsh .\tests\validate-helper.ps1Further reading:
- 2sxc VS Code Guide: https://docs.2sxc.org/guides/vscode/index.html
- 2sxc App Extensions Technical Docs: https://docs.2sxc.org/extensions/app-extensions/technical/index.html
The helper is now intentionally flat and explicit:
app.csprojimportsextensions/dotnet-project/all-in-one.import.csprojall-in-one.import.csprojis the composition root- the composition root imports only a small number of topic files, then dispatches into the DNN or Oqtane branch based on
CmsType
Current import order in all-in-one.import.csproj:
parts/shared/namespace-and-output-type.propsparts/shared/build.propsparts/shared/detect-cms.propsparts/dnn/properties.propsonly whenCmsType=dnnparts/dnn/import-dlls.propsonly whenCmsType=dnnparts/oqtane/properties.propsonly whenCmsType=oqtaneparts/oqtane/import-dlls.propsonly whenCmsType=oqtaneparts/shared/import-dlls.propsparts/shared/ignore-editions.propsparts/dnn-intellisense/all.propsonly whenDesignTimeBuild=trueandCmsType=dnn
The current layout uses 3 kinds of props files:
shared/*Common project shape, environment detection andCmsTyperesolution, shared references, and edition filteringdnn/*andoqtane/*Platform-specific detection, properties, and referencesdnn-intellisense/*DNN-only Razor design-time support for VS Code IntelliSense
parts/shared/namespace-and-output-type.props- sets
RootNamespace=AppCode - sets
OutputType=Library
- sets
parts/shared/build.props- keeps
DESIGN_TIME_BUILDdefined for the helper project - centralizes warning suppressions used by the helper
- keeps
parts/shared/detect-cms.props- defaults
CmsTypetoauto - normalizes
CmsTypeto lowercase - imports
parts/dnn/detect.propsandparts/oqtane/detect.props - switches
CmsTypefromautotodnnoroqtanewhen only one host is detected - validates invalid or ambiguous host resolution before build
- defaults
parts/dnn/detect.props- defaults
DnnMainDlltoDotNetNuke.dll - resolves
DnnBinPathfor both standard and edition-based layouts - sets
DnnDetected=truewhen the marker DLL exists
- defaults
parts/dnn/properties.props- defaults
DnnTargetFramework=net48 - defaults
DnnLangVersion=8.0 - applies
TargetFramework,LangVersion, andPathBinfor the DNN branch
- defaults
parts/dnn/import-dlls.props- adds DNN and classic ASP.NET references
- adds legacy Razor DLL references for DNN
- keeps the ASP.NET Core 2.2 package workaround used for editor support
parts/oqtane/detect.props- defaults
OqtaneMainDlltoOqtane.Server.dll - defaults
OqtaneTargetFramework=net10.0 - resolves production and dev/source paths for standard and edition-based layouts
- sets
OqtaneIsProd,OqtaneIsDev, andOqtaneDetected
- defaults
parts/oqtane/properties.props- defaults
OqtaneLangVersion=latest - applies
TargetFrameworkandLangVersionfor Oqtane - sets
PathBinto prod first, then falls back to dev/source
- defaults
parts/oqtane/import-dlls.props- imports
Oqtane.*.dllfrom the resolvedPathBin
- imports
parts/shared/import-dlls.props- imports shared references such as
ToSic.*,Connect.Koi,CsvHelper,System.Text.Json, andSystem.Memory - imports
Dependencies\*.dllfrom the app
- imports shared references such as
parts/shared/ignore-editions.props- defaults
IgnoredEditionDirstolive;bs3;bs4 - normalizes and expands the semicolon-separated list
- removes ignored items early
- removes them again before
CoreCompilebecause SDK inference can re-add them
- defaults
parts/dnn-intellisense/all.props- aggregates the DNN-only design-time helper files
parts/dnn-intellisense/razor-tool-paths.props- resolves the Razor SDK folders from
MSBuildSDKsPath
- resolves the Razor SDK folders from
parts/dnn-intellisense/razor-analyzers.props- resolves
RazorAnalyzerPath - registers the Razor analyzer
- resolves
parts/dnn-intellisense/razor-tooling.props- resolves and adds the helper assemblies needed for legacy DNN Razor IntelliSense
parts/dnn-intellisense/include-cshtml.props- includes
**\*.cshtmlasAdditionalFiles - preserves
TargetPathfor the Razor design-time pipeline
- includes
tests/validate-helper.ps1- runs the property evaluation check
- runs the design-time compile check
- defaults to the local app
app.csproj - accepts
-Projectto validate a differentapp.csproj
Use validate-helper.ps1 after changing any import, host resolution, reference, or design-time file.
The script is a thin wrapper around two dotnet msbuild checks:
Property evaluationVerifies that the helper resolves the expected core values:CmsType,TargetFramework, andPathBin.Design-time compileRunsCompilewithDesignTimeBuild=true,BuildingInsideVisualStudio=true, andSkipCompilerExecution=trueto verify the IntelliSense pipeline still evaluates correctly.
Requirements:
pwshdotnet
Common usage:
# from the app root
pwsh .\tests\validate-helper.ps1
# against a specific helper app
pwsh .\tests\validate-helper.ps1 -Project "A:\path\to\app.csproj"What the script does:
- Resolves the project path.
- Prints each
dotnet msbuildcommand before running it. - Stops immediately if either check fails.
- Prints
Validation completed successfully.if both checks pass.
flowchart TB
subgraph allInOne["<code>extensions/dotnet-project/all-in-one.import.csproj</code>"]
direction TB
prep["prep"]
cmsDetect["cmsDetect"]
dnn["dnn"]
oqtane["oqtane"]
common["common"]
dnnDevKit["dnnDevKit"]
end
subgraph prep["Step 1: Preparation <code>/1-prep/</code>"]
direction LR
prepNs["namespace-and-output-type.props"] -->
prepBuild["constants-and-warnings.props"]
end
subgraph cmsDetect["Step 2: Prepare Paths and Detect CMS <code>/2-detect/</code>"]
direction LR
prepCMS["detect-cms.props"]
end
subgraph dnn["Step 3: Setup DNN <code>/3-setup/dnn/</code>"]
direction LR
dnnDlls["import-dlls.props"]
dnnProps["properties.props"]
end
subgraph oqtane["Step 3: Setup Oqtane <code>/3-setup/oqtane/</code>"]
direction LR
oqtDlls["import-dlls.props"]
oqtProps["properties.props"]
end
subgraph common["Step 8: Final Steps <code>/8-final/</code>"]
direction LR
commonEditions["ignore-editions.props"]
commonDlls["import-dlls.props"]
end
subgraph dnnDevKit["Step 9: DNN C# DevKit Helpers <code>/9-devkit/dnn/all.props</code>"]
direction LR
DTPATHS["razor-tool-paths.props"]
DTAN["razor-analyzers.props"]
DTTOOLS["razor-tooling.props"]
DTCSHTML["include-cshtml.props"]
end
dnnProps --> dnnDlls
oqtProps --> oqtDlls
commonDlls --> commonEditions
APP["app.csproj<br>(your file)"] --> allInOne
prep --> cmsDetect
cmsDetect -- "CmsType == oqtane" --> oqtane
cmsDetect -- "CmsType == dnn" --> dnn
dnn --> common
oqtane --> common
common -- "if DesignTimeBuild and CmsType == dnn" --> dnnDevKit
DTPATHS --> DTAN
DTAN --> DTTOOLS
DTTOOLS --> DTCSHTML
prepCMS@{ shape: subproc}
dnnDevKit:::noWrap
allInOne:::noWrap
prep:::noWrap
cmsDetect:::noWrap
dnn:::noWrap
oqtane:::noWrap
classDef noWrap white-space:nowrap
This is the flow of code:
- Set
CmsType=autoif not previously set. - Normalize to lowercase - so it should only be
auto,dnn, oroqtaneat this point. - Run the platform-specific detection logic, which looks for the marker DLL in the expected paths for each platform, and sets
DnnDetectedorOqtaneDetectedaccordingly. - If
CmsTypeis stillauto, switch it todnnoroqtaneif only one of them was detected. - If
CmsTypeis notdnnoroqtane, throw an error because the host cannot be resolved.
As an output, it will have a final:
CmsTypevalue of eitherdnnoroqtanethat can be used for conditional imports and properties later on.PathBinvalue pointing to the correctbinfolder of the host, which is needed for reference imports later on.
flowchart TD
A[Start] --> B{Is CmsType set?}
B -- No --> C[Set CmsType=auto]
B -- Yes --> D[Normalize CmsType to lowercase]
D --> E[Run DNN detection logic]
D --> F[Run Oqtane detection logic]
E --> G{Is DNN detected?}
F --> H{Is Oqtane detected?}
G -- Yes --> I[DnnDetected=true]
H -- Yes --> J[OqtaneDetected=true]
I --> K{Is CmsType still auto?}
J --> K
K -- Yes and DNN only --> L[Set CmsType=dnn]
K -- Yes and Oqtane only --> M[Set CmsType=oqtane]
K -- No or Both detected --> N[Error: Ambiguous or invalid CmsType]
L --> O[Set PathBin to DnnBinPath]
M --> P[Set PathBin to OqtaneProdBinPath, fallback to OqtaneDevBinPath]
O --> Q[End with CmsType=dnn and PathBin set]
P --> R[End with CmsType=oqtane and PathBin set]
flowchart LR
BUILD[shared/build.props] --> B1[DESIGN_TIME_BUILD constant]
BUILD --> B2[NoWarn helper suppressions]
DNNDET[dnn/detect.props] --> D1[resolve DNN bin path]
DNNDET --> D2[set DnnDetected]
DNNPROPS[dnn/properties.props] --> D3[set DNN target framework language and PathBin]
DNNDLLS[dnn/import-dlls.props] --> D4[import DNN and legacy Razor DLLs]
OQTDET[oqtane/detect.props] --> O1[resolve Oqtane prod and dev paths]
OQTDET --> O2[set OqtaneIsProd OqtaneIsDev and OqtaneDetected]
OQTPROPS[oqtane/properties.props] --> O3[set Oqtane target framework language and PathBin]
OQTDLLS[oqtane/import-dlls.props] --> O4[import Oqtane DLLs]
SHARED[shared/import-dlls.props] --> S1[import shared 2sxc and framework DLLs]
EDIT[shared/ignore-editions.props] --> EI[remove live bs3 bs4 by default or any IgnoredEditionDirs override]
DT[dnn-intellisense/all.props] --> RZ[resolve Razor SDK tooling paths]
DT --> RA[register Razor analyzer]
DT --> AC[include cshtml as AdditionalFiles]
DT --> HR[add DNN Razor helper refs]
- Minor replace tabs in .sln file with spaces to fix formatting in VS Code.