Exploring C# Scripting with dotnet-script

Exploring C# Scripting with dotnet-script
by Brad Jolicoeur
08/07/2024

I was recently working through some automation challenges and looked up c# scripting options. I wasn't really expecting to find any compelling solutions, but the world of .NET is evolving at a rapid rate in the last few years and thought I'd try my luck. I was pleasantly surprised when the dotnet tool dotnet-script came up in my search. I had not seen this tool before and it started me on a path of evaluation.

Why Scripting?

Why would you want to write scripts in csharp?

  • Scratch pad for figuring something out
  • One time operation you need to hand off to another team to execute
  • Ability to inject customizations in your SaaS platform
  • Automate tasks in the same language that you use for everything else

Dotnet Scripting Tools

There are a number of available tools for scripting with c# that have been around for some time. I found one reference that indicated CS-script had it's start back in 2004.

The tools that have my interest at the moment are dotnet-script and CS-script. They both support cross platform and are current.

  • dotnet-script
  • CS-script

My recent foray into c# scripting lead me to dotnet-script. This tool seems like a good candidate for automation and as a scratch pad. The install is light and easy since you only need .Net 6 or later SDK installed as a prerequisite and you can install with dotnet tool in the cli.

In the past I've used CS-script and it is one of the most mature options that support hosting within your own application as well as CLI. It looks like the CS-script team has kept up with the newer developments with the dotnet ecosystem and this is a rich option for more complex scenarios. This is what I'd likely consider if I was going to embed scripting in to an application.

Both CS-Script and dotnet-script utilize the .csx file extension for code files.

Some older options for c# scripting.

  • ScriptCS
  • Nake
  • Roslyn Scriptin Api

Top Level Statements

Why not just create a console app with top level statements instead?

It took me a bit to warm up to top level statements, but now that I've gotten used to them, I love them. It reduces the ceremony and makes .NET more accessible to engineers who have experience with interpreted languages. They also make creating quick console utilities fun and easy.

That said, there is still more ceremony with creating a project that contains several files and needs to be packaged to deploy. Where as a script file can be a standalone text file that is executed. No fancy IDE required to create a .csx file and you just need the .NET SDK installed to run the script.

Scripting vs Console App

Why is scripting sometimes a better option?

The down side of creating console apps for automation tasks, is that there is still a lot of ceremony with packaging and deploying them. If your not sure what I mean, try handing your DevOps team a console app to execute during your next release and let the fun begin.

In contrast, a bash script or .csx script file is relatively light weight to hand off. They have a limited number of prerequisites to install and you can easily stream the script files from source control or an S3 bucket. In addition, you are not handing off something that is opaque to run. They can open up the script file and reason through what you are doing if they feel the need.

Getting Started

With all that background, you are likely looking to get started with an example. Since my latest foray into c# scripting was with the dotnet-script tool, I'll show how to get started with this tool.

Installation

As a prerequisite you need to have .Net 6 or later SDK installed on your machine.

From a terminal window execute the following to install globally:

dotnet tool install -g dotnet-script

Create a script

To create a script you can just create a .csx file with notepad and add your code to it, but if you use dotnet-script to init your script, you will be set up to debug in VSCode, so I recommend that path.

create a fresh folder on your machine and make that folder the current path for your terminal window.

Then execute the following in the folder dotnet script init

This will generate a omnisharp.json file and a main.csx file in the folder.

.
├── .vscode
│   └── launch.json
├── main.csx
└── omnisharp.json

You can now run this script by executing dotnet script main.csx. You will see 'Hello World!' as the output.

I recommend opening the folder with VSCode to explore the code file at this point, but you could simply open the main.csx file with notepad or other text editor of your choice.

Use a NuGet Package

There isn't much you can do in c# without referencing a NuGet package. It is quick and easy to reference a package when using dotnet-script. Prefix the line with #r and add the name of the package and version like so.

#r "nuget: Newtonsoft.Json, 12.0.1"

This will use the NuGet sources already configured on your workstation. If you need other sources, check out the dotnet-script readme for instructions.

Example that Calls API

By now, hopefully you are ready for a more practical example that uses a NuGet package and calls an API. I've found that humor is a requirement for surviving in the software development industry, so let's create a script that tells us a joke using jokeapi.dev.

Create a file named joke.csx in your folder next to the main.csx file you created earlier. Then paste these contents into your joke.csx file.

#r "nuget: Newtonsoft.Json, 12.0.1"

using System.Net.Http;
using Newtonsoft.Json;

var client = new HttpClient();

string apiURL = "https://v2.jokeapi.dev/joke/Programming?safe-mode";
var response = (await client.GetAsync(apiURL));
var json = (await response.Content.ReadAsStringAsync());

dynamic content = JsonConvert.DeserializeObject(json);

if(content.type == "twopart")
{
    Console.WriteLine($"{content.setup}");
    await Task.Delay(2500);
    Console.WriteLine($"-> {content.delivery}");
}
else
{
    Console.WriteLine($"{content.joke}");
}

You can now execute your joke script.

dotnet script joke.csx

Enjoy the Joke.

Debug in VSCode

Debugging is also easy in VSCode, especially if you initialize your folder with dotnet-script. You must have the folder .vscode at the root of your project folder and it needs a launch.json file with configuration for .NET script.

After executing dotnet script init in my folder I ended up with a launch.json that looked like this.

Tip: I found that if the .vscode folder was not at the root level of the folder I had open in VSCode, debugging did not work.

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": ".NET Script Debug",
            "type": "coreclr",
            "request": "launch",
            "program": "dotnet",
            "args": [
                "exec",
                "C:/Users/me/.dotnet/tools/.store/dotnet-script/1.5.0/dotnet-script/1.5.0/tools/net8.0/any/dotnet-script.dll",
                "${file}"
            ],
            "cwd": "${workspaceRoot}",
            "stopAtEntry": false
        }
    ]
}

Now you can set break points in your joke.csx file and hit F5 while the file is open to debug.

Impressions

In short, I'm impressed with my experience with dotnet-script. I think it is a useful tool that I will use for automating tasks, CI/CD scripting and as a scratch pad for working through problems.

If you are a .NET Engineer and/or you are on a team that heavily uses .NET then you will find dotnet-script useful. In this scenario, you could learn a new language like Python to do these tasks, but then you have something new to maintain. If you are the only one on your team who knows the language you picked, guess who gets stuck maintaining it. Even if you get stuck as the maintainer of a dotnet-script solution, your future self will appreciate not dusting off a language you haven't used in months to make a small change.

If you are interested in getting into the world of .NET/C# or want to introduce folks to the dark side. dotnet-script is a good place to start.

If you are already skilled in Python and your team is a heavy user of that language, then I'd stick with Python. There are going to be an ocean of examples to pull from in the Python ecosystem and it was designed to do this type of task.

Resources

You May Also Like


Fabricate Sample Data with ChatGPT

fall-road.jpg
Brad Jolicoeur - 08/24/2024
Read

Data Analysis with C#: Leveraging .NET for High-Performance Tasks

in-isolation.JPG
Brad Jolicoeur - 08/17/2024
Read

Adding Blazor to My Razor Page Site

arch-base.JPG
Brad Jolicoeur - 06/15/2024
Read