DotNetify: Multicast View Model for Building Collaborative Web Apps with SignalR

Written by dsuryd | Published 2018/10/01
Tech Story Tags: ios | multicast-view-model | signalr | web-apps | collaborative-web-apps

TLDRvia the TL;DR App

DotNetify version 3.1 is out, and with it a new capability to write multicast view model classes on cross-platform ASP.NET server.

What is a multicast view model? Assuming you’re already familiar with dotNetify and its server-side MVVM approach to web app development, it’s a server-side view model where you can share the same instance with multiple SignalR client connections. Any data update to the view model, either by the server or any of the client, will be simultaneously sent to all.

Why multicast view model?

There are many applications for this, practically everything that requires real-time communication to or among multiple clients, i.e. a simple IoT broadcast, chat apps, multiplayer games, and all kinds of online collaborative tools.

But surely this can all be done with just a SignalR hub class like every chat tutorial love to show? The answer is yes, but… let’s take a look at any of those chat app examples. It is deceivingly simple, for if you continue to go down that path and start to build more advanced features, you would likely end up with complex, tightly-coupled code — both on client and server — that aren’t easy to write tests for!

MVVM design pattern helps keep your code highly testable by separating the business logic from infrastructural concerns. DotNetify abstracts out the nitty-gritty of SignalR that for the most part you wouldn’t even know it’s there, so you could concentrate on building the features your customer wants.

How it works

This is how you define a multicast view model:

public class MyChatApp : MulticastVM{}

It’s a plain C# class that inherits from a dotNetify’s base class. For the client-side script — we’ll use React for this, we’ll add a component to connect to this view model:

Whenever this component is rendered on multiple browsers, the server will always give it the same view model instance.

Let’s add the textboxes for some basic chatting:

And here’s the complete server-side view model that will produce the same result as the official SignalR chat example:

public class MyChatApp : MulticastVM{public List<string> Messages { get; } = new List<string>();public Action<string> Message => this.AddList("Messages", msg);}

Notice that we’ve completely removed direct SignalR method invocations from the code, and use property / command bindings to get the data flowing seamlessly between the server and all the client browsers that are connecting to this view model.

When you do need to get more information on the connecting clients, you could inject IPrincipalContextAccessor and IConnectionContext to the view model. These interfaces provide ambient context on the currently connecting client, such as the SignalR connection ID, claims-based identity, IP address used, and so on.

Partitioning instances

Beyond just a single instance, some use cases will want to have a different instance for a different group of connections. For example, an authenticated user can simultaneously connect from a desktop and a smartphone and share one view model instance that’s not shareable with other users.

SignalR has the capability to define and broadcast to groups, which is scalable to multiple servers when Redis is configured. With MulticastVM , defining a group is as simple as overriding the GroupName property:

public class PerUserVM : MulticastVM{private readonly IPrincipalAccessor _principalAccessor;

public override string GroupName{get => _principalAccessor.Principal.Identity.Name;}

public PerUserVM(IPrincipalAccessor principalAccessor){_principalAccessor = principalAccessor;}}

The GroupName value will be dynamic, based on the context of the calling connection. The way this works is, when an authenticated client with the identity name UserA connects, a new instance will be created and associated with the client’s group name ‘UserA’. When UserA again connects from a different device, thus a different connection, it will be given any existing instance with matching group name. And since the identity name is the same, both devices will use the same view model instance. But when UserB connects, there will be no matching group name, and another instance will be created and associated with ‘UserB’ group name.

Using this mechanism, you could extend this to check the group membership of a connecting user from a data store, and create partitioned view model instances for any particular group.

Direct message

What if we need to send a message to only certain users? With IConnectionContext you would be able to associate the connected users with their SignalR connection IDs, and once you have these, use Send base method to send messages to one or more connections of your choosing:

Send(new List<string>{ connId, ... }, stateName, stateValue);

How to get started

If you’re new to dotNetify, you probably have a lot of questions on the example code above. Do visit dotnetify.net website, where the APIs are explained in details. There is plenty of other materials there: live demo, templates, and even UI toolkit for React. Everything is free and open source.

Thank‘s’ for reading!


Published by HackerNoon on 2018/10/01