Using Edge.js to combine Node.js and .NET

Posted by: Ravi Kiran , on 10/6/2015, in Category Node.js
Views: 32080
Abstract: Explore some basic examples of Edge.js and see how Edge.js can be used to interact with .NET and SQL Server using Node.js.

Today, we have an increasing number of technology platforms available to perform similar tasks. Each of these platforms are great in some areas though most of them are pretty close to each other when we take industry needs into consideration. It is always a challenge to make two different platforms work together. When we try to make it happen, we end up with putting a lot of our time and energy in making the binaries of the platforms compatible, finding best ways to hook them together and deploying the application, using a hybrid platform setup. But if we have frameworks or, tools which does the heavy lifting of making the platforms work together, our lives would be much simpler.

 

Node.js is gaining a lot of popularity these days because it is unifying the language used on both server and client side. Though the platform proved that it can do almost everything that any other server platform based on established platforms like .NET and Java can do, it still lacks certain features that the other platforms do with ease. One of them is interacting with SQL databases. Though there are a few NPM packages to interact with SQL databases using Node.js, it is not easy to use them as they seem incomplete for the Node environment. Edge.js provides a solution to this problem by making it possible to call .NET functions from Node.js.

In this article, we will go through some basic examples of Edge.js and see how Edge.js can be used to interact with SQL Server using Node.js.

Note: This article assumes that you are aware of basics of Node.js. If you are new to it, please read the articles based on Node.js basics on dotnetcurry site.

This article is published from the DNC Magazine for .NET Developers and Architects. Download this magazine from here [Zip PDF] or Subscribe to this magazine for FREE and download all previous and current editions

Getting Familiar with Edge.js

To bring .NET and Node.js together, Edge.js has some pre-requisites. It runs on .NET 4.5, so you must have .NET 4.5 installed. As Node.js treats all I/O and Network calls as slower operations, Edge.js assumes that the .NET routine to be called is a slower operation and handles it asynchronously. The .NET function to be called has to be an asynchronous function as well.

The function is assigned to a delegate of type Func<object, Task<object>>. This means, the function is an asynchronous one that can take any type of argument and return any type of value. Edge.js takes care of converting the data from .NET type to JSON type and vice-versa. Because of this process of marshalling and unmarshalling, the .NET objects should not have circular references. Presence of circular references may lead to infinite loops while converting the data from one form to the other.

Hello World using Edge

Edge.js can be added to a Node.js application through NPM. Following is the command to install the package and save it to package.json file:

> npm install edge --save

The edge object can be obtained in a Node.js file as:

var edge = require('edge');

The edge object can accept inline C# code, read code from a .cs or .csx file, and also execute the code from a compiled dll. We will see all of these approaches.

To start with, let’s write a “Hello world” routine inline in C# and call it using edge. Following snippet defines the edge object with inline C# code:

var helloWorld = edge.func(function () {
  /*async(input) => {
    return "Hurray! Inline C# works with edge.js!!!";
  }*/
});

The asynchronous and anonymous C# function passed in the above snippet is compiled dynamically before calling it. The inline code has to be passed as a multiline comment. The method edge.func returns a proxy function that internally calls the C# method. So the C# method is not called till now. Following snippet calls the proxy:

helloWorld(null, function(error, result) {
  if (error) {
    console.log("Error occured.");
    console.log(error);
    return;
  }
  console.log(result);
});

In the above snippet, we are passing a null value to first parameter of the proxy as we are not using the input value. The callback function is similar to any other callback function in Node.js accepting error and result as parameters.

We can rewrite the same Edge.js proxy creation by passing the C# code in the form of a string instead of a multiline comment. Following snippet shows this:

var helloWorld = edge.func(
  'async(input) => {'+
    'return "Hurray! Inline C# works with edge.js!!!";'+
  '}'
);

We can pass a class in the snippet and call a method from the class as well. By convention, name of the class should be Startup and name of the method should be Invoke. The Invoke method will be attached to a delegate of type Func<object, Task<object>>. The following snippet shows usage of class:

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

  public class Startup
  {        
    public async Task<object> Invoke(object input)
    {
      return "Hurray! Inline C# class works with edge.js!!!";
    }
  } */
});

It can be invoked the same way we did previously:

helloFromClass(10, function (error, result) {
  if(error){
    console.log("error occured...");
    console.log(error);
    return;
  }
  console.log(result);
});

