• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

tjanczuk/edge: Run .NET and Node.js code in-process on Windows, MacOS, and Linux

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称(OpenSource Name):

tjanczuk/edge

开源软件地址(OpenSource Url):

https://github.com/tjanczuk/edge

开源编程语言(OpenSource Language):

C++ 77.7%

开源软件介绍(OpenSource Introduction):

Edge.js: .NET and Node.js in-process Build Status

NEW Edge.js is now on Slack at https://edgejs.slack.com. Join here.

An edge connects two nodes. This edge connects Node.js and .NET. V8 and CLR/.NET Core/Mono - in process. On Windows, MacOS, and Linux.

image

You can script C# from a Node.js process:

ES5

var edge = require('edge');

var helloWorld = edge.func(function () {/*
    async (input) => { 
        return ".NET Welcomes " + input.ToString(); 
    }
*/});

helloWorld('JavaScript', function (error, result) {
    if (error) throw error;
    console.log(result);
});

ES6

In ES6 you can use template strings to write multiline C# code.

var edge = require('edge');

var helloWorld = edge.func(`
    async (input) => { 
        return ".NET Welcomes " + input.ToString(); 
    }
`);

helloWorld('JavaScript', function (error, result) {
    if (error) throw error;
    console.log(result);
});

You can also script Node.js from C#:

using System;
using System.Threading.Tasks;
using EdgeJs;

class Program
{
    public static async Task Start()
    {
        var func = Edge.Func(@"
            return function (data, callback) {
                callback(null, 'Node.js welcomes ' + data);
            }
        ");

        Console.WriteLine(await func(".NET"));
    }

    static void Main(string[] args)
    {
        Start().Wait();
    }
}

What problems does Edge.js solve?

Ah, whatever problem you have. If you have this problem, this solves it.

--Scott Hanselman (@shanselman)

Before you dive in

See the Edge.js overview.
Read the Edge.js introduction on InfoQ.
Listen to the Edge.js podcast on Herdingcode.

image

Contents

Introduction
Scripting CLR from Node.js
    What you need
        Windows
        Linux
        OSX
        Docker
    How to: C# hello, world
    How to: integrate C# code into Node.js code
    How to: specify additional CLR assembly references in C# code
    How to: marshal data between C# and Node.js
    How to: call Node.js from C#
    How to: export C# function to Node.js
    How to: script Python in a Node.js application
    How to: script PowerShell in a Node.js application
    How to: script F# in a Node.js application
    How to: script Lisp in a Node.js application
    How to: script T-SQL in a Node.js application
    How to: support for other CLR languages
    How to: exceptions
    How to: app.config
    How to: debugging
    Performance
    Building on Windows
    Building on OSX
    Building on Linux
    Running tests
Scripting Node.js from CLR
    What you need
    How to: Node.js hello, world
    How to: integrate Node.js into CLR code
    How to: use Node.js built-in modules
    How to: use external Node.js modules
    How to: handle Node.js events in .NET
    How to: expose Node.js state to .NET
    How to: use Node.js in ASP.NET application
    How to: debug Node.js code running in a CLR application
    Building Edge.js NuGet package
    Running tests of scripting Node.js in C#
Use cases and other resources
Contribution and derived work

Introduction

Edge.js allows you to run Node.js and .NET code in one process on Windows, MacOS, and Linux.

You can call .NET functions from Node.js and Node.js functions from .NET. Edge.js takes care of marshalling data between CLR and V8. Edge.js also reconciles threading models of single threaded V8 and multi-threaded CLR. Edge.js ensures correct lifetime of objects on V8 and CLR heaps. The CLR code can be pre-compiled or specified as C#, F#, Python, or PowerShell source: Edge.js can compile CLR scripts at runtime. Edge can be extended to support other CLR languages or DSLs.

Edge.js interop model

Edge.js provides an asynchronous, in-process mechanism for interoperability between Node.js and .NET. You can use this mechanism to:

