Developing with SignalR is a very interesting experience. The technology, which allows us to create solutions that interact in real time with the browser and native apps, provides us with several layers of abstraction over technologies such as WebSockets, so that we can focus on the specific features of our apps. However, sometimes these abstractions may be a problem more than a solution.
One of the cases we can see this is with the hubs. A hub is a class that, when processed by signalR, is exposed to the browser by using Javascript. Unlike a REST API, a hub does not (only) responds to a request, they can also send messages both to the client who has made the request, the rest of the clients connected, or all of them, in real time.
To define a hub, we only need a class that inherits from the Hub class and SignalR will do the rest. It will discover (by using reflection) all the different hubs in the current assembly and generate the corresponding javascript.
Here is an example with the following code in C#:
public class ChatHub : Hub { public void Send(string name, string message) { // Call the broadcastMessage method to update clients. Clients.All.broadcastMessage(name, message); } }
The generated code by SignalR looks a little bit like this:
proxies.chatHub = this.createHubProxy('chatHub'); proxies.chatHub.client = { }; proxies.chatHub.server = { send: function (name, message) { return proxies.chatHub.invoke.apply(proxies.chatHub, $.merge(["Send"], $.makeArray(arguments))); } };
As I mentioned earlier, signalR hubs are discovered automatically for the current assembly. What hapens if we store our hubs on a different asembly, for example, if we have two apps who use the same hub? After searching, I found the solution on StackOverflow. As I only needed to load one specific assembly, I picked only this line:
AppDomain.CurrentDomain.Load(typeof(Namespace.ChatHub).Assembly.FullName);
This code loads the Assembly that contains the class so SignalR can «discover» the Hub, we must place it just before the app.MapSignalR() or otherwise the Hub will not be generated.
Obfuscation
If we distribute our application our clients (for example when offering a solution containing SignalR so that they can install it on a local server) is usual to use tools like SmartAssembly or Dotfuscator, to protect the intellectual property of your code.
For a correct SignalR operation, it’s important to remember that we must not obfuscate, or prune the Hub classes, since the class is loaded by reflection. The obfuscation result will rename the classes and probably remove uncalled code (that is, in fact, called from Javascript). The best solution is to use hubs as a ‘proxy’ class and obfuscate those classes and methods called from the hub.
To avoid obfuscation you can can, either manually in the properties of the project, or through attributes. For SmartAssembly we find attributes such as [DoNotObfuscateType] and [DoNotPruneType] which apply to the whole class, and in the case of Dotfuscator we can use the [Obsufcation] attribute.
More information
- Original answer on StackOverflow
- On MSDN Getting started with SignalR
- SmartAssembly docs: Attributes
- On MSDN Obfuscation Attribute