My need to have my Azure MVC-5 Web App read from my Azure Service Bus Message Queue and then send data to all client web pages via SignalR, has been resolved.
Code-wise, it is quite easy and elegant. Here is my solution:
First, my Visual Studio Solution has just one Project--the main web app project. In the root of the project, I have my SignalR hub class, called djwHub;
Imports Microsoft.AspNet.SignalR
Imports Microsoft.AspNet.SignalR.Hubs
Imports Microsoft.AspNet.SignalR.Messaging
Imports System.Threading.Tasks
Imports Microsoft.ServiceBus
Imports Microsoft.ServiceBus.Messaging
<HubName("djwHub")>
Public Class djwHub
Inherits Hub
Private connectString As String = "Endpoint=sb://mynamespace.servicebus.windows.net/;SharedAccessKeyName=myKeyName;SharedAccessKey=myKey"
Private queueName As String = "myQueueName"
Private m_count As Integer
Public Sub beginReadingMessageQue()
Dim rf = MessagingFactory.CreateFromConnectionString(connectString)
Dim taskTimer = Task.Factory.StartNew(Async Function()
Dim receiver = Await rf.CreateMessageReceiverAsync(queueName, ReceiveMode.PeekLock)
While True
Dim timeNow As String = DateTime.Now.ToString()
Clients.All.sendServerTime(timeNow)
Try
Dim message = Await receiver.ReceiveAsync(TimeSpan.FromSeconds(5))
If message IsNot Nothing Then
Dim messageBody = message.GetBody(Of [String])()
Clients.All.sendNewMessage(messageBody)
Await message.CompleteAsync
Else
'no more messages in the queue
Exit Try
End If
Catch e As MessagingException
If Not e.IsTransient Then
'Console.WriteLine(e.Message)
'Throw
End If
End Try
'Delaying by 1/2 second.
Await Task.Delay(500)
End While
End Function, TaskCreationOptions.LongRunning)
End Sub
End Class
Now, my MVC-5 web app doesn't have a Startup class in the root. Instead, my startup occurs in the IdentityConfig.vb class that's located in my App_Start folder. So this is where I put app.MapSignalR() as shown here;
Imports Microsoft.AspNet.Identity
Imports Microsoft.Owin
Imports Microsoft.Owin.Security.Cookies
Imports Owin
Imports myApp.Users.Infrastructure
Imports Microsoft.Owin.Security.Google
Imports Microsoft.AspNet.SignalR
Imports Microsoft.Owin.Cors
Imports Microsoft.AspNet.SignalR.ServiceBus
Namespace Users
Public Class IdentityConfig
Public Sub Configuration(app As IAppBuilder)
app.CreatePerOwinContext(Of AppIdentityDbContext)(AddressOf AppIdentityDbContext.Create)
app.CreatePerOwinContext(Of AppUserManager)(AddressOf AppUserManager.Create)
app.CreatePerOwinContext(Of AppRoleManager)(AddressOf AppRoleManager.Create)
app.UseCookieAuthentication(New CookieAuthenticationOptions() With { _
.AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, _
.LoginPath = New PathString("/Account/Login") _
})
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie)
app.MapSignalR()
End Sub
End Class
End Namespace
The only part left, is the web View page. Note that I've got 6 FusionChart gauges on the web page right now. But you should be able to pick out the SignalR function calls that talk to the djwHub;
@Code
Layout = Nothing
End Code
<head>
<title>Drillers Readout - Job #@ViewBag.JobNumber</title>
@Scripts.Render("~/bundles/modernizr")
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@Scripts.Render("~/bundles/jqueryui")
<script src="~/Scripts/jquery.signalR-2.2.1.js"></script>
<script src="~/Scripts/jquery.signalR-2.2.1.min.js"></script>
<script src="~/signalr/hubs"></script>
<script type="text/javascript">
(function () {
var currentIndex = -1;
var lastTime;
var GxDisplay;
var GyDisplay;
var GzDisplay;
var HxDisplay;
var HyDisplay;
var HzDisplay;
var myHub = $.connection.djwHub;
$.connection.hub.logging = true;
myHub.client.sendNewMessage = function (message) {
var myArray = JSON.parse(message);
currentIndex += 1;
//var dataId = myArray.dataCount;
if (currentIndex == 0) {
lastTime = new Date(myArray.time1);
} else {
var newTime = new Date(myArray.time1);
if (newTime >= lastTime) {
var dataId = myArray.dataCount;
var Gx = myArray.Gx;
var Gy = myArray.Gy;
var Gz = myArray.Gz;
var Hx = myArray.Hx;
var Hy = myArray.Hy;
var Hz = myArray.Hz;
lastTime = newTime;
GxDisplay.feedData("value=" + Gx);
GyDisplay.feedData("value=" + Gy);
GzDisplay.feedData("value=" + Gz);
HxDisplay.feedData("value=" + Hx);
HyDisplay.feedData("value=" + Hy);
HzDisplay.feedData("value=" + Hz);
$("#newMessage").text('#' + dataId + ": " + lastTime + " Gx=" + Gx.toFixed(2) + " Gy=" + Gy.toFixed(2) + " Gz=" + Gz.toFixed(2)
+ " Hx=" + Hx.toFixed(2) + " Hy=" + Hy.toFixed(2) + " Hz=" + Hz.toFixed(2));
}
}
};
$.connection.hub.start().done(function () {
myHub.server.beginReadingMessageQue();
});
myHub.client.sendServerTime = function (serverTime) {
$("#newTime").text(serverTime);
};
FusionCharts.ready(function () {
GxDisplay = new FusionCharts({
type: 'angulargauge',
renderAt: 'GxChart',
width: '250',
height: '175',
dataFormat: 'json',
dataSource: {
"chart": {
"caption": "Gx",
"subcaption": "",
"lowerLimit": "-2000",
"upperLimit": "2000",
"lowerLimitDisplay": "",
"upperLimitDisplay": "",
"showValue": "1",
"valueBelowPivot": "1",
"theme": "fint"
},
"colorRange": {
"color": [{
"minValue": "-2000",
"maxValue": "2000",
"code": "#ADD8E6"
}]
},
"dials": {
"dial": [{
"id": "fcGx",
"value": "0"
}]
}
}
});
GyDisplay = new FusionCharts({
type: 'angulargauge',
renderAt: 'GyChart',
width: '250',
height: '175',
dataFormat: 'json',
dataSource: {
"chart": {
"caption": "Gy",
"subcaption": "",
"lowerLimit": "-2000",
"upperLimit": "2000",
"lowerLimitDisplay": "",
"upperLimitDisplay": "",
"showValue": "1",
"valueBelowPivot": "1",
"theme": "fint"
},
"colorRange": {
"color": [{
"minValue": "-2000",
"maxValue": "2000",
"code": "#ADD8E6"
}]
},
"dials": {
"dial": [{
"id": "fcGy",
"value": "0"
}]
}
}
});
GzDisplay = new FusionCharts({
type: 'angulargauge',
renderAt: 'GzChart',
width: '250',
height: '175',
dataFormat: 'json',
dataSource: {
"chart": {
"caption": "Gz",
"subcaption": "",
"lowerLimit": "-2000",
"upperLimit": "2000",
"lowerLimitDisplay": "",
"upperLimitDisplay": "",
"showValue": "1",
"valueBelowPivot": "1",
"theme": "fint"
},
"colorRange": {
"color": [{
"minValue": "-2000",
"maxValue": "2000",
"code": "#ADD8E6"
}]
},
"dials": {
"dial": [{
"id": "fcGz",
"value": "0"
}]
}
}
});
HxDisplay = new FusionCharts({
type: 'angulargauge',
renderAt: 'HxChart',
width: '250',
height: '175',
dataFormat: 'json',
dataSource: {
"chart": {
"caption": "Hx",
"subcaption": "",
"lowerLimit": "-100000",
"upperLimit": "100000",
"lowerLimitDisplay": "",
"upperLimitDisplay": "",
"showValue": "1",
"valueBelowPivot": "1",
"theme": "fint"
},
"colorRange": {
"color": [{
"minValue": "-100000",
"maxValue": "100000",
"code": "#ff1493"
}]
},
"dials": {
"dial": [{
"id": "fcHx",
"value": "0"
}]
}
}
});
HyDisplay = new FusionCharts({
type: 'angulargauge',
renderAt: 'HyChart',
width: '250',
height: '175',
dataFormat: 'json',
dataSource: {
"chart": {
"caption": "Hy",
"subcaption": "",
"lowerLimit": "-100000",
"upperLimit": "100000",
"lowerLimitDisplay": "",
"upperLimitDisplay": "",
"showValue": "1",
"valueBelowPivot": "1",
"theme": "fint"
},
"colorRange": {
"color": [{
"minValue": "-100000",
"maxValue": "100000",
"code": "#ff1493"
}]
},
"dials": {
"dial": [{
"id": "fcHy",
"value": "0"
}]
}
}
});
HzDisplay = new FusionCharts({
type: 'angulargauge',
renderAt: 'HzChart',
width: '250',
height: '175',
dataFormat: 'json',
dataSource: {
"chart": {
"caption": "Hz",
"subcaption": "",
"lowerLimit": "-100000",
"upperLimit": "100000",
"lowerLimitDisplay": "",
"upperLimitDisplay": "",
"showValue": "1",
"valueBelowPivot": "1",
"theme": "fint"
},
"colorRange": {
"color": [{
"minValue": "-100000",
"maxValue": "100000",
"code": "#ff1493"
}]
},
"dials": {
"dial": [{
"id": "fcHz",
"value": "0"
}]
}
}
});
GxDisplay.render();
GyDisplay.render();
GzDisplay.render();
HxDisplay.render();
HyDisplay.render();
HzDisplay.render();
});
}());
</script>
</head>
<body>
<div id="newTime"></div><br />
<ul id="newMessage"></ul>
<div id="gCharts">
<div id="GxChart"></div>
<div id="GyChart"></div>
<div id="GzChart"></div>
</div>
<div id="hCharts">
<div id="HxChart"></div>
<div id="HyChart"></div>
<div id="HzChart"></div>
</div>
</body>
Special thanks here to astaykov & Ashley Medway, and to Microsoft, for making all of this possible!