  • script Node.js from a .NET application (console app, ASP.NET, etc.)
  • script C# from a Node.js application on Windows, MacOS, and Linux
  • access MS SQL from Node.js using ADO.NET more...
  • use CLR multi-threading from Node.js for CPU intensive work more...
  • write native extensions to Node.js in C# instead of C/C++
  • integrate existing .NET components into Node.js applications

Read more about the background and motivations of the project here.

Follow @tjanczuk for updates related to the module.

Scripting CLR from Node.js

If you are writing a Node.js application, this section explains how you include and run CLR code in your app. It works on Windows, MacOS, and Linux.

What you need

Edge.js runs on Windows, Linux, and OSX and requires Node.js 8.x, 7.x, 6.x, as well as .NET Framework 4.5 (Windows), Mono 4.2.4 (OSX, Linux), or .NET Core 1.0.0 Preview 2 (Windows, OSX, Linux).

NOTE there is a known issue with Mono after 4.2.4 that will be addressed in Mono 4.6.

Windows

If you have both desktop CLR and .NET Core installed, read using .NET Core for how to configure Edge to use one or the other.

image

Linux

image

OSX

image

Docker

Edge.js is available as a Docker image on the tjanczuk/edgejs repository on Docker Hub. The image is based on Debian Trusty, and contains Node.js 6.3.0 x64, Mono 4.2.4 x64, .NET Core 1.0.0 Preview 2 x64 (dotnet-dev-1.0.0-preview2-003121), and Edge.js 6.5.1:

By default Edge uses Mono to execute CLR code:

> docker run -it tjanczuk/edgejs:6.5.1
> cd samples
> node 101_hello_lambda.js
.NET welcomes Node.js

Specify the EDGE_USE_CORECLR=1 environment variable to use .NET Core instead:

> docker run -it tjanczuk/edgejs:6.5.1
> cd samples
> EDGE_USE_CORECLR=1 node 101_hello_lambda.js
.NET welcomes Node.js

Alternatively, you can also specify EDGE_USE_CORECLR when starting the container:

> docker run -it -e EDGE_USE_CORECLR=1 tjanczuk/edgejs:6.5.1

How to: C# hello, world

Follow setup instructions for your platform.

Install edge:

npm install edge

In your server.js:

var edge = require('edge');

var helloWorld = edge.func(function () {/*
    async (input) => { 
        return ".NET Welcomes " + input.ToString(); 
    }
*/});

helloWorld('JavaScript', function (error, result) {
    if (error) throw error;
    console.log(result);
});

Run and enjoy:

$>node server.js
.NET welcomes JavaScript

If you want to use .NET Core as your runtime and are running in a dual runtime environment (i.e. Windows with .NET 4.5 installed as well or Linux with Mono installed), you will need to tell edge to use .NET Core by setting the EDGE_USE_CORECLR environment variable:

$>EDGE_USE_CORECLR=1 node server.js
.NET Welcomes JavaScript

How to: integrate C# code into Node.js code

Edge provides several ways to integrate C# code into a Node.js application. Regardless of the way you choose, the entry point into the .NET code is normalized to a Func<object,Task<object>> delegate. This allows Node.js code to call .NET asynchronously and avoid blocking the Node.js event loop.

Edge provides a function that accepts a reference to C# code in one of the supported representations, and returns a Node.js function which acts as a JavaScript proxy to the Func<object,Task<object>> .NET delegate:

var edge = require('edge');

var myFunction = edge.func(...);

The function proxy can then be called from Node.js like any asynchronous function:

myFunction('Some input', function (error, result) {
    //...
});

Alternatively, if you know the C# implementation will complete synchronously given the circumstances, you can call this function as any synchronous JavaScript function as follows:

var result = myFunction('Some input', true);

The true parameter instead of a callback indicates that Node.js expects the C# implementation to complete synchronously. If the CLR function implementation does not complete synchronously, the call above will result in an exception.

One representation of CLR code that Edge.js accepts is C# source code. You can embed C# literal representing a .NET async lambda expression implementing the Func<object,Task<object>> delegate directly inside Node.js code:

var add7 = edge.func('async (input) => { return (int)input + 7; }');

In another representation, you can embed multi-line C# source code by providing a function with a body containing a multi-line comment. Edge extracts the C# code from the function body using regular expressions:

var add7 = edge.func(function() {/*
    async (input) => {
        return (int)input + 7;
    }
*/});

Or if you use ES6 you can use template strings to define a multiline string:

var add7 = edge.func(`
    async (input) => {
        return (int)input + 7;
    }
`);

If your C# code is more involved than a simple lambda, you can specify entire class definition. By convention, the class must be named Startup and it must have an Invoke method that matches the Func<object,Task<object>> delegate signature. This method is useful if you need to factor your code into multiple methods:

var add7 = edge.func(function() {/*
    using System.Threading.Tasks;

    public class Startup
    {
        public async Task<object> Invoke(object input)
        {
            int v = (int)input;
            return Helper.AddSeven(v);
        }
    }

    static class Helper
    {
        public static int AddSeven(int v) 
        {
            return v + 7;
        }
    }
*/});

If your C# code grows substantially, it is useful to keep it in a separate file. You can save it to a file with *.csx or *.cs extension, and then reference from your Node.js application:

var add7 = edge.func(require('path').join(__dirname, 'add7.csx'));

If you integrate C# code into your Node.js application by specifying C# source using one of the methods above, edge will compile the code on the fly. If you prefer to pre-compile your C# sources to a CLR assembly, or if your C# component is already pre-compiled, you can reference a CLR assembly from your Node.js code. In the most generic form, you can specify the assembly file name, the type name, and the method name when creating a Node.js proxy to a .NET method:

var clrMethod = edge.func({
    assemblyFile: 'My.Edge.Samples.dll',
    typeName: 'Samples.FooBar.MyType',
    methodName: 'MyMethod' // This must be Func<object,Task<object>>
});

If you don't specify methodName, Invoke is assumed. If you don't specify typeName, a type name is constructed by assuming the class called Startup in the namespace equal to the assembly file name (without the .dll). In the example above, if typeName was not specified, it would default to My.Edge.Samples.Startup.

The assemblyFile is relative to the working directory. If you want to locate your assembly in a fixed location relative to your Node.js application, it is useful to construct the assemblyFile using __dirname. If you are using .NET Core, assemblyFile can also be a project name or NuGet package name that is specified in your project.json or .deps.json dependency manifest.

You can also create Node.js proxies to .NET functions specifying just the assembly name as a parameter:

var clrMethod = edge.func('My.Edge.Samples.dll');

In that case the default typeName of My.Edge.Samples.Startup and methodName of Invoke is assumed as explained above.

How to: specify additional CLR assembly references in C# code

When you provide C# source code and let edge compile it for you at runtime, edge will by default reference only mscorlib.dll and System.dll assemblies. If you're using .NET Core, we automatically reference the most recent versions of the System.Runtime, System.Threading.Tasks, System.Dynamic.Runtime, and the compiler language packages, like Microsoft.CSharp. In applications that require additional assemblies you can specify them in C# code using a special hash pattern, similar to Roslyn. For example, to use ADO.NET you must reference System.Data.dll:

var add7 = edge.func(function() {/*

    #r "System.Data.dll"

    using System.Data;
    using System.Threading.Tasks;

    public class Startup
    {
        public async Task<object> Invoke(object input)
        {
            // ...
        }
    }
*/});

If you prefer, instead of using comments you can specify references by providing options to the edge.func call:

var add7 = edge.func({
    source: function() {/*

        using System.Data;
        using System.Threading.Tasks;

        public class Startup
        {
            public async Task<object> Invoke(object input)
            {
                // ...
            }
        }
    */},
    references: [ 'System.Data.dll' ]
});

If you are using .NET Core and are using the .NET Core SDK and CLI, you must have a project.json file (specification here) that specifies the dependencies for the application. This list of dependencies must also include the Edge.js runtime package and, if you need to be able to dynamically compile your code, the package(s) for the compilers that you plan to use, like Edge.js.CSharp. You must have run the dotnet restore (to restore the dependencies) and dotnet build (to build your project and generate the dependency manifest) commands in that project's directory to generate a .deps.json file under bin/[configuration]/[framework], i.e. bin/Release/netstandard1.6/MyProject.deps.json. This .deps.json file must either be in the current working directory that node is executed in or you must specify its directory by setting the EDGE_APP_ROOT environment variable. For example, if for a netstandard1.6 project in the c:\DotNet\MyProject directory, you would run something like:

set EDGE_APP_ROOT=c:\DotNet\MyProject\bin\Release\netstandard1.6
node app.js

Edge.js also supports running published .NET Core applications on servers that do not have the .NET Core SDK and CLI installed, which is a common scenario in production environments. To do so, the project.json for your application should meet the following requirements:

