API Improvements made in SignalR 0.5

We made lots of breaking changes in 0.5 in order to gain more API consistency across the board. Some of the changes may not affect you if you weren’t doing anything with lower level APIs. These types of breaking changes will obviously slow down as we get closer to a 1.0 release.

API comments

This release, we spent some time writing API comments that should be helpful during development.

Breaking changes

A bunch of APIs were removed from PersistentConnection including the following:

  • All overloads of Send
  • SendToGroup
  • AddToGroup
  • RemoveFromGroup

Sending Data

All data is now sent through an IConnection (Connection property). Now, whether you’re within a PersistentConnection or outside of one, the logic to send data over that connection will look exactly the same.

SignalR 0.4

public class MyConnection : PersistentConnection
{
   
protected override Task OnConnectedAsync(IRequest request, IEnumerable<string> groups, string
connectionId)
    {
       
return Connection.Broadcast("A new connection was made!"
);
    }

   
protected override Task OnReceivedAsync(string connectionId, string
data)
    {
       
return Send(data);
    }
}

SignalR 0.5

public class MyConnection : PersistentConnection
{
   
protected override Task OnConnectedAsync(IRequest request, string
connectionId)
    {
       
return Connection.Broadcast("A new connection was made!"
);
    }

   
protected override Task OnReceivedAsync(string connectionId, string
data)
    {
       
return Connection.Send(connectionId, data);
    }
}

Groups

Group management is now centralized and consistent across Hubs and PersistentConnections. Each of those types have a property Groups of type IGroupManager that are scoped to a specific PersistentConnection or Hub. Here’s an example of adding a group in 0.4 and 0.5:

SignalR 0.4

public class MyConnection : PersistentConnection
{
   
protected override Task OnConnectedAsync(IRequest request, IEnumerable<string> groups, string
connectionId)
    {
       
return AddToGroup(connectionId, "foo"
);
    }

   
protected override Task OnReceivedAsync(string connectionId, string
data)
    {
       
return SendToGroup("foo", data);
    }
}
public class MyHub : Hub
{
   
public Task Join(string
group)
    {
       
return
AddToGroup(group);
    }

   
public Task Send(string group, string
message)
    {
       
return Clients[group].addMessage(message);
    }
}

SignalR 0.5

public class MyConnection : PersistentConnection
{
   
protected override Task OnConnectedAsync(IRequest request, string
connectionId)
    {
       
return Groups.Add(connectionId, "foo"
);
    }

   
protected override Task OnReceivedAsync(string connectionId, string
data)
    {
       
return Groups.Send("foo", data);
    }
}
public class MyHub : Hub
{
   
public Task Join(string
group)
    {
       
return
Groups.Add(Context.ConnectionId, group);
    }

   
public Task Send(string group, string
message)
    {
       
return Clients[group].addMessage(message);
    }
}

Dependency Injection

We’ve removed the AspNetHost type that from SignaR 0.4 and added a type that is purely optional to use, GlobalHost. The idea behind this type is to allow easy access to items in the container that will be used most commonly in any SignalR application, as well as a host agnostic default dependency resolver. This type exists in a new assembly SignalR.Hosting.Common.dll.

Replacing the resolver

SignalR 0.4

public class Global : System.Web.HttpApplication
{
   
protected void Application_Start(object sender, EventArgs
e)
    {
       
AspNetHost.SetResolver(new CustomResolver());
    }
}

SignalR 0.5

public class Global : System.Web.HttpApplication
{
   
protected void Application_Start(object sender, EventArgs
e)
    {
       
GlobalHost.DependencyResolver = new CustomResolver
();

       
RouteTable.Routes.MapHubs();
    }
}

Here, we’re changing the global resolver and remapping the default hub route to pick it up. The hub route is automatically added at a very early stage in the application (PreApplicationStart) so we need to remap it to use the new default resolver or we can specify a new one for the hub route directly:

public class Global : System.Web.HttpApplication
{
   
protected void Application_Start(object sender, EventArgs
e)
    {
       
RouteTable.Routes.MapHubs(new CustomResolver());
    }
}

The above logic doesn’t replace the global resolver but instead creates a self contained SignalR world using the specified resolver. If you go with this more “pure” approach, you’re opting out of using the GlobalHost.

Changing the Hub route

In 0.4, the magical signalr/hubs url was implemented as a dynamic http module. Now it’s just a regular route in the route table that can be replaced. Here’s an example of changing it to signalr2:

