Run Node.js and .NET in-process
A software play in two acts
{ Act I }
Where Node.js scripts .NET in-process
on Windows, Mac OS, and Linux (NEW!)
{ Act II }
Where .NET scripts Node.js in-process (NEW!)
skip to act I | skip to act II
Brought to you by Tomasz Janczuk / @tjanczuk
(Use space or arrow keys to navigate)
Prologue
Where Node.js meets .NET in-process
and the intrigue begins
{ Tess }
What problems does Edge.js solve?
{ Scott Hanselman }
Ah, whatever problem you have. If you have this problem, this solves it.
.NET welcomes Node.js
Call C# async lambda from Node.js
var edge = require('edge');
var hello = edge.func(function () {/*
async (input) => {
return ".NET welcomes " + input.ToString();
}
*/});
hello('Node.js', function (error, result) {
if (error) throw error;
console.log(result);
});
see full code
> node sample.js
.NET welcomes Node.js
On Windows...
...On Mac...
...and on Linux
Node.js welcomes .NET
Call Node.js functions from .NET
using EdgeJs;
public static async void Start()
{
var func = Edge.Func(@"
return function (data, cb) {
cb(null, 'Node.js ' + process.version + ' welcomes ' + data);
}
");
Console.WriteLine(await func(".NET"));
}
read more
> sample.exe
Node.js v0.10.28 welcomes .NET
On Windows (Mac/Linux coming)
One interop model
Async, in-process calling convention between CLR and Node.js
Edge.js connects
Node.js and .NET ecosystems
JavaScript with C#, F#, Python, Lisp, and PowerShell
loose typing with strong typing
IO-bound single threaded with CPU-bound multi-threaded
cool with awesome
on Windows, MacOS, and Linux
in one process
Act I
Where Node.js scripts .NET in-process
on Windows, Mac OS, and Linux (NEW!)
docs
C# welcomes Node.js
Call C# async lambda from Node.js
var edge = require('edge');
var hello = edge.func(function () {/*
async (input) => {
return "CSharp welcomes " + input.ToString();
}
*/});
hello('Node.js', function (error, result) {
if (error) throw error;
console.log(result);
});
see full code
> node sample.js
CSharp welcomes Node.js
Not only C#
F# welcomes Node.js
Call F# async workflows from Node.js
var edge = require('edge');
var hello = edge.func('fs', function () {/*
fun input -> async {
return "FSharp welcomes " + input.ToString()
}
*/});
hello('Node.js', function (error, result) {
if (error) throw error;
console.log(result);
});
how to F# in Node.js
$> node sample.js
FSharp welcomes Node.js
Python welcomes Node.js
Script IronPython from Node.js
var edge = require('edge');
var hello = edge.func('py', function () {/*
def hello(input):
return "Python welcomes " + input
lambda x: hello(x)
*/});
hello('Node.js', function (error, result) {
if (error) throw error;
console.log(result);
});
how to python in Node.js
$> node sample.js
Python welcomes Node.js
T-SQL welcomes Node.js
Access MS SQL from Node.js using in-process ADO.NET
var update = require('edge').func('sql', function () {/*
update Products
set ProductName = @newName
where ProductId = @id
*/});
update({ id: 10, newName: 'New Ikura' }, function (error, result) {
if (error) throw error;
console.log(result);
});
how to T-SQL in Node.js
PowerShell welcomes Node.js
Call PowerShell scripts asynchronously from Node.js (Windows only)
var edge = require('edge');
var hello = edge.func('ps', function () {/*
"PowerShell welcomes $inputFromJS"
*/});
hello('Node.js', function (error, result) {
if (error) throw error;
console.log(result);
});
how to PowerShell in Node.js
c:\projects\edgedemo> node sample.js
PowerShell welcomes Node.js
Lisp welcomes Node.js
Call Lisp from Node.js
var edge = require('edge');
var factorial = edge.func('lsharp', function () {/*
(def fact(n)
(if (is n 0) 1 (* n (fact (- n 1)))))
*/});
factorial([5], function (error, result) {
if (error) throw error;
console.log(result);
});
edge-lsharp extension by @richorama
c:\projects\edgedemo> node sample.js
120
FOO welcomes Node.js
Extend Edge.js with other CLR/Mono languages or DSLs
var helloCs = edge.func(...); // C# is the default
var helloFs = edge.func('fs', ...); // F#
var helloPy = edge.func('py', ...); // Python
var helloFo = edge.func('foo', ...); // Your CLR/Mono language or DSL here
learn more
Bind to library
Call a method from existing CLR library
var hello = require('edge').func({
assemblyFile: 'My.Edge.Samples.dll',
typeName: 'Samples.FooBar.MyType',
methodName: 'MyMethod' // Func<object,Task<object>>
}});
hello('Node.js', function (error, result) { ... });
how to integrate CLR code
Bind to code
Compile source code on the fly
var hello = edge.func(
'async (input) => { return "C# welcomes " + input.ToString(); }'
);
how to integrate CLR code
Bind to multiline code
Embed multi-line source code inline
var hello = edge.func(function () {/*
async (input) => {
return "C# welcomes " + input.ToString();
}
*/});
how to integrate CLR code
Bind to source file
Compile source code from a file
var hello = edge.func('mysample.cs');
how to integrate CLR code
Multiple languages
Compile code for many CLR languages or DSLs through extensibility
var helloCs = edge.func(...); // C# is the default
var helloFs = edge.func('fs', ...); // F#
var helloPy = edge.func('py', ...); // Python
var helloPs = edge.func('ps', ...); // PowerShell
var helloPs = edge.func('sql', ...); // T-SQL via ADO.NET
...
how to support other CLR languages or DSLs
Two C# representations
Source code may be an async lambda expression or class library
var helloLambda = edge.func(function () {/*
async (input) => { return "C# welcomes " + input.ToString(); }
*/});
var helloClass = edge.func(function () {/*
using System.Threading.Tasks;
public class Startup {
public async Task<object> Invoke(object input) {
return ".NET welcomes " + input.ToString();
}
}
*/});
how to integrate CLR code
Dynamics
Use C# dynamics to access objects passed from JavaScript
var hello = edge.func(function () {/*
async (dynamic input) => {
return input.nested.text + " work!";
}
*/});
var input = {
nested: {
text: "Dynamics"
}
};
hello(input, function (error, result) { ... });
References and namespaces
Reference libraries and import namespaces within script
var accessSql = edge.func(function () {/*
#r "System.Data.dll"
using System.Data;
async (input) => {
...
}
*/});
Sync vs async
Call CLR functions synchronously or asynchronously
var hello = edge.func(...);
// Make asynchronous call
hello('Node.js', function (error, result) {
...
});
// Make synchronous call
// Requires CLR function to return completed Task<object>
var result = hello('Node.js', true);
how to integrate CLR code
Debugging
Use Visual Studio to debug C# code running in node.exe
Data from Node.js to C#
Pass data from Node.js to C#
var hello = require('edge').func('My.Sample.dll');
var payload = {
anInteger: 1,
aNumber: 3.1415,
aString: 'foobar',
aBool: true,
anObject: { first: 'Tomasz', last: 'Janczuk' },
anArray: [ 'a', 1, true ],
aBuffer: new Buffer(1024)
}
hello(payload, function (error, result) { ... });
see full code
Data from C# to Node.js
Pass data from C# to Node.js
var getData = require('edge').func(function () {/*
async (input) => {
return new {
anInteger = 1,
aNumber = 3.1415,
aBool = true,
anObject = new { a = "b", c = 12 },
anArray = new object[] { "a", 1, true },
aPerson = new Person("Tomasz", "Janczuk"),
aBuffer = new byte[1024]
};
}
*/});
getData(null, function (error, result) { ... });
see full code
Export Node.js function to C#
Node.js function is also data
var addAndMultiplyBy2 = require('edge').func( ... );
var payload = {
a: 2,
b: 3,
timesTwo: function(input, callback) {
callback(null, input * 2);
}
};
addAndMultiplyBy2(payload, function (error, result) { ... });
see full code
Call Node.js function from C#
Node.js function is also data, continued
var addAndMultiplyBy2 = require('edge').func(function () {/*
using ...;
async (dynamic data) => {
int sum = (int)data.a + (int)data.b;
var timesTwo = (Func<object,Task<object>>)data.timesTwo;
return await timesTwo(sum);
}
*/});
see full code
Return C# function to Node.js
C# function is also data
var createHello = require('edge').func(function () {/*
async (input) => {
return (Func<object,Task<object>>)(async (i) => {
Console.WriteLine("Hello from .NET");
return null;
});
}
*/});
var hello = createHello(null, true);
hello(null, true); // prints out "Hello from .NET"
see full example
Manage C# state from Node.js
Call Node.js proxy to C# closure over CLR data
var createCounter = require('edge').func(function () {/*
async (input) => {
var k = (int)input; // CLR state
return (Func<object,Task<object>>)
(async (i) => { return ++k; });
}
*/});
// create counter with initial state of 12
var counter = createCounter(12, true);
console.log(counter(null, true)); // prints 13
console.log(counter(null, true)); // prints 14
see full example
Performance
Latency of Edge.js call is 32x smaller than localhost, cross-process call over HTTP
Let's build something...
Script T-SQL in Node.js via ADO.NET
var update = require('edge').func('sql', function () {/*
update Products
set ProductName = @newName
where ProductId = @id
*/});
update({ id: 10, newName: 'New Ikura' }, function (error, result) {
if (error) throw error;
console.log(result);
});
how to T-SQL in Node.js
Full ADO.NET in Node.js
#r "System.Data.dll"
#r ...
using System.Data;
using ...
public class Startup {
public async Task<object> Invoke(string command) {
string connectionString = ...;
using (var conn = new SqlConnection(connectionString)) {
using (var command = new SqlCommand(commandString, conn)) {
await connection.OpenAsync();
using (var reader = await command.ExecuteReaderAsync() {
...
full article
by
David Neal (@reverentgeek)
Multi-threading in Node.js process
Run CPU-bound background work on CLR thread pool
var heavyLifting = require('edge').func(function () {/*
async (input) => {
// we are on V8 thread here
return await Task.Run<object>(async () => {
// we are on CLR thread pool thread here
await Task.Delay(5000); // simulate CPU bound
return ".NET welcomes " + input.ToString();
});
}
*/});
heavyLifting('Node.js', function (error, result) { ... });
see full code
Windows authentication
Use Windows authentication to authenticate calls to your Node.js HTTP server
var authenticate = require('edge').func(function() {/*
class Startup {
[DllImport("advapi32.dll")] static extern bool LogonUser(...);
public async Task<object> Invoke(dynamic i) {
IntPtr t;
return Startup.LogonUser(i.user, null, i.password, 3, 0, out t))
}
}
*/});
authenticate({
user: 'tjanczuk@redmond.corp.microsoft.com',
password: 'foobar'
}, function (error, result) { ... });
see full code
ZIP compression
Compress files and folders using .NET 4.5 ZIP compression
var zip = require('edge').func(function() {/*
class Startup {
public async Task<object> Invoke(dynamic p) {
await Task.Run(async () => {
System.IO.Compression.ZipFile.CreateFromDirectory(
(string)p.src, (string)p.dest);
});
return null;
}
}
*/});
zip({ src: '..\\mydir', dest: '..\\mydir.zip' }, function (error) {
...
});
see full code
Image conversion
Convert images between JPG, BMP, TIFF, PNG, and GIF
var convertImageToJpg = require('edge').func(function() {/*
#r "System.Drawing.dll"
using System.Drawing;
async (src) => {
await Task.Run(async () => {
Image.FromFile(src).Save((string)src + ".jpg", ImageFormat.Jpeg);
});
return null;
}
*/});
convertImageToJpg('.\\edge.png', function (error) { ... });
see full code
Express.js handler in C#
Plug in .NET OWIN apps as connect middleware or express handlers
var owin = require('connect-owin')
, express = require('express');
var app = express();
app.get('/net', owin('Edge.Samples.dll'));
app.get('/node', function (req, res) {
res.send(200, 'Hello from JavaScript!');
});
app.listen(3000);
bbaia/connect-owin
Call legacy SOAP services from Node.js
Use WCF to call SOAP services
var edge = require('edge');
var kg2pound = edge.func('soap.csx');
kg2pound(123, function (error, result) { ... });
see full code
Call legacy SOAP services from Node.js, continued
Use WCF proxy to call a SOAP service; soap.csx:
// ...
public class Startup {
public async Task<object> Invoke(double kilograms) {
var client = new ConvertWeightsSoapClient(
new BasicHttpBinding(),
new EndpointAddress(
"http://www.webservicex.net/ConvertWeight.asmx"));
return await client.ConvertWeightAsync(
kilograms, WeightUnit.Kilograms, WeightUnit.PoundsTroy);
}
}
see full code
Intermission
Before Act II begins, you may enjoy a moment with art
Act II
Where .NET scripts Node.js in-process (NEW!)
docsScript Node.js from .NET
Asynchronously call Node.js functions in .NET
using EdgeJs;
var func = Edge.Func(@"
return function (data, cb) {
cb(null, 'Node.js ' + process.version + ' welcomes ' + data);
}
");
Console.WriteLine(await func(".NET"));
more
...In console applications...
...In ASP.NET web applications...
Use built-in modules
Core Node.js modules are included with Edge.js
using EdgeJs;
async void Start() {
var createHttpServer = Edge.Func(@"
var http = require('http');
return function (port, cb) {
http.createServer(function (req, res) {
res.end('Hello, world! ' + new Date());
}).listen(port, cb);
};
");
await createHttpServer(8080);
}
more
Use NPM modules
Additional Node.js modules can be installed from NPM
> npm install ws ... ws@0.4.31 node_modules\ws
using EdgeJs;
var createWebSocketServer = Edge.Func(@"
var WebSocketServer = require('ws').Server;
return function (port, cb) {
var wss = new WebSocketServer({ port: port });
wss.on('connection', function (ws) { /* ... */ });
cb();
};
");
more
Expose Node.js state
Wrap Node.js state in a closure
using EdgeJs;
var increment = Edge.Func(@"
var current = 0;
return function (data, callback) {
current += data;
callback(null, current);
}
");
Console.WriteLine(await increment(4)); // outputs 4
Console.WriteLine(await increment(7)); // outputs 11
more
Export Node.js functions
using EdgeJs;
var createServer = Edge.Func(@"
var http = require('http');
return function (port, cb) {
var server = http.createServer(...);
cb(null, function (data, cb) {
server.close(); cb();
});
}
");
var closeServer = (Func<object,Task<object>>)await createServer(8080);
// ...
await closeServer(null);
more
.NET handlers for Node.js events
var onMessage = async (msg) => { return ((string)msg).ToUpper(); };
var createWebSocketServer = Edge.Func(@"
var WebSocketServer = require('ws').Server;
return function (params, cb) {
var wss = new WebSocketServer({ port: params.port });
wss.on('connection', function (ws) {
ws.on('message', function (msg) {
params.onMessage(msg, function (error, res) { ws.send(res); });
});
});
cb();
};");
await createWebSocketServer(new { port = 8080, onMessage = onMessage });
more
Edge.js
Brought to you by Tomasz Janczuk / @tjanczuk
npm install edge
Collaboration welcome