  1. It should target the netcoreapp1.0 framework moniker.
  2. It should reference Microsoft.NETCore.DotNetHost and Microsoft.NETCore.DotNetHostPolicy. This is required so that the publish process can provide all the native libraries required to create a completely standalone version of your application.
  3. "emitEntryPoint": true should be present under buildOptions. You can add an empty Main() implementation to your project to accomodate it; this method will not be called, but is just a requirement in order for dotnet publish to generate a completely standalone app.

On your development machine, you would run dotnet publish -r [target runtime for your production server] (i.e. dotnet publish -r ubuntu.14.04-x64) to aggregate the package assemblies and native libraries necessary to run your application. You can copy the contents of the publish directory up to your SDK- and CLI-less server and use them directly in Edge.js by setting the EDGE_APP_ROOT environment variable to the directory on the server that you copied the published application to.

How to: marshal data between C# and Node.js

Edge.js can marshal any JSON-serializable value between .NET and Node.js (although JSON serialization is not used in the process). Edge also supports marshalling between Node.js Buffer instance and a CLR byte[] array to help you efficiently pass binary data.

You can call .NET from Node.js and pass in a complex JavaScript object as follows:

var dotNetFunction = edge.func('Edge.Sample.dll');

var payload = {
    anInteger: 1,
    aNumber: 3.1415,
    aString: 'foo',
    aBoolean: true,
    aBuffer: new Buffer(10),
    anArray: [ 1, 'foo' ],
    anObject: { a: 'foo', b: 12 }
};

dotNetFunction(payload, function (error, result) { });

In .NET, JavaScript objects are represented as dynamics (which can be cast to IDictionary<string,object> if desired), JavaScript arrays as object[], and JavaScript Buffer as byte[]. Scalar JavaScript values have their corresponding .NET types (int, double, bool, string). Here is how you can access the data in .NET:

using System.Threading.Tasks;

public class Startup
{
    public async Task<object> Invoke(dynamic input)
    {
        int anInteger = (int)input.anInteger;
        double aNumber = (double)input.aNumber;
        string aString = (string)input.aString;
        bool aBoolean = (bool)input.aBoolean;
        byte[] aBuffer = (byte[])input.aBuffer;
        object[] anArray = (object[])input.anArray;
        dynamic anObject = (dynamic)input.anObject;

        return null;
    }
}

Similar type marshalling is applied when .NET code passes data back to Node.js code. In .NET code you can provide an instance of any CLR type that would normally be JSON serializable, including domain specific types like Person or anonymous objects. For example:

using System.Threading.Tasks;

public class Person
{
    public int anInteger = 1;
    public double aNumber = 3.1415;
    public string aString = "foo";
    public bool aBoolean = true;
    public byte[] aBuffer = new byte[10];
    public object[] anArray = new object[] { 1, "foo" };
    public object anObject = new { a = "foo", b = 12 };
}

public class Startup
{
    public async Task<object> Invoke(dynamic input)
    {
        Person person = new Person();
        return person;
    }
}

In your Node.js code that invokes this .NET method you can display the result object that the callback method receives:

var edge = require('edge');

var getPerson = edge.func(function () {/*
    using System.Threading.Tasks;

    public class Person
    {
        public int anInteger = 1;
        public double aNumber = 3.1415;
        public string aString = "foo";
        public bool aBoolean = true;
        public byte[] aBuffer = new byte[10];
        public object[] anArray = new object[] { 1, "foo" };
        public object anObject = new { a = "foo", b = 12 };
    }

    public class Startup
    {
        public async Task<object> Invoke(dynamic input)
        {
            Person person = new Person();
            return person;
        }
    }
*/});

getPerson(null, function (error, result)  
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap