SignalR usage and potential issues of getting connectionID in the masterpage - asp.net-mvc

Recently, we have introduced SignalR into our project and hoping use its features. Currently SignalR is used only for showing progress bar on a couple of webpages on the client side for long running processes on the server. Could anyone help me with implementation of the SignalR and its ramifications?
.Net Framework Standard MVC application at at a time more than 3000 users connected to the webapp in Microsoft Azure hosted site.
SignalR is loaded and a connectionID is ($.connection.hub.start().done(function ().....) acquired in the _Layout.chtml. This is because, if the user may open different features in the webapp on different tabs and the these tabs may happen to have progress bars in it. So a unique connection ID on each Tab opened will help the SignalR to process the response.
I suspect a potential problem here for the page load performance and other unknown issues can be triggered because the layout page is opening a new connectionID each time the pages are loaded or refreshed.
Any other standard solution welcome if this is problematic.
Thanks for your help.
_Layout.chtml
<script src="~/Scripts/bootstrap.min.js"></script>
<script src="~/Scripts/ProgressBarHelper.js" type="text/javascript"></script>
<script src="~/Scripts/jquery.signalR-2.4.2.min.js" type="text/javascript"></script>
<script src="/signalr/hubs"></script>
<script type="text/javascript">
var connectionId = null;
$(function () {
// Reference the auto-generated proxy for the hub.
var progress = $.connection.progressHub;
//console.log(progress);
// Create a function that the hub can call back to display messages.
progress.client.AddProgress = function (message, percentage, reportmsg, showProgressReport, autoClose) {
if (CommonProgressBar.IsVisible() == false)
popupProgressBar.Show();
CommonProgressBar.SetPosition(percentage);
$('#popupProgressMessageText').text(message); //+ 'for ' + connectionId);
if (percentage == "100") {
if (autoClose == true) {
popupProgressReportText.SetText("Report");
popupProgressBar.Hide();
}
else {
popupProgressCloseButton.SetEnabled(true);
}
}
else {
if (autoClose == true) {
popupProgressCloseButton.SetEnabled(false);
}
}
var rptmsg = popupProgressReportText.GetText(); // $('#popupProgressReportText').GetText();
if (reportmsg != null && reportmsg != "") {
if (rptmsg != null && rptmsg != "") {
popupProgressReportText.SetText(rptmsg + "\r\n" + reportmsg);
}
else {
popupProgressReportText.SetText(rptmsg);
}
}
};
$.connection.hub.start().done(function () {
connectionId = $.connection.hub.id;
console.log(connectionId);
});
});
</script>

Related

How to automatically change agent status on Amazon Connect?

I need step by step directions on how to load the CCP into a webpage and use the streams API. I would need the javascript to turn the agent from "missed" to "available" after 25 seconds.
Currently we have to manually update staus which doesn't make sense for our use case.
I saw on the Amazon Connect forum someone made mention of a way to automatically change the status of from Missed to Available.
If you're embedding the CCP and using the Streams API, you can check
the agent status on refresh, and if it's in Missed Call, set it to
Available. I have this set to happen after 10 seconds.
For an embedded CCP you can do this using Stream API. You can subscribe to the agent refresh status, and do it there.
connect.agent(function (agent) {
logInfoMsg("Subscribing to events for agent " + agent.getName());
logInfoMsg("Agent is currently in status of " + agent.getStatus().name);
agent.onRefresh(handleAgentRefresh);
}
function handleAgentRefresh(agent) {
var status = agent.getStatus().name;
logInfoEvent("[agent.onRefresh] Agent data refreshed. Agent status is " + status);
//if status == Missed Call,
// set it to Available after 25 seconds."
//For example -but maybe this is not the best approach
if (status == "Missed") { //PLEASE review if "Missed" and "Availble" are proper codes
setTimeout(function () {
agent.setState("Available", {
success: function () {
logInfoEvent(" Agent is now Available");
},
failure: function (err) {
logInfoEvent("Couldn't change Agent status to Available. Maybe already in another call?");
}
});
;
}, 25000);
}
}
If you also need to know how to embed the CCP in a website, you can just do something like this
<!DOCTYPE html>
<meta charset="UTF-8">
<html>
<head>
<script type="text/javascript" src="amazon-connect-1.4.js"></script>
</head>
<!-- Add the call to init() as an onload so it will only run once the page is loaded -->
<body onload="init()">
<div id=containerDiv style="width: 400px;height: 800px;"></div>
<script type="text/javascript">
var instanceURL = "https://my-instance-domain.awsapps.com/connect/ccp-v2/";
// initialise the streams api
function init() {
// initialize the ccp
connect.core.initCCP(containerDiv, {
ccpUrl: instanceURL, // REQUIRED
loginPopup: true, // optional, defaults to `true`
region: "eu-central-1", // REQUIRED for `CHAT`, optional otherwise
softphone: { // optional
allowFramedSoftphone: true, // optional
disableRingtone: false, // optional
ringtoneUrl: "./ringtone.mp3" // optional
}
});
}
</script>
</body>
</html>
You can see the documentation for StreamsAPI here https://github.com/amazon-connect/amazon-connect-streams/blob/master/Documentation.md

Check overall progress of Azure Job and send it to an ASP.NET View

I'm creating a website with ASP.NET MVC5, hosted on Azure.
My users may upload a video, and I'd like to create a progress bar or something indicating how much % of the upload / transcoding has been done.
I'm following this Microsoft tutorial and the videos are correctly uploaded.
However, they show these lines of code once the job is sumbitted to Azure :
job = job.StartExecutionProgressTask(
j =>
{
Console.WriteLine("Job state: {0}", j.State);
Console.WriteLine("Job progress: {0:0.##}%", j.GetOverallProgress());
},
CancellationToken.None).Result;
I'm trying to adapt it to show it on my webpage.
Question
However, I'm unable to "send" j.GetOverallProgress() to my view.
Can anyone explain how to do this ?
What I did so far
Install Microsoft.AspNet.SignalR package (v 2.4.1)
(first time using SignalR)
Add these lines in my view :
<script src="~/Scripts/signalR/jquery.signalR-2.4.1.js"></script>
<script src="~/signalr/hubs"></script>
<script type="text/javascript">
$(document).ready(function () {
var hub = $.connection.videoService;
$("#btnCreate").on("click", function () {
hub.client.displayProgress = function (data) {
console.log(data); //Nothing is logged
};
})
});
</script>
And these lines on my back-end : (VideoService.cs in \Services folder)
public class VideoService : Hub
{
public EncodeMyVideo(...)
{
/* [...] */
job = job.StartExecutionProgressTask(j => {
displayProgress(job);
}, CancellationToken.None).Result;
}
public double displayProgress(IJob job)
{
return job.GetOverallProgress();
}
}
In debug mode, it goes to displayProgress method but never send anything to the view.
As a aside note, I also tried ith this :
public void displayProgress(IJob job)
{
Clients.Caller.displayProgress(job.GetOverallProgress());
}
But I get this error :
Using a Hub instance not created by the HubPipeline is unsupported.
I'm very new with SignalR, and despite this SO answer, I don't really understand what the problem is.
What should I do to send job.GetOverallProgress(); into my view ?
displayProgress method runs in another thread so it does not share the same Context with the EncodeMyVideo method. So you need to use GlobalHost.ConnectionManager.GetHubContext<> to get your hub context and your client connections.
string callerId = Context.ConnectionId;
job = job.StartExecutionProgressTask(j => {
displayProgress(job, callerId);
}, CancellationToken.None).Result;
public void displayProgress(IJob job, string clientId)
{
//Access to your hub context outside the request context
GlobalHost.ConnectionManager.GetHubContext<VideoService>().Clients.Client(clientId).displayProgress(job.GetOverallProgress());
}
For reporting progress you may also check this Reporting progress from hub method invocations

How can I HTTP PUT 0 size file using XMLHttpRequest in javascript?

We are developing a javascript library for accessing webdav server. And find difficulties in developing upload functionality. We think we should use HTTP PUT for uploading and developed our upload codes using HTTP PUT. It was OK to upload files using http put to a webdav server.
But when we try to upload empty content using HTTP PUT, server replies 411 ( Length required ). We set content-length header but server still replies 411. We don't know why.
Is it right to develop webdav upload functionality using HTTP PUT?
If it is so, how can we develop to create 0 size file using webdav?
The following is our test codes.
<!DOCTYPE html>
<html>
<head>
<title>Upload Testing</title>
<script type="text/javascript">
function ready() {
alert('ready');
doPUT('http://hqz.witkitty.com/soliton.co.jp/modc/emptyfile.txt', '', function () {
alert('success');
}, function (e) {
alert('error ' + e.status);
});
}
function doPUT(path, data, callback, errback) {
alert("going to upload");
var request = createXMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState == 4) {
if (request.status == 201 || request.status == 204) {
if (callback) callback(request);
} else {
if (errback) errback(request);
}
}
}
request.open('PUT', path, true);
request.setRequestHeader('Content-type', 'application/pdf');
request.setRequestHeader('Content-length', 0);
request.send(data);
}
function createXMLHttpRequest() {
return window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
}
</script>
</head>
<body>
<input type="button" onclick="ready()" value='Test Uploading'/>
</body>
</html>
That sounds like a bug, either in the user agent or in the server.
a) Does this happen with all user agents?
b) Can you capture an HTTP trace and check whether the Content-Length header field is present?