A separate C# file

Though it is possible to write the C# code inline, being developers, we always want to keep the code in a separate file for better organization of the code. By convention, this file should have a class called Startup with the method Invoke. The Invoke method will be added to the delegate of type Func<object, Task<object>>.

Following snippet shows content in a separate file, Startup.cs:

using System.Threading.Tasks;

public class Startup
{
    public async Task<object> Invoke(object input)
    {
        return new Person(){
            Name="Alex",
            Occupation="Software Professional",
            Salary=10000,
            City="Tokyo"
        };
    }
}

public class Person{
    public string Name { get; set; }
    public string Occupation { get; set; }
    public double Salary { get; set; }
    public string City { get; set; }
}

Performing CRUD Operations on SQL Server

Now that you have a basic idea of how Edge.js works, let’s build a simple application that performs CRUD operations on a SQL Server database using Entity Framework and call this functionality from Node.js. As we will have a considerable amount of code to setup Entity Framework and perform CRUD operations in C#, let’s create a class library and consume it using Edge.js.

Creating Database and Class Library

As a first step, create a new database named EmployeesDB and run the following commands to create the employees table and insert data into it:

CREATE TABLE Employees(
  Id INT IDENTITY PRIMARY KEY,
  Name VARCHAR(50),
  Occupation VARCHAR(20),
  Salary INT,
  City VARCHAR(50)
);

INSERT INTO Employees VALUES
  ('Ravi', 'Software Engineer', 10000, 'Hyderabad'),
  ('Rakesh', 'Accountant', 8000, 'Bangalore'),
  ('Rashmi', 'Govt Official', 7000, 'Delhi');

Open Visual Studio, create a new class library project named EmployeesCRUD and add a new Entity Data Model to the project pointing to the database created above. To make the process of consuming the dll in Edge.js easier, let’s assign the connection string inline in the constructor of the context class. Following is the constructor of context class that I have in my class library:

public EmployeesModel()
    : base("data source=.;initial catalog=EmployeesDB;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework;")
{
}

Add a new class to the project and name it EmployeesOperations.cs. This file will contain the methods to interact with Entity Framework and perform CRUD operations on the table Employees. As a best practice, let’s implement the interface IDisposable in this class and dispose the context object in the Dispose method. Following is the basic setup in this class:

public class EmployeesOperations : IDisposable
{
    EmployeesModel context;
 
    public EmployeesOperations()
    {
        context = new EmployeesModel();
    }
        public void Dispose()
    {
        context.Dispose();
    }
}

As we will be calling methods of this class directly using Edge.js, the methods have to follow signature of the delegate that we discussed earlier. Following is the method that gets all employees:

public async Task<object> GetEmployees(object input)
{
    return await context.Employees.ToListAsync();
}

There is a challenge with the methods performing add and edit operations, as we need to convert the input data from object to Employee type. This conversion is not straight forward, as the object passed into the .NET function is a dynamic expando object. We need to convert the object into a dictionary object and then read the values using property names as keys. Following method performs this conversion before inserting data into the database:

public async Task<object> AddEmployee(object emp)
{
    var empAsDictionary = (IDictionary<string, object>)emp;
    var employeeToAdd = new Employee() { 
        Name = (string)empAsDictionary["Name"],
        City = (string)empAsDictionary["City"],
        Occupation = (string)empAsDictionary["Occupation"],
        Salary = (int)empAsDictionary["Salary"]
    };

    var addedEmployee = context.Employees.Add(employeeToAdd);

    await context.SaveChangesAsync();

    return addedEmployee;
}

The same rule applies to the edit method as well. It is shown below:

public async Task<object> EditEmployee(object input)
{
    var empAsDictionary = (IDictionary<string, object>)input;
    var id = (int)empAsDictionary["Id"];

    var employeeEntry = context.Employees.SingleOrDefault(e => e.Id == id);

    employeeEntry.Name = (string)empAsDictionary["Name"];
    employeeEntry.Occupation = (string)empAsDictionary["Occupation"];
    employeeEntry.Salary = (int)empAsDictionary["Salary"];
    employeeEntry.City = (string)empAsDictionary["City"];

    context.Entry(employeeEntry).State = System.Data.Entity.EntityState.Modified
    
    return await context.SaveChangesAsync();
}

