Handling in LightSwitch commands in less than 10 lines of code.

Introduction

I elaborated some months ago and approach for sending command from client to server in LightSwitch because I thought we should start  being serious about the command table pattern.

The approach was more or less ok, but quite incompatible with the idea of simplicity in LightSwitch. In other words, it was too cumbersome to set things up, etc. ..

I’m happy I can come up now with something new and based on both a very serious improvement in the LightSwitch base architecture and on a brilliant library called “SignalR”.

The improvent in LightSwitch is the recent introduction of the ServerApplicationContext class. This allows you to break into (but in a completely secure way)  the LightSwitch application context from outside LightSwitch. For example from a web page or from a WCF dataservice.  Jan Van der Haegen has a great post on how to do this. (executing-an-arbitrary-method-or-long-running-process-on-the-lightswitch-server)

The next building block is the SignalR library. This post is not a tutorial on SignalR. I will only focus on how to use it in LightSwitch. I kindly refer to the SignalR documentation for more details.  In fact, most of the time, I’m quite reluctant to adopt a third-party library, but I’m quite happy to make an exception for SignalR, because it’s simply brilliant.

In my view, if you currently use the command table pattern. Consider throwing away all these classes, throw away your command table and start using following approach.

Many things to Jewel for bringing SignalR in my radar ! 

Setting up SignalR

We will need to include both client and server side a nuget package for SignalR.

In case you don’t know you to include a Nuget package:

  • Put your solution in file view
  • Right-click the client project and select “Manage Nuget Packages”
  • Find SignalR.Client.Silverlight5
  • Go now to the server project and do the same as for the client project, except select now as package: SignalR.Hosting.AspNet

That’s pretty simple. We have now all SignalR related assemblies in our client and server project.

Update: in case you have problems with the client side nuget package, you can download the dlls here as well: http://sdrv.ms/SDodpL .

Defining the command parameters

Commands will typically have parameters: the client needs to provide parameters to the server and the server will return certain parameters.

We add server side a class with following very simple command:

namespace LightSwitchApplication
{
    public class RequestParams
    {
        public string RequestParam1 { get; set; }
        public string RequestParam2 { get; set; }

    }
    public class ResponseParams
    {
        public string ResponseParam1 { get; set; }
        public string ResponseParam2 { get; set; }
    }
}

We will no longer use the common project because it’s gone in case you didn’t know.
As a result we will simply add the class client side via a file reference:
Right-click the client project and take “add existing item” and make sure you select “add as link”. By doing so, potential changes to the command structure are always propagated to the client project.

 

Great, now both client and server side have access to your command.


Setting up the command handling Server side

SignalR works in terms of Hubs. We have to setup such a Hub class. It will host the command, in my example called “MyCommand”.

We leverage here the new feature in the LightSwitch preview 2: the ServerApplicationContext. As you can see, we can inside our command access the full DataWorkspaces of our LightSwitch application. To proof this, we send back some information for the customer collection in the command response.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.LightSwitch;
using Microsoft.LightSwitch.Security.Server;
using SignalR.Hubs;
using System.Diagnostics;
using System.Threading;
using SignalR;
using Microsoft.LightSwitch.Framework;
namespace LightSwitchApplication
{
    public class MyHub : Hub
    {
       public ResponseParams MyCommand(RequestParams requestParams)
        {
            using (ServerApplicationContext serverContext = Application.CreateContext())
            {
                var customers = serverContext.DataWorkspace.ApplicationData.Customers; // full access !!
                string input = requestParams.RequestParam1;
                Thread.Sleep(5000); // this emulates a typical long running process.
                return new ResponseParams
                {
                    ResponseParam1 = "just for letting you know",
                    ResponseParam2 = "We can access the data in your workspace: first customer: " + customers.First().LastName
                };
            }
        }
    }
}

Note that the above command is designed for demo purposes only, it’s not actually doing something useful, but it shows the plumbing.

You  can see that my command needs access to the “customers” collection, so make sure you have Customer type in your  dataworkspace and provide some sample customers (at least one).

Setting up the comand client side

We first need to setup a HubProxy, which allows to comunicate with the server. We do this in the Application_Initialize event. By doing so, all screens will have access to the HubProxy:

using System;
using System.Linq;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections.Generic;
using Microsoft.LightSwitch;
using Microsoft.LightSwitch.Framework.Client;
using Microsoft.LightSwitch.Presentation;
using Microsoft.LightSwitch.Presentation.Extensions;
using SignalR.Client.Hubs;
using Microsoft.LightSwitch.Security;
namespace LightSwitchApplication
{
    public partial class Application
    {
        public IHubProxy HubProxy { get; set; }
        partial void Application_Initialize()
        {
            HubConnection hubConnection;
            hubConnection = new HubConnection("http://localhost:8010"); //make sure it matches your port in dev
            HubProxy = hubConnection.CreateProxy("MyHub");
            hubConnection.Start().Wait();
        }
    }
}

Next we will call our command behind a simple lightSwitch button. I do this on a CustomerListDetail screen:

using System;
using System.Linq;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections.Generic;
using Microsoft.LightSwitch;
using Microsoft.LightSwitch.Framework.Client;
using Microsoft.LightSwitch.Presentation;
using Microsoft.LightSwitch.Presentation.Extensions;
using SignalR.Client.Hubs;
namespace LightSwitchApplication
{
   public partial class CustomersListDetail
    {
        partial void MyFirstCommand_Execute()
        {
            Application.HubProxy.Invoke<ResponseParams>("MyCommand",
                new RequestParams { RequestParam1 = "request 1", RequestParam2 = "request 2" }).ContinueWith((task) =>
                    {
                        var responseparam2 = task.Result.ResponseParam2;
                        this.Details.Dispatcher.BeginInvoke(() =>
                        {
                            this.ShowMessageBox("Thanks for " + responseparam2);
                        });

                    });
            this.ShowMessageBox("The show must go on, why should we wait before the task completes?");
        }
    }
}

 

What’s next?

SignalR is really huge.  Note that without the new ServerApplicationContext it would be impossible to access the application context (and thus the data). Well,…  it would be possible via a  service reference to the application service endpoint, but making service references between artefacts server side is not so elegant. Furthermore there is no service reference between client and server.

I have also the impression that microsoft treats SignalR as a “first class citizen”… and that’s not without reason.

Furthermore, SignalR is not only usable for sending commands between client and server (and getting back the response), it has also an extremely powerfull publish/subscribe mechanism. In other words we could use it to broadcast message to all or a subset of clients connected to the server. That will probably my next post.