bling.github.io

This blog has relocated to bling.github.io.

Friday, June 8, 2012

N2N: .NET 2 Node: Part 1

Let’s get some actual coding done.  If you want some basic prologue, check out the first part of my series here.
Anywho, let’s get to the meat of what we’re trying to accomplish.  I will start with the typical C# class you would write, do the same in Javascript, and then again in C#, but using the style of Javascript using closures.
Here’s the use case:
  • Handle a web request
  • Check the MongoDB database to see if an entry exists and return it if available.
  • Otherwise, query an external API for data, save it into MongoDB, and then return that.

This is a pretty standard straight-forward approach in web applications.  If you’re not using MongoDB, you’ll be using memcache, Redis, or some other equivalent data store.

First, let’s implement an pseudo-code ASP.NET MVC controller which does the above:

public class QuoteController : Controller
{
MongoCollection collection;
IExternalApi external;

public ActionResult Get(string symbol)
{
var quote = this.collection.FindOne(new { symbol });
if (quote == null)
{
var json = this.external.GetQuote(symbol);
this.collection.Save(json);
}

ViewBag.Quote = quote;
return View();
}
}

Error handling and initialization are omitted for brevity, but it’s an otherwise straight-forward synchronous send JSON to the browser response.  So how does something like this look in Node?  First, let’s throw in a web framework.  I picked Express because it seems to have the most traction right now.  Again, for brevity I will omit a bunch of boot strapping code, and skip to the core logic.

var QuoteService = function() {
var mongoCollection; // new MongoCollection
var external; // new External API

// req/res are the request/response passed in by Node/Express
this.get = function(symbol, req, res) {
mongoCollection.findOne({ symbol: symbol }, function(error, item) {
if (item === null) {
external.getQuote(symbol, function(error, result) {
mongoCollection.save(result);
req.write(result);
req.end();
});
} else {
req.write(item);
req.end();
}
});
};
};
module.exports = QuoteService;


OK, looks a lot different!  The first thing you’ll notice is that the class is actually a function.  Javascript doesn’t have real classes like C# or Java, but you can simulate things like private and public members by using closures.  In the code snippet above, mongoCollection and external are private variables.  However, the get function is public because it’s prefixed with this.  Finally, because Javascript has closures you can still access the private variables and retain their state.

Last but not least, as far as programming in Node is concerned, every function returns void.  Node is extremely fast – but it is still single threaded, which means you need to take extreme care not to block on any operation.  In summary, you need to get used to working with callbacks.  In the example above, the callback for the Mongo query invokes another function, which again uses a callback for the result.  Typically any errors are also passed through to the callback, so in place of try/catch blocks, you will be checking to see if the error parameter has a value.

To conclude, let’s write a C# version that mimics the Javascript style.

public static void Main(string[] args)
{
var QuoteService = new Func<dynamic>(() => {
var mongoCollection = new MongoCollection();
var external = new IExternalApi();
return new
{
get = new Action<string, Request, Response>((symbol, req, res) =>
{
mongoCollection.FindOne(new{symbol}, (error, item) => {
if (item == null)
{
external.GetSymbol(symbol, (e, result) => {
mongoCollection.Save(result);
req.Write(result);
req.End();
});
}
else
{
req.Write(item);
req.End();
}
});
})
}});
}

100% valid C# syntax.

Would you ever want to do that in C#?  Probably not.  But if you need some mental conversion from C# to/from Javascript, I think it’s a good place to start.

In summary, Javascript is kind of like programming in C# using only anonymous classes, Action/Func, dynamic, declared all in the main method.

Sunday, June 3, 2012

N2N: .NET 2 Node

Well, it’s been quite a while since I’ve blogged about…well…anything, and I figured it’s about time I get off my lazy butt and do something with my spare time on weekends.  What better option than to see what all the hype is about Node?  I had to do it sooner or later.

As any newbie would do, they go to Google and type “nodejs tutorial”.  The Node Beginner Book came up first, so I went with that.  It was an excellent tutorial.  Prior to this I also skimmed through the book JavaScript, The Good Parts, so I had a basic understanding of the language syntax.

One of the first oddities I noticed, was that NodeJS seems to have a convention of comma-first.  You notice this immediately because most examples start with require(‘module’), and if they require more than one module, the second line is prefixed with a comma (as opposed to the more traditional comma at the end of the line).  I apparently missed the discussion by 2 years!  It was still interesting nonetheless.

As someone with a strong .NET background, I definitely experienced all the usual ‘gotchas’:

  • == vs ===
  • falsey values
  • variable hoisting

Once you understand all of these things, Javascript isn’t so bad.  Oh, and of course understanding closures will get you a long way in being effective with Javascript, because that’s what you need to use to do proper scoping.  If C# didn’t have lambdas and closures it would have been a much longer journey to “get it”.

Not too longer after, I deployed my first Heroku app running on NodeJS.

Anyways, enough with the prologue…I won’t bore you with anymore beginner/tutorial stuff.

Let’s get on with what I plan on doing over a multi-part blog series.  When I build something on my own time, I can’t build something just for the hell of it to learn something….that’s not enough.  If I build something it has to be useful – something that I (or someone else) will find valuable.

I won’t reveal what it is yet, but it’s going to involve Node/MongoDB on the backend, with Backbone on the front-end.  Should be fun :-)