SignalR client method fires multiple times - c#

I'm playing around with SignalR, trying to create a heatmap overlay on a Google map. However, my method is firing multiple times and I can't figure out why.
The data returned from SQL is formatted into JSON, so I can plot it onto the overlay using this plugin - http://www.patrick-wied.at/static/heatmapjs/
I've been following the web api demo found at http://techbrij.com/database-change-notifications-asp-net-signalr-sqldependency but without luck. My code is below:
View (snipped to show relevant code)
<script type="text/javascript">
var map;
var heatmap;
var testData;
$(function () {
// Proxy created on the fly
var sales = $.connection.productSalesHub;
// Declare a function on the product sales hub so the server can invoke it
sales.client.displaySales = function () {
getSalesData();
};
// Start the connection
$.connection.hub.start();
getSalesData();
});
function getSalesData() {
$.ajax({
url: '../api/values',
type: 'GET',
datatype: 'json'
})
.done(function (res) {
if (res.length > 0) {
var myLatlng = new google.maps.LatLng(48.3333, 16.35);
// sorry - this demo is a beta
// there is lots of work todo
// but I don't have enough time for eg redrawing on dragrelease right now
var myOptions = {
zoom: 2,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.SATELLITE,
disableDefaultUI: false,
scrollwheel: true,
draggable: true,
navigationControl: true,
mapTypeControl: false,
scaleControl: true,
disableDoubleClickZoom: false
};
testData = res;
map = new google.maps.Map(document.getElementById("heatmapArea"), myOptions);
heatmap = new HeatmapOverlay(map, {
"radius": 15,
"visible": true,
"opacity": 60,
legend: {
position: 'br',
title: 'Amount of items sold'
}
});
google.maps.event.addListenerOnce(map, "idle", function() {
heatmap.setDataSet(testData);
});
}
})
.fail(function () {
alert("error");
})
.always(function() {
alert("complete");
});
}
</script>
Values controller:
public class ValuesController : ApiController
{
ProductSalesRepository repo = new ProductSalesRepository();
// GET api/values
public JObject Get()
{
var data = repo.GetProductSalesData();
return repo.BuildJson(data);
}
}
ProductSalesHub.cs
public class ProductSalesHub : Hub
{
public static void Show()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ProductSalesHub>();
context.Clients.All.displaySales();
}
}
And lastly, my repo
public class ProductSalesRepository
{
public IEnumerable<ProductSalesInfo> GetProductSalesData()
{
using (
var connection =
new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(#"SELECT top 10 [lat],[lng],[count]
FROM [dbo].[ProductSales]", connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
using (var reader = command.ExecuteReader())
return reader.Cast<IDataRecord>()
.Select(x => new ProductSalesInfo()
{
Lat = x.GetString(0),
Long = x.GetString(1),
Count = x.GetInt32(2)
}).ToList();
}
}
}
public JObject BuildJson(IEnumerable<ProductSalesInfo> data )
{
IEnumerable<ProductSalesInfo> productSalesInfos = data as List<ProductSalesInfo> ?? data.ToList();
int max = (from d in productSalesInfos.ToList() select d.Count).Max();
JObject o = new JObject(
new JProperty("max", max),
new JProperty("data",
new JArray(from d in productSalesInfos
select new JObject(
new JProperty("lat", d.Lat),
new JProperty("lng", d.Long),
new JProperty("count", d.Count)))));
return o;
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
ProductSalesHub.Show();
}
}
I've been staring at this for hours now, without figuring out why the ajax call is triggered multiple times.
Any ideas?

I had this exact same problem. I was injecting my repository class and had multiple instances of it. Here you are adding the event every time GetProductSalesData() method is called. I would add the event once in the constructor and use the singleton pattern to make sure it is only called once.

Because every client who open the page request the server by signalR and signalR response to all client thats why you see multiple response and request.
use
this.Clients.Client(this.Context.ConnectionId).displaySales();
instead of
context.Clients.All.displaySales();
cheers.

Related

Razor Page Method RedirectToPage calls action, but does not load the page (No MVC)

