--- name: dotnet-file-based-apps description: 'Builds .NET 10 file-based C# apps. Directives, CLI commands, csproj migration.' metadata: short-description: .NET skill guidance for foundation tasks --- # dotnet-file-based-apps .NET 10 SDK file-based apps let you build, run, and publish C# applications from a single `.cs` file without creating a `.csproj` project file. The SDK auto-generates project configuration from `#:` directives embedded in the source file. This feature targets scripts, utilities, and small applications where traditional project scaffolding is unnecessary. **This is NOT file I/O.** For FileStream, RandomAccess, FileSystemWatcher, and path handling, see [skill:dotnet-file-io]. **Prerequisites:** Requires .NET 10 SDK or later. Run [skill:dotnet-version-detection] to confirm SDK version. Cross-references: [skill:dotnet-version-detection] for SDK version gating, [skill:dotnet-project-analysis] for project-based analysis (file-based apps have no `.csproj`), [skill:dotnet-scaffold-project] for csproj-based project scaffolding. ## Scope - #: directives (package, sdk, property, project) - CLI commands for file-based apps (dotnet run, dotnet publish) - Migration from file-based to .csproj project format ## Out of scope - File I/O (FileStream, RandomAccess, paths) -- see [skill:dotnet-file-io] - Project-based .csproj scaffolding -- see [skill:dotnet-scaffold-project] - Solution structure analysis -- see [skill:dotnet-project-analysis] --- ## Directives Overview File-based apps use `#:` directives to configure the build. Directives are **SDK-level instructions, not C# syntax**. They must appear at the top of the `.cs` file, before any C# code. Four directive types are supported: | Directive | Purpose | Example | | ------------ | ------------------------------------------ | ----------------------------- | | `#:package` | Add a NuGet package reference | `#:package Serilog@3.1.1` | | `#:sdk` | Set the SDK (default: `Microsoft.NET.Sdk`) | `#:sdk Microsoft.NET.Sdk.Web` | | `#:property` | Set an MSBuild property | `#:property PublishAot=false` | | `#:project` | Reference another project file | `#:project ../Lib/Lib.csproj` | --- ## `#:package` Directive Adds a NuGet package reference. Specify the package name, optionally followed by `@version`. ````csharp #:package Newtonsoft.Json #:package Serilog@3.1.1 #:package Spectre.Console@* ```csharp Version behavior: - **`@version`** -- pins to a specific version - **`@*`** -- uses the latest stable version (NuGet floating version) - **No version** -- only works when Central Package Management (CPM) is configured with a `Directory.Packages.props` file; otherwise, specify a version explicitly or use `@*` --- ## `#:sdk` Directive Specifies which SDK to use. Defaults to `Microsoft.NET.Sdk` if omitted. ```csharp #:sdk Microsoft.NET.Sdk.Web ```csharp ```csharp #:sdk Aspire.AppHost.Sdk@9.2.0 ```csharp Use this directive to access SDK-specific features. For example, `Microsoft.NET.Sdk.Web` enables ASP.NET Core features and automatically includes `*.json` configuration files in the build. --- ## `#:property` Directive Sets an MSBuild property value. Use this to customize build behavior. ```csharp #:property TargetFramework=net10.0 #:property PublishAot=false ```csharp ### Conditional Property Values Property directives support MSBuild property functions and expressions for conditional configuration. **Environment variables with defaults:** ```csharp #:property LogLevel=$([MSBuild]::ValueOrDefault('$(LOG_LEVEL)', 'Information')) ```csharp **Conditional expressions:** ```csharp #:property EnableLogging=$([System.Convert]::ToBoolean($([MSBuild]::ValueOrDefault('$(ENABLE_LOGGING)', 'true')))) ```csharp --- ## `#:project` Directive References another project file or directory containing a project file. Use this to share code between a file-based app and a traditional project. ```csharp #:project ../SharedLibrary/SharedLibrary.csproj ```csharp The referenced project is built and linked as a project reference, just like `` in a `.csproj`. --- ## CLI Commands The .NET CLI supports file-based apps through familiar commands. ### Run ```bash # Preferred: pass file directly dotnet run app.cs # Explicit --file option dotnet run --file app.cs # Shorthand (no 'run' subcommand) dotnet app.cs # Pass arguments after -- dotnet run app.cs -- arg1 arg2 ```csharp When a `.csproj` exists in the current directory, `dotnet run app.cs` (without `--file`) runs the project and passes `app.cs` as an argument to preserve backward compatibility. Use `dotnet run --file app.cs` to force file-based execution. ### Pipe from stdin ```bash echo 'Console.WriteLine("hello");' | dotnet run - ```bash The `-` argument reads C# code from standard input. Useful for quick testing and shell script integration. ### Build ```bash dotnet build app.cs ```bash Build output goes to a cached location under the system temp directory by default. Override with `--output` or `#:property OutputPath=./output`. ### Clean ```bash # Clean build artifacts for a specific file dotnet clean app.cs # Clean all file-based app caches in the current directory dotnet clean file-based-apps # Clean caches unused for N days (default: 30) dotnet clean file-based-apps --days 7 ```text ### Publish ```bash dotnet publish app.cs ```bash File-based apps enable **native AOT by default**. The output goes to an `artifacts` directory next to the `.cs` file. Disable AOT with `#:property PublishAot=false`. ### Pack as .NET Tool ```bash dotnet pack app.cs ```bash File-based apps set `PackAsTool=true` by default. Disable with `#:property PackAsTool=false`. ### Restore ```bash dotnet restore app.cs ```bash Restore runs implicitly on build/run. Pass `--no-restore` to `dotnet build` or `dotnet run` to skip it. --- ## Shell Execution (Unix) Enable direct execution on Unix-like systems with a shebang line. ```csharp #!/usr/bin/env dotnet #:package Spectre.Console using Spectre.Console; AnsiConsole.MarkupLine("[green]Hello, World![/]"); ```text ```bash chmod +x app.cs ./app.cs ```bash The file must use `LF` line endings (not `CRLF`) and must not include a BOM. --- ## Launch Profiles File-based apps support launch profiles via a flat `[AppName].run.json` file in the same directory as the source file. For `app.cs`, create `app.run.json`: ```json { "profiles": { "https": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } } ```text Select a profile with `--launch-profile`: ```bash dotnet run app.cs --launch-profile https ```bash If both `app.run.json` and `Properties/launchSettings.json` exist, the traditional location takes priority. --- ## User Secrets File-based apps generate a stable user secrets ID from the file's full path. ```bash dotnet user-secrets set "ApiKey" "your-secret-value" --file app.cs dotnet user-secrets list --file app.cs ```bash --- ## Implicit Build Files File-based apps respect MSBuild and NuGet configuration files in the same or parent directories: - **`Directory.Build.props`** -- inherited MSBuild properties - **`Directory.Build.targets`** -- inherited MSBuild targets - **`Directory.Packages.props`** -- Central Package Management versions - **`nuget.config`** -- NuGet package source configuration - **`global.json`** -- SDK version pinning Be mindful of these files when placing file-based apps in a repository that also contains traditional projects. Inherited properties may cause unexpected build behavior. --- ## Build Caching The SDK caches build outputs based on source content, directives, SDK version, and implicit build files. Caching improves repeated `dotnet run` performance. Known caching pitfalls: - Changes to implicit build files (`Directory.Build.props`, etc.) may not trigger rebuilds - Moving files to different directories does not invalidate the cache - **Concurrent execution** of the same file-based app can cause build contention errors -- build first with `dotnet build app.cs`, then run multiple instances with `dotnet run app.cs --no-build` Clear the cache with `dotnet clean app.cs` or `dotnet clean file-based-apps`. --- ## Folder Layout Do not place file-based apps inside a `.csproj` project's directory tree. The project's implicit build configuration will interfere. ```csharp # Recommended layout repo/ src/ MyProject/ MyProject.csproj Program.cs scripts/ # Separate directory for file-based apps utility.cs tool.cs ```csharp --- ## Migration: File-Based to Project-Based When a file-based app outgrows a single file, convert to a traditional project. ### Automatic Conversion ```bash dotnet project convert app.cs ```bash This creates a new directory named after the app, containing: - A `.csproj` with equivalent SDK, properties, and package references derived from the `#:` directives - A copy of the `.cs` file with `#:` directives removed The original `.cs` file is left untouched. ### When to Convert Convert to a project-based app when: - Multiple source files are needed - Complex MSBuild customization is required beyond what `#:property` supports - The app needs `dotnet test` with a test framework - The app needs integration with CI/CD workflows that expect a `.csproj` - Team members need IDE project support (Solution Explorer, etc.) --- ## Default Behaviors File-based apps differ from project-based apps in several default settings: | Setting | File-based default | Project-based default | | --------------------------- | ------------------------------- | ----------------------------- | | Native AOT (`PublishAot`) | `true` | `false` | | Pack as tool (`PackAsTool`) | `true` | `false` | | Build output location | System temp directory | `bin/` in project directory | | Publish output location | `artifacts/` next to `.cs` file | `bin///publish/` | --- ## Agent Gotchas 1. **Do not confuse file-based apps with file I/O** -- `dotnet-file-based-apps` covers running C# without a project file (`.NET 10 SDK feature`). For FileStream, RandomAccess, and path handling, use [skill:dotnet-file-io]. 2. **Do not use `#:` directives after C# code** -- all directives must appear at the top of the file, before any C# statements, `using` directives, or namespace declarations. The SDK ignores directives placed later in the file. 3. **Do not omit package versions without CPM** -- `#:package SomePackage` without a version only works when Central Package Management is configured via `Directory.Packages.props`. Without CPM, use `#:package SomePackage@1.0.0` or `#:package SomePackage@*`. 4. **Do not assume `dotnet build` and `dotnet test` work the same** -- `dotnet build app.cs` compiles via a virtual project, but `dotnet test` does not apply to file-based apps. Convert to a project for test framework support. 5. **Do not place file-based apps inside a `.csproj` project directory** -- the project's implicit build files (`Directory.Build.props`, etc.) will affect the file-based app, causing unexpected behavior. Use a separate directory. 6. **Do not run concurrent instances without pre-building** -- parallel execution of the same file-based app causes build output contention. Build first with `dotnet build app.cs`, then run instances with `dotnet run app.cs --no-build`. 7. **Do not forget backward compatibility** -- when a `.csproj` exists in the current directory, `dotnet run app.cs` passes `app.cs` as an argument to the project rather than running it as a file-based app. Use `dotnet run --file app.cs` to force file-based execution. 8. **Do not use `CRLF` line endings with shebang** -- Unix shebang execution requires `LF` line endings and no BOM. Files with `CRLF` will fail with `/usr/bin/env: 'dotnet\r': No such file or directory`. --- ## References - [File-based apps - .NET](https://learn.microsoft.com/en-us/dotnet/core/sdk/file-based-apps) - [Native AOT deployment](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) - [Central Package Management](https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management) - [MSBuild property functions](https://learn.microsoft.com/en-us/visualstudio/msbuild/property-functions) ```` ## Code Navigation (Serena MCP) **Primary approach:** Use Serena symbol operations for efficient code navigation: 1. **Find definitions**: `serena_find_symbol` instead of text search 2. **Understand structure**: `serena_get_symbols_overview` for file organization 3. **Track references**: `serena_find_referencing_symbols` for impact analysis 4. **Precise edits**: `serena_replace_symbol_body` for clean modifications **When to use Serena vs traditional tools:** - **Use Serena**: Navigation, refactoring, dependency analysis, precise edits - **Use Read/Grep**: Reading full files, pattern matching, simple text operations - **Fallback**: If Serena unavailable, traditional tools work fine **Example workflow:** ```text # Instead of: Read: src/Services/OrderService.cs Grep: "public void ProcessOrder" # Use: serena_find_symbol: "OrderService/ProcessOrder" serena_get_symbols_overview: "src/Services/OrderService.cs" ```