dotnet-file-based-apps
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.
#: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 `<ProjectReference>` 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/<config>/<tfm>/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)