SignalR Tutorial with KnockoutJs



What we'll be doing

We're going to do a basic quickstart tutorial on building a realtime webappliction using SignalR. SignalR is hosted on GitHub and makes it easy to push messages from the server to the client browsers. We'll be simply flling out a form to add a story div to the current page, KnockoutJs and SignalR source code download is here.

SignalR Sceencast (see written tutorial below)

SignalR Tutorial

For this app you'll need Visual Studio 2010 with SP1 and Asp.Net MVC 3 package installed. We also assume you've got NuGet installed, if you haven't install NuGet.

Step 1 - Create an Empty Asp.Net MVC 3 App

Goto File --> New Project --> Web --> Asp.Net MVC 3 Application (Make sure it's .Net Framework 4). Call your project something interesting like SignalRTutorial .

Firstly let's just check everything compiles and we indeed have an empty web application.  We're going to break the tradition and make a Scrum board. This is partially to try and keep me more organised in the new year.

Step 2 - Add SignalR Package

Right click on references and click Manage NuGet Packages. Search for SignalR and click install. Just check everything compiles before we start.

Step 3 - Create a StoryCard Class

Minor part but this will be our simple DTO (Data Transfer Object) that we'll pass between the server and browser.

using SignalRTutorial.Hubs;

namespace SignalRTutorial.Models
{
    public class StoryCard
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public StoryState State { get; set; }

        public int Id { get; set; }

        public static StoryCard Create(string title, string description, StoryState storyState)
        {
            return new StoryCard
                       {
                           Id=1,
                           Title = title,
                           Description = description,
                           State = storyState
                       };
        }
    }
}

namespace SignalRTutorial.Models
{
    public enum StoryState{Todo,InProgress,Verify,Done}
}

Step 4 - Create a SignalR Hub

Create a new class called ScrumBoardHub and inherit from Hub this lets SignalR work it's magic.

using System.Collections.Generic;
using SignalR.Hubs;
using SignalRTutorial.Models;

namespace SignalRTutorial.Hubs
{
    public class ScrumBoardHub : Hub
    {
        private readonly IScrumRepository _repository;

        public ScrumBoardHub():this(new ScrumBoardRepository()){}

        public ScrumBoardHub(IScrumRepository repository)
        {
            _repository = repository;
        }
       
        public void CreateStory(StoryCard story)
        {
            Caller.AddedStory(story);
        }

        public void GetStories()
        {
            var storyCards = new List()
                                 {
                                     new StoryCard
                                         {
                                             Id = 1, Title = "Story 1", Description = "Story 1 desc", State = StoryState.Todo
                                         }, new StoryCard
                                                {
                                                    Id = 2, Title = "Story 2", Description = "Story 2 desc", State = StoryState.Todo
                                                },
                                 };
            var array = storyCards.ToArray();
            Caller.populateStories( array);
        }
    }
}

Step 5 - Create a Board Listing Page

I've just used the index page from the HomeController. Below are the various bits of Javascript you'll need for this to work. Please pay particular attentiaon to KnockoutJs and Jquery.signalr-0.5.3.js 

Just above is the line that will thanks to a bit of KnockoutJs magic our list of stories. We're using the data-bind attribute but this time we're specifying a template called story-template.

Then we simply tell it to bind to stories property on our ScrumBoard viewModel which you'll see defined below in the javascript.

Step 6 - wireup KnockoutJs model binding with SignalR

Below is the Javascript for mapping our StoryCard object on the server side to a Story client side object. We have a Scrumboard object that exposes a few methods including binding itself to the title and description fields for adding new stories. When the form is submitted, addStory is called which in turn calls createStory on the SignalR hub. On the serverside we then use SignalR to call out to the addedStory javascript method which simply creates a new instance of a Story in the javascript and adds it onto the stories collection. 

KnockoutJs then uses it's model binding, to add another div to the container, as indicated by the foreach binding above.