public class Global : System.Web.HttpApplication
{
   
protected void Application_Start(object sender, EventArgs
e)
    {
       
RouteTable.Routes.MapHubs("~/signalr2");
    }
}

Obviously this change needs to be accompanied by a change to your JavaScript include:

<!DOCTYPE html>
<
html
>
<
head>
    <title></title>
    <script src="Scripts/jquery-1.6.4.js" type="text/javascript"></script>
    <script src="Scripts/jquery.signalR-0.5rc.js" type="text/javascript"></script>
    <script src="signalr2/hubs" type="text/javascript"></script
>
</
head
>
<
body>

</body
>
</
html
>

Getting a reference to the connection/hub outside of a connection or hub

This is an extremely popular pattern for integrating with other frameworks. Here’s an example of broadcasting to all clients from an mvc controller:

SignalR 0.4

public class HomeController : Controller
{
   
public void
PerformLongRunningOperation()
    {
       
var connection = AspNetHost.DependencyResolver.Resolve<IConnectionManager>().GetConnection<MyConnection
>();

        connection.Broadcast(
"Called from an mvc controller"
);
    }

   
public void
PerformLongRunningHubOperation()
    {
       
var clients = AspNetHost.DependencyResolver.Resolve<IConnectionManager>().GetClients<MyHub
>();

        clients.notify(
"Hello world");
    }
}

SignalR 0.5

public class HomeController : Controller
{
   
public void
PerformLongRunningOperation()
    {
       
IPersistentConnectionContext context = GlobalHost.ConnectionManager.GetConnectionContext<MyConnection
>();

        context.Connection.Broadcast(
"Called from an mvc controller"
);
    }

   
public void
PerformLongRunningHubOperation()
    {
       
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MyHub
>();

        context.Clients.notify(
"Hello world");
    }
}

With this, you can inject these new context objects into your mvc controller or whatever framework you want to combine with SignalR. For example:

public class HomeController : Controller
{
   
private readonly IHubContext
_hubContext;

   
public HomeController(IHubContext
hubContext)
    {
        _hubContext = hubContext;
    }
       
   
public void
PerformLongRunningHubOperation()
    {
        _hubContext.Clients.notify(
"Hello world");
    }
}

Configuring SignalR

On each host (Self host and GlobalHost) there’s a Configuration property. This gives you access to the server configuration for things like setting timeouts, the keep alive interval and the heart beat interval:

public class Global : System.Web.HttpApplication
{
   
protected void Application_Start(object sender, EventArgs
e)
    {
       
GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(20);
    }
}

I’ll be blogging about more of these changes as we get closer to the release of 0.5.

Published Friday, May 4, 2012 3:23 PM by davidfowl

Comments

# re: API Improvements made in SignalR 0.5

Monday, May 7, 2012 9:28 AM by gkumik

I have connected two clients (JS) which exchange messages over hub, everything works fine.  Then I'm traing to push message to those clients from MVC controller, It dosn't work. I'm not getting any exception, just clients don't get messages. The code which I use in controller:

           IHubContext context = GlobalHost.ConnectionManager.GetHubContext<Results>();

           Hubs.Message msg = new Hubs.Message();

           msg.Sender = "auto";

           msg.MsgText = "test";

           context.Clients.onMessag(msg);

What I'm missing?

# re: API Improvements made in SignalR 0.5

Monday, May 7, 2012 10:12 AM by davidfowl

@gkumik Did you spell onMessage wrong? You have onMessag.

# re: API Improvements made in SignalR 0.5

Monday, May 7, 2012 11:05 AM by gkumik

Thanks, indeed it was spelling mistake ;).

Great that SignalR can be used this way!

# re: API Improvements made in SignalR 0.5

Wednesday, May 30, 2012 3:08 AM by Ian Mercer

Another breaking change from 0.4 to 0.5 is that HubProxy now uses the hubs Type.Name (or HubName attribute) and NOT the Type.FullName.  If you are using HubProxy you'll need to update this.

# re: API Improvements made in SignalR 0.5

Friday, June 8, 2012 4:29 AM by Mike

Silverlight 5 and Signalr0.5

Can't get this to work are there code changes to the SL client code

I am using - code below but get run-time errors (worked fine in 0.4)

var conn = new HubConnection(Application.Current.Host.Source.GetComponents(UriComponents.Scheme | UriComponents.HostAndPort,UriFormat.Unescaped));

var hub = conn.CreateProxy("MVCSCM_MIS.Hubs.chatHub");

conn.Start();