On the

Edge

of Node.js and CLR

 

Tomasz Janczuk / @tjanczuk

The new Babel

Source: Donnie Berkholz's Story of Data

When Node.js is not enough

 

cpu-bound work

legacy technology constraint

reuse existing components

utilize better implementation

child_process.exec

 

added complexity

added latency

 

There has to be a better way

.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...

Edge.js on Windows

...On Mac...

Edge.js on MacOS

...and on Linux

Edge.js on Linux

Interop model

Async, in-process calling convention between CLR and Node.js

Edge.js interop model

This is the better way

 

one process

32x times faster

 

Edge.js

details

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
          

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
          

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

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 f1 = edge.func('async (i) => { return ((string)i).ToUpper(); }');

var f2 = edge.func(function () {/*
    async (i) => { 
        return ((string)i).ToUpper(); 
    }
*/});

var f3 = edge.func('mysample.cs');
            
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 (intput) => {
        ...
    }
*/});
            

Data from Node.js to C#

Pass data from Node.js to C#


var hello = require('edge').func(...);

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

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) => { 
            return ".NET welcomes " + i.ToString();
        });
    }
*/});

createHello(null, function (error, hello) {
    hello('Node.js', console.log); // prints out ".NET welcomes Node.js"
}); 
          
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

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

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

Art break

Salvador Dali, Sleep, 1937

Let's build something...

Shameless plug

Run custom authorizaton rules in JavaScript or C#

Identity as a service company

ZIP compression

Compress files and folders using .NET 4.5 ZIP compression


var zip = require('edge').func(function() {/*
  async (dynamic input) => {
    await Task.Run(async () => {
      System.IO.Compression.ZipFile.CreateFromDirectory(
        (string)input.src, (string)input.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

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)

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

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
          

Edge.js connects

Node.js and .NET

cool with awesome

on Windows, MacOS, and Linux

in one process

Tomasz Janczuk / @tjanczuk

github.com/tjanczuk/edge

What will you build?

Fork me on GitHub
Follow @tjanczuk