We will compose REST APIs using Express.js and call the above functions inside them. Before that, we need to make the compiled dll of the above class library available to the Node.js application. We can do it by building the class library project and copying the result dlls into a folder in the Node.js application.

 

Creating Node.js Application

Create a new folder in your system and name it ‘NodeEdgeSample’. Create a new folder ‘dlls’ inside it and copy the binaries of the class library project into this folder. You can open this folder using your favorite tool for Node.js. I generally use WebStorm and have started using Visual Studio Code these days.

Add package.json file to this project using “npm init” command (discussed in Understanding NPM article) and add the following dependencies to it:

"dependencies": {
  "body-parser": "^1.13.2",
  "edge": "^0.10.1",
  "express": "^4.13.1"
}

Run NPM install to get these packages installed in the project. Add a new file to the project and name it ‘server.js’. This file will contain all of the Node.js code required for the application. First things first, let’s get references to all the packages and add the required middlewares to the Express.js pipeline. Following snippet does this:

var edge = require('edge');
var express = require('express');
var bodyParser = require('body-parser');

var app = express();

app.use('/', express.static(require('path').join(__dirname, 'scripts')));

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

Now, let’s start adding the required Express REST APIs to the application. As already mentioned, the REST endpoints will interact with the compiled dll to achieve their functionality. The dll file can be referred using the edge.func function. If type and method are not specified, it defaults class name as Startup and method name as Invoke. Otherwise, we can override the class and method names using the properties in the object passed into edge.func.

Following is the REST API that returns list of employees:

app.get('/api/employees', function (request, response) {
   var getEmployeesProxy = edge.func({
      assemblyFile: 'dlls\\EmployeeCRUD.dll',
      typeName: 'EmployeeCRUD.EmployeesOperations',
      methodName: 'GetEmployees'
   });

   getEmployeesProxy(null, apiResponseHandler(request, response));
});

The function apiResponseHandler is a curried generic method for all the three REST APIs. This function returns another function that is called automatically once execution of the .NET function is completed. Following is the definition of this function:

function apiResponseHandler(request, response) {
   return function(error, result) {
      if (error) {
         response.status(500).send({error: error});
         return;
      }

      response.send(result);
   };
}

Implementation of REST APIs for add and edit are similar to the one above. The only difference is, they pass an input object to the proxy function.

app.post('/api/employees', function (request, response) {
   var addEmployeeProxy = edge.func({
      assemblyFile:"dlls\\EmployeeCRUD.dll",
      typeName:"EmployeeCRUD.EmployeesOperations",
      methodName: "AddEmployee"
   });

   addEmployeeProxy(request.body, apiResponseHandler(request, response));
});

app.put('/api/employees/:id', function (request, response) {
   var editEmployeeProxy = edge.func({
      assemblyFile:"dlls\\EmployeeCRUD.dll",
      typeName:"EmployeeCRUD.EmployeesOperations",
      methodName: "EditEmployee"
   });

   editEmployeeProxy(request.body, apiResponseHandler(request, response));
});

Consuming APIs on a Page

The final part of this tutorial is to consume these APIs on an HTML page. Add a new HTML page to the application and add bootstrap CSS and Angular.js to this file. This page will list all the employees and provide interfaces to add new employee and edit details of an existing employee. Following is the mark-up on the page:

<!doctype html>
<html>
<head>
   <title>Edge.js sample</title>
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"/>
</head>
<body ng-app="edgeCrudApp">
   <div class="container" ng-controller="EdgeCrudController as vm">
      <div class="text-center">
         <h1>Node-Edge-.NET CRUD Application</h1>
         <hr/>
         <div class="col-md-12">
            <form name="vm.addEditEmployee">
               <div class="control-group">
                  <input type="text" ng-model="vm.employee.Name" placeholder="Name" />
                  <input type="text" ng-model="vm.employee.Occupation" placeholder="Occupation" />
                  <input type="text" ng-model="vm.employee.Salary" placeholder="Salary" />
                  <input type="text" ng-model="vm.employee.City" placeholder="City" />
                  <input type="button" class="btn btn-primary" ng-click="vm.addOrEdit()" value="Add or Edit" />
                  <input type="button" class="btn" value="Reset" ng-click="vm.reset()" />
               </div>
            </form>
         </div>
         <br/>
         <div class="col-md-10">
            <table class="table">
               <thead>
               <tr>
                  <th style="text-align: center">Name</th>
                  <th style="text-align: center">Occupation</th>
                  <th style="text-align: center">Salary</th>
                  <th style="text-align: center">City</th>
                  <th style="text-align: center">Edit</th>
               </tr>
               </thead>
               <tbody>
               <tr ng-repeat="emp in vm.employees">
                  <td>{{emp.Name}}</td>
                  <td>{{emp.Occupation}}</td>
                  <td>{{emp.Salary}}</td>
                  <td>{{emp.City}}</td>
                  <td>
                     <button class="btn" ng-click="vm.edit(emp)">Edit</button>
                  </td>
               </tr>
               </tbody>
            </table>
         </div>

      </div>
   </div>

   <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
   <script src="app.js"></script>
