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.