Scott Hanselman

Project-less scripted C# with ScriptCS and Roslyn

April 24, '13 Comments [31] Posted in NuGet | NuGetPOW | Open Source | VS2012
Sponsored By
ScriptCS inside of SublimeText2 with the ScriptCS package giving SyntaxHighlighting

Glenn Block is working on something interesting that combines C#, NuGet, Roslyn (the new "compiler as a service") and his love of text editors and scripts. Now, with help from Justin Rusbatch (@jrusbatch) and Filip Wojcieszyn (@filip_woj) they are having all kinds of fun...using C# as a scripting language.

Every few years someone tries to turn C# into a competent scripting world, myself included. Often this has included batch files and MacGyver magic, file associations and hacks. Clearly the .NET community wants something like this, but we are collectively still trying to figure out what it should look like. PowerShell aficionados - and I count myself amongst them - might look at such efforts as a threat or a pale reinvention of PowerShell, but the fact remains that C# at the command line, be it as a script or a REPL, is an attractive concept.

Simply put by example, ScriptCS lets me do this:

C:\temp>copy con hello.csx
Console.WriteLine("Pants");
^Z
1 file(s) copied.

C:\temp>scriptcs hello.csx
Pants

That's Hello World. There's no namespace, no class, just some C# in a .csx file. Roslyn takes care of the compilation and the resulting code and .exe never hits the disk.

Self-hosting Web APIs

So that's interesting, but what about bootstrapping a web server using NancyFX to host a Web API?

Go and clone this repo:

git clone https://github.com/scriptcs/scriptcs-samples.git

Look in the Nancy folder. There's a packages.config. Just like a node.js application has a packages.json file with the dependencies in has, a .NET app usually has a packages.config with the name. In node, you type npm install to restore those packages from the main repository. Here I'll type scriptcs -install...

C:\temp\scriptcs-samples\nancy>scriptcs -install
Installing packages...
Installed: Nancy.Hosting.Self 0.16.1.0
Installed: Nancy.Bootstrappers.Autofac 0.16.1.0
Installed: Autofac 2.6.3.862
Installation successful.

Now, running start.csx fires up an instance of Nancy listening on localhost:1234. There's no IIS, no ASP.NET.

C:\temp\scriptcs-samples\nancy>scriptcs start.csx
Found assembly reference: Autofac.Configuration.dll
Found assembly reference: Autofac.dll
Found assembly reference: Nancy.Bootstrappers.Autofac.dll
Found assembly reference: Nancy.dll
Found assembly reference: Nancy.Hosting.Self.dll
Nancy is running at http://localhost:1234/
Press any key to end

There is also the notion of a "ScriptPack" such that you can Require<T> a library and hide a lot of the bootstrapping and complexity. For example, I could start up WebAPI after installing a Web API package that includes some starter code. Note this is all from the command line. I'm using "copy con file" to get started.

C:\temp\foo>scriptcs -install ScriptCs.WebApi
Installing packages...
Installed: ScriptCs.WebApi
Installation completed successfully.
...snip...
Added ScriptCs.WebApi, Version 0.1.0, .NET 4.5
Packages.config successfully created!

C:\temp\foo>copy con start.csx
public class TestController : ApiController {
public string Get() {
return "Hello world!";
}
}

var webApi = Require<WebApi>();
var server = webApi.CreateServer("http://localhost:8080");
server.OpenAsync().Wait();

Console.WriteLine("Listening...");
Console.ReadKey();
server.CloseAsync().Wait();
^Z
1 file(s) copied.

C:\temp\foo>scriptcs start.csx
Found assembly reference: Newtonsoft.Json.dll
...snip...
Listening...

Pretty slick. Add in a little Live Reload-style action and we could have a very node-ish experience, all from the command line and from within your text editor of choice, except using C#.

Note that this is all using the same CLR and .NET that you've already got, running at full speed. Only the compilation is handled differently to give this script-like feel.

