Your official information source from the .NET Web Development and Tools group at Microsoft.
SignalR brought developers an easier way to build real-time, very responsive web applications. But, how does it play with other available technologies? I took a couple of days to implement a very common scenario needed in every Enterprise Application: A DataTable to do CRUD operations persisting changes on a database.
My initial thought was this is going to be a trivial task, I have done DataTables a few times in the past. Then, I realized the real-time model with many concurrent users introduces a few challenges.
Before going through details, here is a screenshot of what I accomplished: A DataTable where multiple users are collaborating on the add/edit/delete of its contents! When user 1 edits David Fowler, the Delete and Edit buttons disappear for David in the browsers of users 2, 3, and 4. When user 2 edits Gustavo Armenta, the Delete and Edit buttons disappear for Gustavo in the browsers of users 1, 3, and 4. When user 1 finishes editing David Fowler, Delete and Edit buttons reappear on all browsers.
My SignalR client receives a JSON message indicating there is a new friend. Now I need to update my html table with the new content. AngularJS plays beautiful in this scenario as I only have to add a row in my existing array (array.push(item)) and refresh content (scope.apply()).
// SignalR Events hubProxy.client.add = function (friend) { console.log("client.add(" + JSON.stringify(friend) + ")"); $scope.friends.push(friend); $scope.$apply(); }
The previous code works well when the server pushes data to client. Now, let’s handle the case when the user is pushing changes.
// AngularJS Events $scope.add = function () { hubProxy.server.add({ Name: $scope.add_friend.Name }); $scope.add_friend.Name = ""; }
What to do when two or more users want to edit the same row? You could allow them to edit simultaneously and the last one to save changes win. You could track versions and let the first user save changes and fail for other users as they need to refresh to the latest version before submitting changes. You could lock the row and allow only a single user to make changes, once he finishes other users can take the lock.
In this sample, I have followed the lock approach with a few more considerations.
Most of the magic happen on the SignalR Hub. OnConnected() returns both a list of items and a list of locks. OnReconnected() simply refreshes the state as if connecting for the first time. OnDisconnected releases the lock I had taken so other active users can edit the row.
private static ConcurrentDictionary<string, int> _locks = new ConcurrentDictionary<string, int>(); public override async Task OnConnected() { var query = from f in _db.Friends orderby f.Name select f; await Clients.Caller.all(query); await Clients.Caller.allLocks(_locks.Values); } public override async Task OnReconnected() { // Refresh as other users could update data while we were offline await OnConnected(); } public override async Task OnDisconnected() { int removed; _locks.TryRemove(Context.ConnectionId, out removed); await Clients.All.allLocks(_locks.Values); } public void TakeLock(Friend value) { _locks.AddOrUpdate(Context.ConnectionId, value.Id, (key, oldValue) => value.Id); Clients.All.allLocks(_locks.Values); } public void Update(Friend value) { var updated = _db.Friends.First<Friend>(f => f.Id == value.Id); updated.Name = value.Name; _db.SaveChanges(); Clients.All.update(updated); int removed; _locks.TryRemove(Context.ConnectionId, out removed); Clients.All.allLocks(_locks.Values); }
Then, AngularJS provides a very clear mapping between HTML content and conditional logic using ”ng-repeat” and “ng-show”. Look how easy is to make UI decisions based on the current state of JSON objects bound to $scope.
<tr ng-repeat="friend in friends | orderBy:predicate"> <td><button name="deleteButton" ng-click="delete(friend)" ng-show="!friend.IsLocked">Delete</button></td> <td> <button name="editButton" ng-click="edit(friend)" ng-show="!friend.IsLocked">Edit</button> <button name="updateButton" ng-click="update(friend)" ng-show="isEdit(friend)">Update</button> </td> <td>{{friend.Id}}</td> <td ng-show="!isEdit(friend)">{{friend.Name}}</td> <td ng-show="isEdit(friend)"><input type="text" ng-model="edit_friend.Name" /></td> </tr>
https://github.com/gustavo-armenta/SignalR-JS-HTML
Have you seen other HTML/JS/SignalR controls out there? Please share the links! I have only seen this one: http://mvc.syncfusion.com/demos/ui/grid/productshowcase/signalr#
Thanks for posting Gustavo. Very useful info indeed.
I am very intrigued by what this technology is starting to enable. I work in an environment where we have business users who would like to be able to make quick updates to "tables" stored in the database, but they don't know anything about databases or SQL. If we could provide a UI that's clean, but automatically notifies each other when users are changing data, it would be very useful.
Thanks for sharing!
This is awesome!