</body>
</html>

Add a new folder to the application and name it ‘scripts’. Add a new JavaScript file to this folder and name it ‘app.js’. This file will contain the client side script of the application. Since we are building an Angular.js application, the file will have an Angular module with a controller and a service added to it. Functionality of the file includes:

  • Getting list of employees on page load
  • Adding an employee or, editing employee using the same form
  • Resetting the form to pristine state once the employee is added or, edited

Here’s the code for this file:

(function(){
    var app = angular.module('edgeCrudApp', []);
    app.controller('EdgeCrudController', function (edgeCrudSvc) {
        var vm = this;

        function getAllEmployees(){
            edgeCrudSvc.getEmployees().then(function (result) {
                vm.employees = result;
            }, function (error) {
                console.log(error);
            });
        }

        vm.addOrEdit = function () {
            vm.employee.Salary = parseInt(vm.employee.Salary);
            if(vm.employee.Id) {
                edgeCrudSvc.editEmployee(vm.employee)
                    .then(function (result) {
                        resetForm();
                        getAllEmployees();
                    }, function (error) {
                        console.log("Error while updating an employee");
                        console.log(error);
                    });
            }
            else{
                edgeCrudSvc.addEmployee(vm.employee)
                    .then(function (result) {
                        resetForm();
                        getAllEmployees();
                    }, function (error) {
                        console.log("Error while inserting new employee");
                        console.log(error);
                    });
            }
        };

        vm.reset= function () {
            resetForm();
        };

        function resetForm(){
            vm.employee = {};
            vm.addEditEmployee.$setPristine();
        }

        vm.edit = function(emp){
            vm.employee = emp;
        };

        getAllEmployees();
    });

    app.factory('edgeCrudSvc', function ($http) {
        var baseUrl = '/api/employees';

        function getEmployees(){
            return $http.get(baseUrl)
                .then(function (result) {
                    return result.data;
                }, function (error) {
                    return error;
                });
        }

        function addEmployee(newEmployee){
            return $http.post(baseUrl, newEmployee)
                .then(function (result) {
                    return result.data;
                }, function (error) {
                    return error;
                });
        }

        function editEmployee(employee){
            return $http.put(baseUrl + '/' + employee.Id, employee)
                .then(function (result) {
                    return result.data;
                }, function (error) {
                    return error;
                });
        }

        return {
            getEmployees: getEmployees,
            addEmployee: addEmployee,
            editEmployee: editEmployee
        };
    });
}());

Save all the files and run the application. You should be able to add and edit employees. I am leaving the task of deleting employee as an assignment to the reader.

Conclusion

In general, it is challenging to make two different frameworks talk to each other. Edge.js takes away the pain of integrating two frameworks and provides an easier and cleaner way to take advantage of good features of .NET and Node.js together to build great applications. It aligns with the Node.js event loop model and respects execution model of the platform as well. Let’s thank Tomasz Jancjuk for his great work and use this tool effectively!

Download the entire source code of this article (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Rabi Kiran (a.k.a. Ravi Kiran) is a developer working on Microsoft Technologies at Hyderabad. These days, he is spending his time on JavaScript frameworks like AngularJS, latest updates to JavaScript in ES6 and ES7, Web Components, Node.js and also on several Microsoft technologies including ASP.NET 5, SignalR and C#. He is an active blogger, an author at SitePoint and at DotNetCurry. He is rewarded with Microsoft MVP (Visual Studio and Dev Tools) and DZone MVB awards for his contribution to the community


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!

Categories

JOIN OUR COMMUNITY

POPULAR ARTICLES

FREE .NET MAGAZINES

Free DNC .NET Magazine

Tags

JQUERY COOKBOOK

jQuery CookBook