I have a project with a few Razor Pages. The first is a log in where on submit I call this method.
public RedirectToPageResult OnPostLogin()
{
string salt; // grab the unique salt we stored on the database
string hashedPassword;
using (var conn = new SqlConnection(_config.GetConnectionString("UserDBContext")))
{
salt = conn.Query<string>(GetDBSalt(), new // grabs the salt from the database
{
#Email = Email
}).FirstOrDefault();
if (salt != null)
{
hashedPassword = PasswordHash.Hash(Password, Convert.FromBase64String(salt)); // hashes the password and salt to compare against the database password
}
else
{
hashedPassword = "";
}
var result = conn.Query<Guid>(CheckUserSql(), new
{
#Email = Email, // ditto below
#Password = hashedPassword // checks if the hashed password matches the password we have on the database
}).FirstOrDefault();
var resultString = result.ToString();
if (resultString != "00000000-0000-0000-0000-000000000000")
{
//Response.Redirect($"/CardPage/{resultString}");
return RedirectToPage("CardPage", "Card", new { id = resultString });
}
else
{
return RedirectToPage("LogIn");
}
}
}
Then goes to the cshtml page, CardPage, where this OnGet method is called.
public void OnGetCard(string id)
{
using (var conn = new SqlConnection(_config.GetConnectionString("UserDBContext")))
{
var cards = conn.Query<string>(GetUserSql(), new
{
#Id = id
}).FirstOrDefault();
this.HttpContext.Session.SetString("Id", id);
CardsSelected = cards;
}
}
This works perfectly fine, it moves the id to the next page and I'm able to do what I need to on this page. But when I try to do the exact same thing on CardPage with this OnPost method. This OnPost is called from an AJAX method.
public RedirectToPageResult OnPost([FromBody] Cards cards)
{
var urlId = HttpContext.Session.GetString("Id");
var cardString = cards.cards;
using (var conn = new SqlConnection(_config.GetConnectionString("UserDBContext")))
{
conn.Execute(SetCardsSql(), new
{
#Cards = cardString,
#Id = urlId
});
}
return RedirectToPage("HierarchyPage", "Build", new { id = urlId });
}
And this OnGet method
public void OnGetBuild(string id)
{
using (var conn = new SqlConnection(_config.GetConnectionString("UserDBContext")))
{
var cards = conn.Query<string>(GetUserSql(), new
{
#Id = id
}).FirstOrDefault();
this.HttpContext.Session.SetString("Id", id);
CardsSelected = ParseCardsSelected(cards);
}
string slotHTML;
int slotID = 1;
// create the rows of the pyramid and insert its slots
for (int currentRowNumber = 1; currentRowNumber <= MAX_ROWS; currentRowNumber++)
{
slotHTML = "";
for (int n = 1; n <= currentRowNumber; n++, slotID++)
{
// generates slot HTML and assigns an unique ID to each slot
slotHTML += string.Format(CARD_SLOT_HTML, slotID);
}
Rows += string.Format(SLOT_ROW_HTML, currentRowNumber, slotHTML);
}
Rows = new string(Rows.Where(c => !char.IsWhiteSpace(c) || c.Equals(' ')).ToArray());
}
The method is called and executed, then goes to the cshtml page where I have the model set up. All of this executes correctly, but the page does not load in the browser. I have been searching for hours on end to find a solution, but I can't find anything with a similar problem. I have a temporary workaround by taking the url that should be loaded and manually loading it and it works that way.
To recap, the first two methods work, the second two methods do not work. In the second sequence, the methods are called in the OnGet, but the page is not loaded into view. When putting the url in manually, the page is loaded into view correctly. Log in --> CardPage works, CardPage --> HierarchyPage does not work.
I will update this question with more code if needed.
If using Ajax , you should redirect to another page and pass parameter from client side in success callback function :
$.ajax({
...
}).done(function (data) {
window.location.replace(data.redirectUrl);
})
Server side will return Json result with parameter :
public ActionResult OnPost([FromBody]Cards cards)
{
...
return new JsonResult(new { redirectUrl = Url.Page("HierarchyPage", "Build", new { id = urlId }) });
}

Twilio on Call-Connect or Hangup get call details