$(function () {

            function Story(id, title, description, state) {
                this.Id = ko.observable(id);
                this.Title = ko.observable(title);
                this.Description = ko.observable(description);
                this.State = ko.observable(state);
            }

            function ScrumBoard() {
                this.title = ko.observable("New Story");
                this.description = ko.observable("New Description");

                this.hub = $.connection.scrumBoardHub;
                this.viewModel = null;
                this.stories = ko.observableArray([]);
                var stories = this.stories;

                this.init = function () {
                    console.log("init");
                    this.hub.getStories();
                };

                this.addStory = function (newStory) {
                    console.log("addStory");
                    var t = { "title": this.title(), "description": this.description(), "state":"todo" };
                    this.hub.createStory(t).done(function () {
                        console.log('Success!')
                    }).fail(function (e) {
                        console.warn(e);
                    });
                    this.title("");
                    this.description("");

                };

                this.hub.addedStory = function (story) {
                    console.log("addedStory");
                    stories.push(new Story(story.Id, story.Title, story.Description,story.State));
                };

                this.hub.populateStories = function (storyArray) {
                    console.log("populate stories");
                    var mappedTasks = $.map(storyArray, function (item) {
                        return new Story(item.Id, item.Title, item.Description,item.State);
                    });

                    stories(mappedTasks);
                };

            }


            var scrumBoard = new ScrumBoard();
            $.connection.hub.start(function () { scrumBoard.init(); });
            ko.applyBindings(scrumBoard);


        });

Conclusion

That's it. Isn't that cool, we could improve it by also storing stories in a database using NHibernate, and we can further scale it by utilising Redis. I'll embelish later

References

Testing SignalR Tutorial

The Source code for this tutorial

https://github.com/SignalR/SignalR

Scott Hanselman's SignalR Tutorial

AmazedSaint - RealTime TaskPad with SignalR and KnockoutJS

SignalR and KnockoutJs Realtime task list


NHibernate Video Tutorial



So I finally got round to doing a simple Fluent NHibernate Tutorial screencast. I've been playing around with screencasts recently so thought what better quickstart to do than one on NHibernate. It roughly follows my previous NHibernate Tutorial but it's simplified to fit into YouTube's 15min limit. I hope you enjoy it, and please have a look at the recommended reading if you're interested in learning NHibernate. Be sure to checkout the Github repository here or just download the code from github as a zip

NHibernate Screencast

NHibernate Tutorial source code

So here's the code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentNHibernate.Mapping;


namespace FluentNHibernate.Example
{
    internal class Program
    {
        private static void Main(string[] args)
        {
          
            using(var session = NHibernateHelper.OpenSession())
            {
                using (var transaction = session.BeginTransaction())
                {
                    var fordMake = new Make
                                       {
                                           Name = "Ford"
                                       };
                    var fiestaModel = new Model
                                          {
                                              Name = "Fiesta",
                                              Make = fordMake
                                          };

                    var car = new Car
                                  {
                                      Make = fordMake,
                                      Model = fiestaModel,
                                      Title = "Dans Car"
                                  };

                    session.Save(car);

                    transaction.Commit();

                    Console.WriteLine("Created Car " + car.Title);
                }
            }
            Console.ReadLine();
        }
    }


    public class MakeMap : ClassMap
    {
        public MakeMap()
        {
            Id(x => x.Id);
            Map(x => x.Name);

        }
    }

    public class ModelMap : ClassMap
    {
        public ModelMap()
        {
            Id(x => x.Id);
            Map(x => x.Name);
            References(x => x.Make).Cascade.All();

        }
    }

    public class CarMap : ClassMap
    {
        public CarMap()
        {
            Id(x => x.Id);
            Map(x => x.Title);
            References(x => x.Make).Cascade.All();
            References(x => x.Model).Cascade.All();

        }
    }

    public class Make
    {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
    }

    public class Model
    {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
        public virtual Make Make { get; set; }
    }

    public class Car
    {
        public virtual int Id { get; set; }
        public virtual string Title { get; set; }
        public virtual Make Make { get; set; }
        public virtual Model Model { get; set; }
    }
   
}

And here's the NHibernateHelper class

using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Tool.hbm2ddl;

namespace FluentNHibernate.Example
{
    public class NHibernateHelper
    {
        private static ISessionFactory _sessionFactory;

        private static ISessionFactory SessionFactory
        {
            get
            {
                if (_sessionFactory == null)
                    InitializeSessionFactory();

                return _sessionFactory;
            }
        }

        private static void InitializeSessionFactory()
        {
            _sessionFactory = Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2008
                              .ConnectionString(
                                  @"Server=localhost\SQLExpress;Database=SimpleNHibernate;Trusted_Connection=True;")
                              .ShowSql()
                )
                .Mappings(m =>
                          m.FluentMappings
                              .AddFromAssemblyOf<Car>())
                .ExposeConfiguration(cfg => new SchemaExport(cfg)
                                                .Create(true, true))
                .BuildSessionFactory();            
        }

        public static ISession OpenSession()
        {
            return SessionFactory.OpenSession();
        }
    }
}

NHibernate Recommended Reading

If you're getting into NHibernate I'd recommend these books provding a good introduction and then a deeper dive. NHibernate in Action is a bit out of date but's still a great book.


Search

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2014