Installing ScriptCS

The easiest way to install and use ScriptCS is to use Chocolatey (a system-wide NuGet-based application/component installer. "Chocolatey NuGet," get it?) And yes, it's Chocolatey spelled incorrectly with an "-ey."

You can use Chocolatey to do things like "cinst 7zip" or "cinst git" but we'll be using it just to get ScriptCS set up. It's also easily removed if it freaks you out and it installs no services and won't change anything major up save your PATH.

First paste this into a cmd.exe prompt:

@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%systemdrive%\chocolatey\bin

This will PowerShell, run https://chocolatey.org/install.ps1 and add Chocolatey to your path.

Then, run

cinst ScriptCS

Which will put ScriptCS in a path like C:\Chocolatey\lib\ScriptCs.0.0.0 while Chocolatey makes it available in your PATH.

Sublime Text or Visual Studio

You can get syntax highlighting for your CSX files inside of Sublime Text 2 with the "ScriptCS" package you can install from package control. If you're using Visual Studio you can get the Roslyn CTP to turn on CSX syntax highlighting.

You can use PackageControl in SublimeText2 and install the ScriptCS package

You can even debug your running ScriptCS projects by opening the ScriptCS.exe as a project. (Did you know you can open an EXE as a project?) Add the .csx script to the command line via Project Properties, drag in the scripts you're working on and debug away.

Debugging requires the Roslyn SDK, although personally, I've been doing just fine with scripts at the command line which requires nothing more than the basic install and a text editor.

It's not clear where ScriptCS is going, but it'll be interesting to see! Go get involved at scriptcs.net. This kind of stuff gets me excited about the prospect of a compiler as a service, and also cements my appreciation of C# as my enabling language of choice. Between C# and JavaScript, you can really get a lot done, pretty much anywhere.

I'll have a video walkthrough on how this works as I explain it to Rob Conery up on TekPub soon! (Here's a referral coupon for 20% off of Tekpub!)

What do you think?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Wednesday, April 24, 2013 10:32:23 PM UTC
Pretty cool... It's good to see how other people use Roslyn. I've been looking at adding intellisense to an re-hosted workflow foundation designer using Roslyn. I wonder when Roslyn will be released.
Stephen Hardie
Wednesday, April 24, 2013 11:59:21 PM UTC
Using ScriptCS with Azure Mobile Services would be great.
Romoku
Thursday, April 25, 2013 1:53:35 AM UTC
so i have a function in powershell that will compile me a C# expression and run it returning the objects to powershell and use it a fair bit. i've been meaning to round it out.
here are some examples

#test code
c "new{a=1,b=2,c=3}"
c "new{a=1,b=2,c=3}"
(c DateTime.Now).adddays(5)
(c "new{a=1,b=2,c=3}").b
c 'from x in Directory.GetFiles(@"c:\downloads") where x.Contains("win") select x'

and here is the code.

$mytypes = @()
function run-csharpexpression([string] $expression )
{
$global:ccounter = [int]$ccounter + 1
$local:name = [system.guid]::NewGuid().tostring().replace('-','_').insert(0,"csharpexpr")
$local:ns = "ShellTools.DynamicCSharpExpression.N${ccounter}"

$local:template = @"
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

namespace $ns
{
public static class Runner
{
public static object RunExpression()
{
return [[EXPRESSION]];
}
}
}
"@

$local:source = $local:template.replace("[[EXPRESSION]]",$expression)
#saving to a temporary file so that the error with anonymous types with the same signature doesn't happen
$local:filename = [System.IO.Path]::GetTempFileName()
$null = add-Type $local:source -Language CsharpVersion3 -outputassembly $local:filename
$null = [System.Reflection.Assembly]::LoadFile($local:filename )
invoke-Expression ('[' + $local:ns + '.Runner]::RunExpression()')
}

set-alias c run-csharpexpression

Thursday, April 25, 2013 8:54:35 AM UTC
Hi Scott,

