.NET has had several scripting capabilities over the years, but never one ‘built in’. Until now that is! With .NET 10, C# move closer to things like Python, JavaScript or PowerShell. No (visible) csproj or dlls.
Lets try it!
echo "Console.WriteLine(Environment.Version);" > app.cs
dotnet run app.cs
It’s like C# but with a sprinkle of Python!
You can even skip the file and pipe directly from stdin
echo 'Console.WriteLine(Environment.Version);' | dotnet run -
Or like this
dotnet run - <<EOF
Console.WriteLine("first");
Console.WriteLine("second");
EOF
Want to build a quick small cli tool? Start with a ‘shebang’ #!/usr/bin/dotnet run and you can execute the .cs file directly. On Linux that is, on Windows it is ignored.
To use a different SDK such as ASP.Net, just add #:sdk Microsoft.NET.Sdk.Web
And to reference nuget packages you can use the #:package directive like below
#:package Figgle.Fonts@0.6.5
Console.WriteLine(Figgle.Fonts.FiggleFonts.Slant.Render("soapfault.com"));
Use cases?
I’m sure I’ll use this both in CI/CD pipeline scenarios but also in Kubernetes.
Kubernetes
In Kubernetes I have often leveraged the power of dotnet’s SDK image for debug and troubleshooting. One way was to mount both a ‘.csproj’ and a ‘.cs’ from a configmap and then do a ‘dotnet run’ in the entrypoint.
We can now simplify that even more by just piping a ‘.cs’ file in the entrypoint.
Lets deploy a job
---
apiVersion: batch/v1
kind: Job
metadata:
name: net10-inline-job
spec:
template:
spec:
restartPolicy: Never
containers:
- name: app
image: mcr.microsoft.com/dotnet/sdk:10.0
command: ["/bin/bash", "-c"]
args:
- |
dotnet run - <<EOF
#:package Figgle.Fonts@0.6.5
Console.WriteLine(Figgle.Fonts.FiggleFonts.Slant.Render(Environment.MachineName));
EOF
If we apply the manifest and look in the log
We can take this one step further and deploy a ASP.Net minimal api.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: net10-inline
spec:
replicas: 1
selector:
matchLabels:
app: net10-inline
template:
metadata:
labels:
app: net10-inline
spec:
containers:
- name: app
image: mcr.microsoft.com/dotnet/sdk:10.0
command: ["/bin/bash", "-c"]
args:
- |
dotnet run - <<EOF
#:sdk Microsoft.NET.Sdk.Web
#:property PublishTrimmed=false
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/troubleshoot", () =>
new { Server = Environment.MachineName });
app.Run();
EOF
ports:
- containerPort: 8080
We now have the API running, lets call it.
All in all this is a really useful feature!