What would unhook my View and Controller from each other in Chrome and IE but not in Firefox?

I have previously successfully tested this MVC functionality in my app in Chrome but have recenlty also tested in IE (10) and Firefox.
When I mash the submit button on a page which sends model values to its controller for running a query and generating a report, it now works only in Firefox (each of the three browser indeed have their own peculiar characteristics -- where they shine or "dull" in relation to their cohorts (gleaming in purple and gold) -- but Chrome and Firefox seem to have lost the connection between the submit button's click handler and the corresponding Controller's method.
The app seems to simply hang after mashing the submit button in Chrome and IE; the breakpoints I have -- the first of which is at the very beginning of the corresponding [HttpPost] ActionResult in the Controller class -- are not reached. In fact, the app seems to freeze after mashing the button -- right-clicking the submit button after that does not give me an "inspect that element" in the context menu.
[HttpPost]
public ActionResult ReceiptCriteria(SalesReceiptCriteriaModel model)
{
if (ModelState.IsValid) // <-- there is a breakpoint here; only Firefox reaches it
{
. . .
In Firefox, it runs, and the breakpoints are hit.
What could possibly cause Chrome and IE to fail in this way, wheras Firefox soldiers on?
UPDATE
In response to Moby's request, here is the jQuery for the View in question:
The HTML in the View is pretty generic; the jQuery is:
$("#submit_button").click(function() {
// http://stackoverflow.com/questions/18192288/how-can-i-compare-date-time-values-using-the-jqueryui-datepicker-and-html5-time
var begD = $.datepicker.parseDate('mm/dd/yy', $('#BeginDate').val());
var endD = $.datepicker.parseDate('mm/dd/yy', $('#EndDate').val());
if (begD > endD) {
alert('Begin date must be before End date');
$('#BeginDate').focus();
return false;
}
else if (begD.toString() == endD.toString()) {
var dteString = begD.getFullYear() + "/" + (begD.getMonth() + 1) + "/" + begD.getDate();
var begT = new Date(dteString + " " + $('#BeginTime').val());
var endT = new Date(dteString + " " + $('#EndTime').val());
if (begT > endT) {
alert('Begin date must be before End date');
$('#BeginTime').focus();
return false;
}
}
$("#NumberOfResults").css("visibility", "visible");
$("#NumberOfResults").html("Please wait...");
EnableButton("submit_button", false);
// If all are selected, don't enumerate them; just set it at "All" (change of case, from 'all' to 'All', shows that the logic did execute)
var deptsList = $('#depts').checkedBoxes();
if (deptsList.length < deptsArray.length) {
$('#deptHeader span').html(deptsList.join(", "));
}
else if (deptsList.length == deptsArray.length) {
$('#deptHeader span').html("All");
}
// " "
var sitesList = $('#sites').checkedBoxes();
$('#sitesHeader span').html(sitesList.join(", "));
if (sitesList.length < sitesArray.length) {
$('#sitesHeader span').html(sitesList.join(", "));
}
else if (sitesList.length == sitesArray.length) {
$('#sitesHeader span').html("All");
}
$('#hiddenDepts').val(deptsList);
$('#hiddenSites').val(sitesList);
var UPCs = $('#UPC').val();
if (UPCs == "All") {
$('#UPC').val("1"); // take everything (1 and greater)
}
var resultsText = jQuery.trim($("#spanNumberOfResults").text());
if (resultsText != "") {
$("#NumberOfResults").css("visibility", "visible");
if (resultsText == "0") {
$("#NumberOfResults").css("color", "red");
} else {
var href = '/#ConfigurationManager.AppSettings["ThisApp"]/TLDCriteria/LoadReport';
var report_parms = {
GUID: "#Model.GUID",
SerialNumber: "#Model.SerialNumber",
ReportName: "#Model.ReportName"
};
window.open(href, "report_window", "resizable=1, width=850, left=" + (screen.width / 2 - 425));
}
}
}); // end of submit button click
function EnableButton(id, enable) {
if (enable) {
$("#" + id).removeAttr("disabled")
.removeClass("bottomButtonDisabled")
.removeClass("bottomButtonEnabled")
.addClass("bottomButtonEnabled");
} else {
$("#" + id).attr("disabled", "true")
.removeClass("bottomButtonDisabled")
.removeClass("bottomButtonEnabled")
.addClass("bottomButtonDisabled");
}
}
UPDATE 2
Something else which may or may not shed some light on this problem is my .js and .css references:
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js" type="text/javascript" defer > </script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript" defer> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript" defer> </script>
<script src="#Url.Content("~/Scripts/jquery-migrate-1.2.0.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/anytime.compressed.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/dynamicCheckboxes.js")" type="text/javascript" > </script>
. . .
<link href="http://code.jquery.com/ui/1.9.2/themes/smoothness/jquery-ui.css" rel="stylesheet" type="text/css" />
<link href="#Url.Content("~/Content/dynamicCheckboxes.css")" rel="stylesheet" type="text/css" />
<link href="#Url.Content("~/Content/anytime.compressed.css")" rel="stylesheet" type="text/css" />
<!--[if lt IE 9]>
<script src="/Scripts/html5shiv.js"> </script>
<![endif]-->
UPDATE 3
The Network tab in the Chrome Developer Tools looks like the middle of Wyoming (a whole lot of nothing), with a msg about the bottom informing me "No requests captured. Reload the page to see detailed information on the network activity."
When I dutifully mashed F5, it showed all the .js and .css files accessed, and finally (at the top), the page I'm gawking at. Mashing the "View Report" causes no more activity in the tab, though. I do see the console.log() msg I placed at the end of the submit button click handler, though, to wit: "made it to the end of submit button click"
There is one err msg in the console, too, but this:
Failed to load resource: the server responded with a status of 400 (Bad Request) http://localhost/%3C%=%20System.Configuration.ConfigurationManager.AppSettings[%22ThisApp%22]%20%%3E/Content/Images/SSCSsprite.png
Would simply fail to load the resource, not wreak other mayhem, right?
UPDATE 4
Based on Simon Halsey's hint, I found that, on stepping though the jQuery in Chrome, it fails this test:
if (resultsText != "") {
...obviously it's not in Firefox, and I assume that it also fails in IE (I'll czech to be sure in both cases, and update this).
Later: It's "" in Firefox, too...and the first time through, it also failed-wouldn't continue on. Second time through, it got through, though...???
There is two options:
There is no request due to javascript error
Your request signature doesnt math controller method
A.
Browsers have different behaivior with some javascript functions. Thats one of the reasons why jQuery is so popular.
The most efficient way to find it is to debug javascript line by line in each browser.
Likely it is the reason.
B.
Also your javascript is quite exotic for me. I guess you are catching sumbit button click and modifying inputs values on a fly.
I would recommend to use $.post or $.ajax and preventDefault instead.
It would make your javascript more clear and simple.
C.
To analyze what requests are sent from your browser I would recommend to use fiddler.
http://fiddler2.com/

Can someone explain to me a few thing about this JQuery code I have here from the MVC Music Store tutorial

The thing that confuses me somewhat and it's probably due to the conventions in
the jquery ajax() request .post() function is that it does not indicate anywhere that if request is successful that it should call the handleUpdate() function which gets the returned json object via "var json = context.get_data();", also why is the whole chunk of code starting with "if (data.ItemCount == 0)" in the handleUpdate() identical to the one in the .post() on success run > function (data) { duplicate code } .
Maybe because function (data) {} is callback function it waits for the entire request/response cycle to finish and that includes "var json = context.get_data();" in handleUpdate() ?
Thanks..
Pasted from the tutorial PDF, no other jscript in this view.
<script type="text/javascript">
$(function () {
// Document.ready -> link up remove event handler
$(".RemoveLink").click(function () {
// Get the id from the link
var recordToDelete = $(this).attr("data-id");
if (recordToDelete != '')
{
// Perform the ajax post
$.post("/ShoppingCart/RemoveFromCart", { "id": recordToDelete },
function (data) {
// Successful requests get here
// Update the page elements
if (data.ItemCount == 0)
{
$('#row-' + data.DeleteId).fadeOut('slow');
}
else
{
$('#item-count-' + data.DeleteId).text(data.ItemCount);
}
$('#cart-total').text(data.CartTotal);
$('#update-message').text(data.Message);
$('#cart-status').text('Cart (' + data.CartCount + ')');
});
}
});
});
function handleUpdate()
{
// Load and deserialize the returned JSON data
var json = context.get_data();
var data = Sys.Serialization.JavaScriptSerializer.deserialize(json);
// Update the page elements
if (data.ItemCount == 0)
{
$('#row-' + data.DeleteId).fadeOut('slow');
}
else
{
$('#item-count-' + data.DeleteId).text(data.ItemCount);
}
$('#cart-total').text(data.CartTotal);
$('#update-message').text(data.Message);
$('#cart-status').text('Cart (' + data.CartCount + ')');
}
</script>
The handleUpdate() function is a relic from the previous MVC2 version of the tutorial where the Ajax for removing items from the cart was handled by Microsoft's Ajax called via an Ajax.ActionLink helper. (see below)
This was changed to use JQuery Ajax in the MVC3 version of this tutorial but the handleUpdate() code has been left in it seems by mistake during the conversion from MVC2 to MVC3.
<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="/Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
function handleUpdate(context) {
// Load and deserialize the returned JSON data
var json = context.get_data();
var data = Sys.Serialization.JavaScriptSerializer.deserialize(json);
// Update the page elements
$('#row-' + data.DeleteId).fadeOut('slow');
$('#cart-status').text('Cart (' + data.CartCount + ')');
$('#update-message').text(data.Message);
$('#cart-total').text(data.CartTotal);
}
</script>
...
<%: Ajax.ActionLink("Remove from cart", "RemoveFromCart",
new { id = item.RecordId },
new AjaxOptions { OnSuccess = "handleUpdate" })%>
There is no way (according to this code) that handleUpdate is being called on success of $.post. Jquery post function has following syntax
$.post(url,data, callback);
and in the code you can see that all three parameters are explicitly specified and callback is an anonymous function with signature
function(data){}
Now, what you can see is that this anonymous function and handleUpdate are doing exactly the same logic. That makes me believe that they belong to the two different scenarios. For example, first scenario is that links are rendered using
Html.ActionLink(LinkText, ActionName, new{#class = "RemoveLink"})
In this case click event is handled by jquery function on the top and all the logic is done in this function (including ajax and callback). Second function might have been used for some
//please confirm all parameters of the function
Ajax.ActionLink(LinkText, ActionName, new AjaxOptions{onSuccess = "handleUpdate"});
and this seems to be connected with microsoftmvc ajax files that that used to exist in ancient times. You can put alert in each function and check what is the case with you.

Resources