just wanted to let you (and the readers) know about a similar project CS-Script, in my opinion more advanced and very stable (used in production since the .net 2.0).

Not only it's the scripting engine, it enables duck typing, dynamic code generation (without using the C# CodeDOM) and lot's more (excellent help included).

There are also plugins for different versions of Visual Studio, so imagine scripting with combination of C# and Reshaper, it is simply unmatched experience (Intellisense, debugging, everything works).

This is the main reason why I allowed myself to skip entirely the PowerShell train for now, because PowerShell tooling and experience is still in it's infancy, at least compared to VS + R# combo.

Disclaimer: no connection whatsoever with the author of the project, just one extremely happy user.

Regards,
Alex
Alex
Thursday, April 25, 2013 9:06:55 AM UTC
Awesome article Scott, great to hear people talking about ScriptCs. Glenn & crew should be proud :)
Ian
Thursday, April 25, 2013 12:27:09 PM UTC
I look forward to your video. I ran the powershell to install chocolatey, but when I typed cinst ScriptCS I got a screen full of errors :(
Koz
Thursday, April 25, 2013 1:11:11 PM UTC
The IF is a similar project with a wcf service & clickonce app that gives you intellisense and the ability to compile and run custom modules: Instruction Framework

Looking forward to the release of Roslyn.
Thursday, April 25, 2013 2:13:56 PM UTC
I also tried to run the command to install ScriptCS using Chocolatey and got an error saying "The term '\ChocolateyInstall\nuget' is not recognized as the name of a cmdlet, function, script file, or operable program."
Yarx
Thursday, April 25, 2013 2:47:44 PM UTC
This is awesome. It only took a couple of minutes to install and set up and now I'm testing C# RegExs at the command line!

This totally rocks. Well done to Glenn and the other guys who did this.
Thursday, April 25, 2013 3:01:50 PM UTC
Tried this out also received the error others are talking about.

Write-Error : scriptcs did not finish successfully. Boo to the chocolatey gods!
-----------------------
[ERROR] The term '\ChocolateyInstall\nuget' is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the spelling of the n
ame, or if a path was included, verify that the path is correct and try again.
-----------------------
At C:\Chocolatey\chocolateyinstall\helpers\functions\Write-ChocolateyFailure.ps
1:29 char:2
Thursday, April 25, 2013 3:03:42 PM UTC
Actually closing and reopening command prompt did the trick!

I just had to ensure I did a -force command so it re-installed all the goodness. :) Thanks Hansleman!
Thursday, April 25, 2013 3:36:56 PM UTC
Geez, even taking screen shots with an UNREGISTERED version :(
Scott
Thursday, April 25, 2013 4:52:51 PM UTC
Scott - Ya I need to pay. ;)
Thursday, April 25, 2013 6:22:03 PM UTC
@Yarx were you running as admin when you ran the script?

Others who are having errors, close your shell and reopen after installing chocolatey.
Glenn Block
Thursday, April 25, 2013 6:28:57 PM UTC
Regarding the powershell one-liner for installing Chocalatey: wow, scary. Let's forget everything we've learned about MITM and website compromises. Let's discard all immediate protection of https and all the ongoing protection of Authenticode and tell folks to blindly execute whatever code is pulled over an unencrypted, unauthenticated http request.

The code samples are great, thanks, but maybe next time you could pay a little more attention to safe computing practices, even if that means less content that reads like the "curl" one-liners that are so trendy today?
Peter
Thursday, April 25, 2013 7:03:20 PM UTC
Am I missing something, or is it really true that there's zero encryption in Chocolatey's package system -- neither transport crypto like https nor package signing like Authenticode (or the Debian dpkg/apt-get system they cite as an inspiration)?

I guess that explains the unsafe powershell one-liner: if there's no use of modern crypto for cinst and the rest after you install Chocolatey, what difference does it make if the initial installation is untrustworthy?