I am using ASP.NET MVC Web calling with Twilio
Here is my Connect function
function callCustomer(phoneNumber) {
updateCallStatus("Calling " + phoneNumber + "...");
phoneNumber = phoneNumber.replace(/ /g, '');
var params = { To: phoneNumber };
Twilio.Device.connect(params);
}
Here is my Hangup function
function hangUp() {
Twilio.Device.disconnectAll();
}
Here is my TwiML Bin
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial callerId="++516xxx9999" record="record-from-answer">{{To}}</Dial>
</Response>
I am using Twilio client v1.6
//media.twiliocdn.com/sdk/js/client/v1.6/twilio.min.js
I want to collect complete information of each call as I connect to call or as I hang up the call like Call Duration, Call Sid, Record Sid, Call To, and other. Then with that information I would like implement play recorded call in my application.
I believe one way of doing it is set CALL STATUS CHANGES under Voice & Fax and receive all params.
This is how I ended up handling it.
/* Callback for when a call ends */
Twilio.Device.disconnect(function (connection) {
console.log(connection);
// Disable the hangup button and enable the call buttons
hangUpButton.prop("disabled", true);
callCustomerButtons.prop("disabled", false);
callSupportButton.prop("disabled", false);
updateCallStatus("Ready");
addCallLog(connection.parameters.CallSid);
});
addCallLog function
function addCallLog(id) {
var type = "";
var entityId = Number($("#Id").val());
$.ajax({
url: "/Phone/AddCallLog?callId=" + id,
type: "POST",
contentType: "application/json;",
success: function (data) {
// Handle Success Event
},
error: function (data) {
// Handle Error Event
}
});
}
Controller Method
[HttpPost]
public ActionResult AddCallLog(string callId,string type,int entityId)
{
TwilioClient.Init(_callSetting.Twilio.AccountSid, _callSetting.Twilio.Authtoken);
var records = CallResource.Read(parentCallSid: callId).ToList();
if (records.Any())
{
var callResource= records[0];
var parentRecord = CallResource.Fetch(pathSid: callId);
if (callResource.Status.ToString().Equals("completed", StringComparison.OrdinalIgnoreCase))
{
CallRecord callRecord = new CallRecord
{
EntityKey = entityId,
EntityType = type,
CallDateTimeUtc = callResource.DateCreated ?? DateTime.UtcNow,
CallSId = callResource.Sid,
ParentCallSId = callResource.ParentCallSid,
CalledById = _operatingUser.Id,
DurationInSeconds = parentRecord==null? Convert.ToDouble(callResource.Duration): Convert.ToDouble(parentRecord.Duration),
ToPhone = callResource.To,
CompanyId = _operatingUser.CompanyId
};
var callRecordResult= _callRecordService.Add(callRecord);
var recording = RecordingResource.Read(callSid: callId).ToList();
if (!recording.Any()) return Json(true);
foreach (RecordingResource recordingResource in recording)
{
using (var client = new WebClient())
{
var url =
"https://api.twilio.com" + recordingResource.Uri.Replace(".json", ".mp3");
var content = client.DownloadData(url);
CallRecordMedia callRecordMedia = new CallRecordMedia
{
CallRecordId = callRecordResult.Id,
ContentType = "audio/mpeg",
RecordingSId = recordingResource.Sid,
RecordingCallSId = recordingResource.CallSid,
FileType = "mp3",
Data = content,
Price = Convert.ToDouble(recordingResource.Price),
PriceUnit = recordingResource.PriceUnit,
DurationInSeconds = Convert.ToDouble(recordingResource.Duration)
};
_callRecordService.AddCallRecording(callRecordMedia);
}
}
}
}
return Json(true);
}

Live update map - Timer class - c#

