Remigiusz ZalewskiRemigiusz Zalewski

.NET 10 File-Based Apps - Best Way to Script in C#

dotnet10-file-based-apps

Introduction

How many times have you needed a quick C# script - to call an API, transform some data, or test a library - and ended up spending 10 minutes setting up a project just to run 20 lines of code? That friction is real, and it pushes a lot of .NET developers toward Python or PowerShell for scripting tasks.

.NET 10 changes that. File-based applications let you write a single .cs file and run it directly with dotnet run. No .csproj, no solution, no boilerplate. Just code.

In this post I'll walk you through everything: NuGet packages, MSBuild properties, SDKs, spinning up a full Minimal API from a single file, publishing, and converting back to a full project when you need to.

🎬 Watch the full video here:


Prerequisites

You need .NET 10 SDK installed. To verify, run:

dotnet --list-sdks

Make sure a 10.x.x entry appears in the output. If not, grab it from the Microsoft website. This feature will not work on .NET 8 or .NET 9 - the SDK version matters.


⚡ Your First File-Based App

Create a file called demo.cs:

Console.WriteLine("Hello, World!");

Run it:

dotnet run demo.cs

That's it. No project file, no namespace, no static void Main. The runtime handles everything.

If you have a global.json pinning an older SDK in the directory, you'll get an error like couldn't find the project to run. Make sure the effective SDK is .NET 10.


📦 Using NuGet Packages

You reference NuGet packages with a special directive at the top of the file:

#:package Newtonsoft.Json@13.0.*

using Newtonsoft.Json;

var x = new { Name = "John", Age = 18 };
var serialized = JsonConvert.SerializeObject(x);
Console.WriteLine(serialized);

Key points:

  • Use #:package PackageName@version at the top of the file
  • Wildcards like 13.0.* resolve to the latest matching version
  • The package is downloaded and cached automatically on first run

You'll notice a warning about reflection and trimming. That leads us to the next section.


MSBuild Properties

File-based apps have native AOT enabled by default. That's great for performance, but it breaks libraries that rely on reflection - like Newtonsoft.Json. You can override MSBuild properties directly in the file:

#:property PublishAot false
#:property LangVersion preview
#:property Nullable false
  • PublishAot false - disables AOT when publishing, required for reflection-based libraries
  • LangVersion preview - gives you access to C# preview features; set it to a specific version like 14 to lock to that
  • Nullable false - turns off nullable reference types (they are enabled by default in file-based apps)

These map directly to MSBuild properties you'd normally put in a .csproj. The #:property directive is how you reach them from a single file.


🌐 Using SDKs - Building a Minimal API

This is where it gets interesting. You can target the Microsoft.NET.Sdk.Web SDK from a single file and build a fully functional Minimal API:

#:property PublishAot false
#:sdk Microsoft.NET.Sdk.Web
#:package Microsoft.AspNetCore.OpenApi@*
#:package Scalar.AspNetCore@*

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();
builder.Services.AddHttpClient();

var app = builder.Build();

app.MapOpenApi();
app.MapScalarApiReference();

app.MapGet("/weather/{city}", async (string city, IHttpClientFactory factory) =>
{
    var client = factory.CreateClient();
    var result = await client.GetStringAsync(
        $"https://wttr.in/{city}?format=j1");
    return Results.Content(result, "application/json");
});

app.Run();

Key points:

  • #:sdk switches the underlying SDK - here to the Web SDK
  • You get full access to WebApplication, DI, middleware, and routing
  • The Scalar UI is available at /scalar/v1 after running
  • This is 26 lines and produces a working HTTP API

Run it the same way:

dotnet run demo.cs

Publishing a File-Based App

To publish a self-contained executable from a single file:

#:property PublishAot false
#:property PublishSingleFile true
#:property EnableStaticWebAssetsManifest false

Then publish:

dotnet publish demo.cs

With these properties, the output in artifacts/demo/ contains just two files: the executable and the PDB. Without PublishSingleFile true, you'll also get web.config and in-process hosting files because of the Web SDK.

The EnableStaticWebAssetsManifest false property suppresses extra static asset files that aren't needed for API-only apps.


Converting to a Full Project

When the script grows and you need a proper project structure, use the built-in conversion command:

dotnet project convert demo.cs --output demo

This generates a demo.csproj with all the NuGet packages and properties you defined in the file. Your demo.cs stays in place - the --delete-source flag removes it if you want a clean cut.

One current limitation: only single-file conversion is supported. .NET 11 is expected to bring multi-file support, which will make this workflow viable for larger scripts that were split across helpers. Worth watching.


Comparison: File-Based vs Traditional Project

FeatureFile-Based AppTraditional Project
Setup timeSecondsMinutes
NuGet packages#:package directive.csproj reference
MSBuild properties#:property directive.csproj element
SDK targeting#:sdk directiveSdk attribute in .csproj
AOT by defaultYesNo
Nullable by defaultYesDepends on template
Multi-file support.NET 11 (planned)Yes
Best forScripts, prototypes, toolsProduction services

Key Takeaways

  • File-based apps in .NET 10 let you run a single .cs file with dotnet run demo.cs - no project required.
  • Use #:package, #:sdk, and #:property directives to access NuGet packages, alternate SDKs, and MSBuild properties from within the file.
  • AOT is enabled by default; set #:property PublishAot false when using reflection-based libraries like Newtonsoft.Json.
  • You can build a fully working Minimal API with Scalar and OpenAPI from a single 26-line file using the Web SDK.
  • When a script grows into a real project, dotnet project convert generates a .csproj from the file automatically.
  • Multi-file conversion is coming in .NET 11, making this workflow even more practical for larger tooling scripts.

Connect with me:

Follow me on LinkedIn

Subscribe on YouTube