Worse yet, their public roadmap and Google groups search results suggest that the folks behind Chocolatey aren't even *talking* about adding any kind of encrypted authentication to the package/repo scheme.

Looks like the kind of stuff you'd want to avoid on systems that mattered -- very unlike apt-get.
Peter
Thursday, April 25, 2013 7:22:54 PM UTC
Peter - I don't think we have an issue flipping to SSL.

The rest however is going to require some major changes. We built chocolatey on top of NuGet's infrastructure. Quite a few of the arguments you are making here are completely valid and go towards our choice of infrastructure. Perhaps we should look to customizing or using a different packaging framework.
Thursday, April 25, 2013 9:28:28 PM UTC
Peter and all - change the feed to https://chocolatey.org/api/v2/ and you have SSL - we'll introduce this change in the next version of chocolatey and then break support for transferring packages without.

Also, I've applied SSL changes to the site so that one liner install is pointing to HTTPS.
Friday, April 26, 2013 4:36:02 PM UTC
Was hoping someone would mention the Mono C# REPL, first released in 2008.

Jed
Friday, April 26, 2013 8:00:37 PM UTC
Also keep in mind that there's already a .NET language with a great scripting experience: F#. It has been part of Visual Studio since 2010, and is now supported in Xamarin Studio, too.
Keith
Friday, April 26, 2013 10:19:20 PM UTC
Keith.

Scriptcs is not trying to replace F#, however F# requires one to learn a new language. Scriptcs is offering a light option that allows a developer to use their existing C# skills to write scripts.
Glenn Block
Friday, April 26, 2013 10:19:58 PM UTC
Jed, there is work underway to port the Scriptcs engine to support Mono which uses the Mono.cs engine.
Glenn Block
Thursday, May 09, 2013 9:16:32 AM UTC
@Alex: Thanks for highlighting CS-Script

For an open-source .NET project that I never heard of, it's pretty well-formed. The site is a bit old-school, but the documentation is good.
Andrew Corkery
Wednesday, May 29, 2013 10:42:57 PM UTC
Scott - yet another blog stealing your content here: http://google-chrome-browser.com/project-less-scripted-c-scriptcs-and-roslyn
Jeff Schumacher
Friday, June 14, 2013 1:06:43 AM UTC
I too got errors trying to run the examples. For both the nancy and the webapi example, when I run the 'script -install' command, when it gets to 'INFO: Copying assemblies to bin folder..' I get exceptions.

For the Nancy one I got an ArgumentException 'The path is not of a legal form' at System.IO.Path.NormalizePath(..) ,

For the webapi I got a FileNotFoundException 'could not find file 'bla bla\ScriptCs.WebApi'
Chris Lee
Sunday, June 16, 2013 12:35:46 AM UTC
Chris - Did you ask in the Issues section of their GitHub Site?
Monday, June 17, 2013 2:23:04 AM UTC
Did a chocolatey update and tried the nancy scriptcs install again and it worked.
Chris Lee
Tuesday, August 20, 2013 4:23:24 PM UTC
great! I want learn more about this, i'm learning about C# and I'm interested in how to use C# as a scripting language
Thursday, August 29, 2013 5:05:12 PM UTC
Scott - yet another blog stealing your content here: http://google-chrome-browser.com/project-less-scripted-c-scriptcs-and-roslyn
wm Bekh
Friday, September 20, 2013 3:08:44 PM UTC
Peter, nobody is forcing you to install Chocolatey. Chocolatey is a NuGet wrapper and you use it at your discretion. If you have fundamental issues with encryption for installation packages over the web, maybe the web is not for you at the moment.
Richard
Monday, November 25, 2013 8:12:05 PM UTC
After using both, I have to say I prefer cs-script over scriptcs. It's pretty mature and has lots of cool options, like compiling the script to EXE from an explorer context menu, console or Windows execution, and using a caching pre-compiler. It also supports XP and Server 2003 (I know, I know) that have .NET 4.0 x86 limitations.
Bryan
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.