I'm building a project with asp.net.
Part of the the project is a view (using google maps api) that is showing the the status of the parking lots with maerkers on the map.
Im using JSON file to create the markers.
Moreover, Im using arduino with some sensors that are indicated of the parking lot status.
I want that this Json will be update (override the previous) every 2 seconds (so that if a car enters the parking lot and now its full - it will present on the map as full)
I have 2 functions that creates this Json's and I want to call them every 2 seconds as I said before.
I could not do it. I'll be glad to receive your help.
The name of the view page: "TotalPs".
This is the controller in which the relevant function is located:
public ActionResult TotalPs()
{
ViewBag.Message = "TotalPs";
return View();
}
public ActionResult TotalPData()
{
ReadArduino(); //READ THE DATA FROM THE ARDUINO
callA(); // CREATES THE FIRST JSON
callB(); // CREATES THE 2ND JSON
var totalQueryParkingLot =
from lot in db.parkingLots
orderby lot.PricePerHour
select lot;
return Json(totalQueryParkingLot);
}
public void callA()
{
var totalQueryParkingLot =
from lot in db.parkingLots
orderby lot.PricePerHour
select lot;
var data2 = totalQueryParkingLot.ToList();
var jsonString2 = JsonConvert.SerializeObject(data2);
if (jsonString2 != null)
{
if (!Directory.Exists(Server.MapPath("~/Content/")))
{
Directory.CreateDirectory(Server.MapPath("~/Content/"));
}
}
System.IO.File.WriteAllText(Server.MapPath("~/Content/TotalJsonPL.json"), jsonString2);
}
public void callB()
{
var FreeQueryParkingLot =
from pub in db.publicParkings
orderby pub.PricePerHourpublicParking
select pub;
var data8 = FreeQueryParkingLot.ToList();
var jsonString3 = JsonConvert.SerializeObject(data8);
if (jsonString3 != null)
{
if (!Directory.Exists(Server.MapPath("~/Content/")))
{
Directory.CreateDirectory(Server.MapPath("~/Content/"));
}
}
System.IO.File.WriteAllText(Server.MapPath("~/Content/TotalJsonPU.json"), jsonString3);
}
public void ReadArduino()
{
SerialPort port = new SerialPort("COM3", 9600);
port.BaudRate = 9600;
port.PortName = "COM3";
port.Open();
bool status1 = true;
bool status2 = true;
bool status3 = true;
char[] arr = new char[4];
String data_arduino = port.ReadLine();
for (int i = 0; i < arr.Length; i++)
{
char first = data_arduino[i];
arr[i] = first;
}
int space = arr[0] - 48;
var arduinoQuery1 = from b in db.parkingLots where b.parkingLotID == 22 select b;
foreach (parkingLot parkingLot in arduinoQuery1)
{
parkingLot.freeSpaces = space;
}
db.SaveChanges();
}
In the view I call the function TotalPData() that is calling to the other functions.
Tnx!!
I am assuming that you are applying a ajax call to retrieve json data. So, you can assign interval using setInterval to execute ajax call periodically.
var interval = setInterval(ajaxCall, 5000); //5000 MS == 5 seconds
function ajaxCall() {
clearInterval(interval);
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: 'Controller/TotalPData',
dataType: "json",
success: function (response) {
interval = setInterval(ajaxCall, 5000);
// Do something
},
error: function (a, b, c) {
}
});
}
Also, It could be better to use SignalR to perform this kind of requirements.
SignalR

How to upload image from PhoneGap to Asp.Net Web API?

I have these available APIs:
[HttpPost]
[CorsEnabled]
[ActionName("upstream")]
public DTO.Callback UploadPhoto(Stream data)
{
var m = new Logic.Components.User();
return m.UploadPhoto(data, Common.UserValues().Email);
}
[HttpPost]
[CorsEnabled]
[ActionName("upbyte")]
public DTO.Callback UploadPhoto(byte[] data)
{
var m = new Logic.Components.User();
return m.UploadPhoto(data, Common.UserValues().Email);
}
[HttpPost]
[CorsEnabled]
[ActionName("upfile")]
public DTO.Callback UploadPhoto(HttpPostedFileBase data)
{
var m = new Logic.Components.User();
return m.UploadPhoto(data, Common.UserValues().Email);
}
[HttpPost]
[CorsEnabled]
[ActionName("up")]
public DTO.Callback UploadPhoto(DTO.UserPhoto data)
{
var m = new Logic.Components.User();
return m.UploadPhoto(data, Common.UserValues().Email);
}
UserPhoto class
public class UserPhoto
{
public string Base64 { get; set; }
public byte[] Data { get; set; }
}
In the behind code, I try to convert or get the equivalent byte[] of each request data.
If I would get the correct Image byte, then I'm good to go.
In my PhoneGap application, I have these codes:
A function that opens the camera:
takePicture: function (success, error) {
var s = function (data) {
navigator.camera.cleanup();
(success || angular.noop)(data);
},
e = function (data) {
navigator.camera.cleanup();
(error || angular.noop)(data);
};
navigator.camera.getPicture(s, e,
{
quality: 100,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.CAMERA,
encodingType: Camera.EncodingType.PNG,
correctOrientation: true
}
);
}
My first try is to convert the image to base64 string and use the 'up' API.
It works just fine for low quality not higher than 50. But the image becomes almost unrecognizable. So I set the quality to 100. And then the new problem comes, the phone hangs...
So I tried to use FileTransfer. Here is the code:
fileTransfer: function (filePath, serverUri, mimeType, params, success, error) {
var
u = $coreData.user.getSession()
;
var options = new FileUploadOptions();
options.fileKey = 'file';
options.mimeType = mimeType;
options.params = params;
options.chunkedMode = true;
options.headers = {
Connection: 'close',
Device: m.createDeviceHeader(),
'Authentication-Token': (u && u.SessionKey),
'Content-Type': 'application/json'
};
var ft = new FileTransfer();
ft.upload(filePath, encodeURI(serverUri), success, error, options);
}
Sample usage:
uploadFile: function (path) {
var def = $q.defer();
$coreUtility
.fileTransfer(path, $coreAPI.user.getUrl('upfile'), 'image/png', null,
function (success) {
def.resolve(m.callback(true, UPLOAD_PHOTO_SUCCESS, success));
},
function (error) {
def.reject(m.callback(false, UPLOAD_PHOTO_FAIL, error));
});
return def.promise;
}
But I was not able to upload the file, I always get not supported media type formatter and sometimes null reference exceptions. I'm totally out of idea.
Alright I get it now. For other struggling on the same problem, here is the solution.
[HttpPost]
[CorsEnabled]
[ActionName("upfile")]
public DTO.Callback UploadPhoto()
{
var m = new Logic.Components.User();
return m.UploadPhoto(HttpContext.Current.Request, Common.UserValues().Email);
}
Some logic:
public DTO.Callback UploadPhoto(HttpRequest req, string email)
{
if (req.Files.Count > 0)
{
var file = req.Files[0];
var m = new Logic.Components.User();
return m.UploadPhoto(file.InputStream, email);
}
return new DTO.Callback { Message = "Fail to upload. Make sure that you are uploading an image file." };
}
Some explanation about the solution:
The first part is your API, and the second part is the backend code.
To pass the image stream from PhoneGap to your ASP.Net MVC Web API, you will just have to use HttpContext.Current.Request to get the Stream from Phonegap.

