Edge.js

Run node.js and .NET code in-process

 

An edge connects two nodes

This edge connects node.js with .NET

 

Created by Tomasz Janczuk / @tjanczuk

 

(Use space or arrow keys to navigate)

Edge.js connects

30k+ npm modules with 11k+ nuget packages

JavaScript with C#, F#, Python, and PowerShell

loose typing with strong typing

IO-bound single threaded with CPU-bound multi-threaded

cool with awesome

in one process

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

c:\projects\edgedemo> node sample.js
CSharp welcomes Node.js
          

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

c:\projects\edgedemo> 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

c:\projects\edgedemo> 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


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
          

Interop model

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

edge.js interop model

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 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
...
            
how to support other CLR languages

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 (intput) => {
        ...
    }
*/});
            

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

edge.js debugging

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

performance of edge.js

details

Let's build something...

Access SQL from node.js with ADO.NET


var sql = require('edge').func('sql.csx');

sql('select top 2 * from Products', function (error, result) {
  if (error) throw error;
  console.log(result);
});            
         
see full code

Access SQL from node.js, continued

sql.csx:


#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() {
    ...
         
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

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

WebSockets in .NET

Expose WebSockets to .NET using Edge.js, including on Windows Server 2008 or Windows 7


var createMyNetWebSocket = require('edge').func(function () {/*
  // .NET WebSocket application in C# - see full code 
*/})

var wss = new require('ws').Server(...);

wss.on('connection', function(ws) {
    var onMessage = createMyNetWebSocket(ws.send, true);
    ws.on('message', onMessage);
});

wss.listen(8080);
         
see full code

Express.js handler in C#

Plug in .NET OWIN apps as connect middleware or express handlers


var edge = require('edge-express')
    , express = require('express');

var app = express();
app.use(express.bodyParser());
app.get('/net', edge('Edge.Samples.dll'))
app.get('/node', function (req, res) {
    res.send(200, 'Hello from JavaScript!');
});

app.listen(3000);
         

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

What will you build?

Integrate with ASP.NET authentication ◆ Public key cryptography ◆ Get X.509 certificates from certificate store ◆ Access camera, microphone, and speakers ◆ Print documents ◆ Write to Event Log ◆ Use Event Tracing for Windows ◆ Bridge to nuget package manager ◆ Interact with external services using SOAP and WCF ◆ Active Directory ◆ Call services that require Windows authentication ◆ Access MS SQL ◆ Implement CPU-bound workers ◆ Process images

and more ...

Edge.js

Created by Tomasz Janczuk / @tjanczuk

github.com/tjanczuk/edge

npm install edge

Collaboration welcome

Fork me on GitHub