edge.js verbindet Node.js und .NET Framework in einem Prozess

Seite 3: Komplexe Datentypen & Debugging

Inhaltsverzeichnis

Die bisherigen Beispiele basieren auf primitiven Datentypen. edge.js kann aber auch komplexere Datentypen zwischen JavaScript und .NET-Sprachen austauschen. Verwendbar sind alle .NET-Klassen, die sich in das JSON-Format serialisieren lassen. Um einen Austausch binärer Daten zu ermöglichen, unterstützt edge.js zusätzlich den Austausch von Node.js-Buffer-Objekten, die unter .NET als Byte-Array (byte[]) nutzbar sind. Alle JavaScript-Objekte stehen unter .NET in der Form IDictionary<string,object> bereit. JavaScript-Arrays werden unter .NET als Objekt-Array (object[]) repräsentiert. Die primitiven Datentypen konvertiert edge.js in die korrespondierenden Datentypen beider Umgebungen, zum Beispiel System.Int32 in number.

Die folgenden beiden Listings zeigen die Übergabe eines JavaScript-Objekts an C#. Zuerst die komplexe Datenübergabe aus JavaScript:

var charpFunktion = edge.func('Edge.Beispiel.dll');

var payload = {
eineGanzZahl: 1,
eineGleitkommaZahl: 3.1415,
einText: 'foo',
einWahrheitsWert: true,
einPuffer: new Buffer(10),
einArray: [ 1, 'foo' ],
einObjekt: { a: 'foo', b: 12 }
};

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

Und dann eine C#-Klasse, die das JavaScript-Objekt aus dem vorigen Listing empfängt:

using System.Collections.Generic;
using System.Threading.Tasks;

namespace Edge.Beispiel
{
public class Startup
{
public async Task<object> Invoke(object input)
{
IDictionary<string, object> payload =
(IDictionary<string,object>)input;
int eineGanzZahl = (int)payload["eineGanzZahl"];
double eineGleitkommaZahl =
(double)payload["eineGleitkommaZahl"];
string einText = (string)payload["einText"];
bool einWahrheitWert = (bool)payload["einWahrheitWert"];
byte[] einPuffer = (byte[])payload["einPuffer"];
object[] eineArray = (object[])payload["einArray"];
IDictionary<string, object> einObjekt =
(IDictionary<string,object>)payload["einObjekt"];

return null;
}
}
}

Auch .NET-Fehler in Form von Exception-Objekten werden serialisiert und – wie schon im ersten Listing gezeigt – auf JavaScript-Exception-Objekte abgebildet, sodass der JavaScript-Entwickler dieses Objekt wie gewohnt auswerten kann. Dabei verwendet edge.js eine Wrapper-Klasse für die Exception, die sowohl den .NET-Typen des Fehlers als auch den Stacktrace von .NET an Node.js weiterreicht.

Für die Übergabe der Daten aus .NET nach JavaScript gilt das gleiche Verhalten. Eine Herausforderung soll hier nicht außen vor gelassen werden. Bei der Konvertierung der Daten von .NET nach JavaScript sowie in der umgekehrten Richtung findet keine Prüfung auf zirkuläre Referenzen statt. In beiden Fällen wird das Programm mit einer Stack-Overflow-Ausnahme beendet.

Um zu steuern, dass nicht alle Attribute einer .NET-Klasse an Node.js übergeben worden sind, kann der .NET-Entwickler die Annotation [System.Web.Script.Serialization.ScriptIgnoreAttribute] einsetzen. Dazu muss er dann aber auch für den Prozess, in dem edge.js verwendet wird, die Umgebungsvariable EDGE_ENABLE_SCRIPTIGNOREATTRIBUTE auf den Wert "1" setzen. Im Standard ist EDGE_ENABLE_SCRIPTIGNOREATTRIBUTE aus Leistungsgründen auf den Wert "0" gesetzt. Im umgekehrten Fall gibt es nur die Möglichkeit, ein neues Objekt zu erstellen, das die zu übertragenden Felder enthält. Das ist notwendig, da JavaScript keine Analogie zu den .NET-Attributen kennt.