Issues with HttpContext.Current.Response.Redirect in a class

I have written a class which gets the post data using jquerypost and then perform some crud operations. Its works fine adding stuff to the database , but it doesnt redirects to the page when passing certain data , below is the code :
$('#clickme').click(function (e) {
e.preventDefault();
indx = $("#DropDownList1 option:selected").index();
indx += 1;
var test = $("#DropDownList" + (indx + 1));
var url = "/Base/performOperations/shootme/"
jQuery.post(url, { name: jQuery("#name").val(), email: jQuery("#email").val(), federation: jQuery(test).val(), selectedscheme: jQuery("#DropDownList1").val() },
function (data) {
if (data == scheme1) {
window.location = "http://www.openme.com"
}
});
});
namespace pw
{
public class performOperations
{
public static string ShootMe() {
HttpRequest post = HttpContext.Current.Request;
string name = post["name"];
string email = post["email"];
string selectedscheme = post["selectedscheme"];
string federation = post["federation"];
string conn = System.Configuration.ConfigurationManager.AppSettings["mydb"];
string sql = "INSERT INTO dbo.mydb(Email,Name,schemes,LoginDate,schemeType) VALUES(#email,#name,#scheme,#dateTime,#federation)";
SqlHelper.ExecuteNonQuery(conn, CommandType.Text, sql,
new SqlParameter("#email", email),
new SqlParameter("#name", name),
new SqlParameter("#scheme", selectedscheme),
new SqlParameter("#dateTime", DateTime.Now),
new SqlParameter("#federation", federation));
return selectedscheme;
}
}
}
Any ideas why the redirect doesnt takes place, or am i doing it in a wrong way , i need to redirect to a particular page once the data is injected to the db.
Any assistance will be appreciated
If you are calling the POST method using AJAX, redirection at server side will not work.
You will have to redirect it at client side after request completion using javascript the request.
$('#clickme').click(function (e) {
e.preventDefault();
indx = $("#DropDownList1 option:selected").index();
indx += 1;
var test = $("#DropDownList" + (indx + 1));
var url = "/Base/sample/Hello/"
jQuery.post(url, { name: jQuery("#name").val(), email: jQuery("#email").val(), federation: jQuery(test).val(), selectedscheme: jQuery("#DropDownList1").val() },
function (data) {
if (data == "shoothim") {
window.location = "http://www.cuthishead.com"
}
else if(data == "shoother")
{
window.location = "http://www.chopherbody.com"
}
else if(data == "shootboth")
{
window.location = "http://www.fulldeath.tv"
}
});
return selectedscheme or URL from your page method
set window.location based on web method result on jquery
need to set WebMethod attribute for your C# method
Check Using jQuery to Call ASP.NET AJAX Page Methods – By Example

Categories

Resources