Für asynchrone Programmierung braucht man Callbacks und damit die Möglichkeit, Funktionen an Parameter zu übergeben. Das können sowohl JavaScript als auch die gängigen .NET-Sprachen. Wie erwähnt, erwartet Node.js, dass die aufgerufene .NET-Methode die Signatur Func<object,Task<object>> besitzt. Umgekehrt erwartet .NET, dass eine von Node.js kommende Callback-Methode als ersten Parameter ein Error-Objekt und als zweiten Parameter das Ergebnis entgegennimmt. Die beiden folgenden Listings zeigen ein Beispiel für einen Rückruf von .NET zu JavaScript. Zuerst ein Beispiel zur Verwendung eines Callback aus Node.js heraus:

var edge = require('edge');

var multipliziereMit2 = edge.func('Edge.Beispiel.dll');

var payload = {
hinzufuegen: function (daten, callback) {
callback(null, daten.a + daten.b);
}
};

multipliziereMit2(payload, function (fehler, ergebnis) {
if (fehler) throw fehler;
console.log(ergebnis);
})

Dann der .NET-Code zur Verwendung des Node.js-Callbacks:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Edge.Beispiel.Multi
{
public class Startup
{
public async Task<object> Invoke(IDictionary<string, object> input)
{
Func<object, Task<object>> hizufuegen = (Func<object,
Task<object>>)input["hinzufuegen"];
var zweiNummern = new{a = 2, b = 3};
var hinzufuegenErgebnis = (int)await hinzufuegen(zweiNummern);
return hinzufuegenErgebnis * 2;
}
}
}

Es ist auch möglich, dass die aufgerufene .NET-Methode einen Funktionszeiger (mit Signatur Func<object,Task<object>>) liefert, den JavaScript dann, wie im folgenden Listing zu sehen ist, wieder für asynchrone oder synchrone (mit zweitem Parameter "true") Aufrufe verwenden kann.

var zaehlerErzeugen = edge.func(function () {/*
async (input) =>
{
var k = (int)input;
return (Func<object,Task<object>>)(async (i) => { return ++k; });
}
*/});

var zaehler = zaehlerErzeugen(12, true); // erzeugt einen Zähler mit dem
// initialen Wert von 12
console.log(zaehler(null, true)); // Ausgabe 13
console.log(zaehler(null, true)); // Ausgabe 14

Wer die Debugging-Möglichkeiten von Visual Studio kennen gelernt hat, wird den Wunsch haben, diesen komfortablen Debugger auch in Verbindung mit edge.js einzusetzen. Hierbei ist es am einfachsten, wenn der .NET-Code sich in einer separaten DLL/Assembly befindet. Sind diese Voraussetzungen gegeben, lässt sich unter den Debugging-Einstellung des .NET-Projekts eine "externe Anwendung", in diesem Fall node.exe, als zu startende Anwendung auswählen. Anschließend ist noch das Arbeitsverzeichnis auf den richtigen Pfad zu
setzen (in Abbildung 4 ist dies der Eintrag c:\dev\heise_edgejs\debug) und als Befehlszeilenargument die JavaScript-Datei anzugeben, die zum Starten der Anwendung dient
(Beispiel: debug_node.js).

Debugging-Einstellungen in Visual Studio (Abb. 4)

Die Abbildung 4 zeigt den Dialog die Einstellung für Debugging in Visual Studio, während in Abbildung 5 die Programmausführung bei einem Haltepunkt zum Stillstand gebracht wurde. Leider lassen sich von Node.js ausgeführte JavaScript-Dateien derzeit noch nicht debuggen. Die Entwicklung von Visual Node, einer Node.js-Integration in Visual Studio, wird hier Abhilfe schaffen. Aktuell ist das Werkzeug der Firma Red Gate Software angekündigt, aber noch nicht verfügbar.

Debugging des von Node.js aufgerufenen .NET-Codes innerhalb von Visual Studio (Abb. 5)