Hello I initialize the api/page using a simple web api controller, my question is how to reinitialize the xml values every few seconds without refreshing the whole page. The scope of this is to detect the database changes (if any) and show them in real time on the page.
My api is similar to this:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api
the api is initialized with data from the db, but i want to continously recall the function so the values would change if the the db values change.
Per your example, something like:
$(document).ready(function () {
setInterval(
// Send an AJAX request
$.getJSON(uri)
.done(function (data) {
//clear products list
$('#products').html('');
// On success, 'data' contains a list of products.
$.each(data, function (key, item) {
// Add a list item for the product.
$('<li>', { text: formatItem(item) }).appendTo($('#products'));
});
});
, 3000);
});
This will call your API endpoint every three seconds, clear the products element and reload the list. This method is called polling and will be called whether your data has changed or not. You could also implement this via SignalR, which would be closer to real-time and would be event driven refreshing, but that might be more complicated than what you are trying to do.
As per your request, this is a simple timer function
using System.Timers:
Timer timer; = new Timer();
timer.Interval = 10000; // 10 seconds, change this as required
timer.Elapsed += new ElapsedEventHandler(Timer_Elapsed);
timer.Enabled = true;
timer.Start();
void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
timer.Stop();
//Call your database function and initialize as you want here
timer.Start();
}
Or you can refer this (recommended)-
Scheduling Web Api method to run on set intervals
I think you are getting something from particular table and tables values always change right . Ones you are created web api and if you want to refresh . You need to schedule the api(url). Which means that just need to trigger it.
If you are using windows you can schedule your api(url).
step1: Go to control panel and select administrative Tools.
Step2: After that select tack scheduler .
step3: Right side Actions you can see "Create Task"
step4:it will redirect to new window . In general just add name .
Step4.1:- click on Trigger . There you can schedule it properly(Time and date or daily)
Step4.2:-After that click on Action . Create click new .
There youn can see Program\script: add your path like this:
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
one more option is there "Add Arguments(optional)": "Here you can
add your api link or url"
click ok . its completed . have a wonderful day. Thank you .
public class MachineController : ApiController
{
Machine[] machines = Gateway.test2(10);
public IEnumerable<Machine> GetAllMachines()
{
return machines;
}
public IHttpActionResult GetMachine(int id)
{
var machine = machines.FirstOrDefault((p) => p.id == id);
if (machine == null)
{
return NotFound();
}
else
return Ok(machine);
}
}
}
public static Machine[] test2(int i)
{
Machine[] result = new Machine[i+1];
for (int j = 0; j < 10; j++)
{
result[j] = testDatabaseCon(j + 1);
result[j].id = j + 1;
}
result[i] = new Machine();
result[i].name = "test";
result[i].description = "test";
result[i].EnteredCount++;
return result;
}
Related
I have looked all over for a solution to an issue. I have noticed that in my android app, every time I fire an event from <button Clicked="GPSToggle_Clicked">, for some reason it increments the number of times my methods get called. So after I compile and load this on my phone, I hit my "GPSToggle_Clicked" button, and then to stop hit that button again. On the first "stop", I'll get a single instance of the below output:
---------------------------------------------------------------Attempting string parsing
---------------------------------------------------------------Sending string to SubmitGeneratedGPX
---------------------------------------------------------------path: /storage/emulated/0/Download/GPX/2022-10-27-02-44-06.gpx
---------------------------------------------------------------GPX File creation success
---------------------------------------------------------------:RawBufferToJsonString: [{"Timestamp":"2022-10-27T18:43:52.849+00:00","Latitude":41.5263818,"Longitude":-81.6507923,"Altitude":153.29998779296875,"Accuracy":20.0,"VerticalAccuracy":1.7990270853042603,"Speed":null,"Course":null,"IsFromMockProvider":false,"AltitudeReferenceSystem":2},{"Timestamp":"2022-10-27T18:43:53.696+00:00","Latitude":41.5263819,"Longitude":-81.6507921,"Altitude":153.29998779296875,"Accuracy":20.0,"VerticalAccuracy":1.7697961330413818,"Speed":null,"Course":null,"IsFromMockProvider":false,"AltitudeReferenceSystem":2},{"Timestamp":"2022-10-27T18:43:54.526+00:00","Latitude":41.5263819,"Longitude":-81.6507921,"Altitude":153.29998779296875,"Accuracy":20.0,"VerticalAccuracy":1.7697961330413818,"Speed":null,"Course":null,"IsFromMockProvider":false,"AltitudeReferenceSystem":2},{"Timestamp":"2022-10-27T18:43:55.374+00:00","Latitude":41.5263819,"Longitude":-81.6507921,"Altitude":153.29998779296875,"Accuracy":20.0,"VerticalAccuracy":1.7697961330413818,"Speed":null,"Course":null,"IsFromMockProvider":false,"AltitudeReferenceSystem":2},{"Timestamp":"2022-10-27T18:43:56.21+00:00","Latitude":41.5263811,"Longitude":-81.650792,"Altitude":153.29998779296875,"Accuracy":20.0,"VerticalAccuracy":1.7160584926605225,"Speed":null,"Course":null,"IsFromMockProvider":false,"AltitudeReferenceSystem":2}]
Every subsequent time I hit start/stop on the app, I get the real-time data in the output multiplied by the number of times I've started/stopped since the last compiling.
the main app page button event thats fired:
private async void GPSToggle_Clicked(object sender, EventArgs e)
{
var LocationPermissionStatus = await Xamarin.Essentials.Permissions.RequestAsync<Xamarin.Essentials.Permissions.LocationAlways>();
var FileReadPermissionStatus = await Xamarin.Essentials.Permissions.RequestAsync<Xamarin.Essentials.Permissions.StorageRead>();
var FileWritePermissionStatus = await Xamarin.Essentials.Permissions.RequestAsync<Xamarin.Essentials.Permissions.StorageWrite>();
if(LocationPermissionStatus == Xamarin.Essentials.PermissionStatus.Denied)
{
// TODO
return;
}
// run if device is android
if(Device.RuntimePlatform == Device.Android)
{
if (!CrossGeolocator.Current.IsGeolocationAvailable || !CrossGeolocator.Current.IsGeolocationEnabled)
{
// gps is not enabled, throw alert
Console.WriteLine("---------------------------------------------------------------GPS is DISABLED");
await DisplayAlert("Error", "GPS is not enabled. You must enable GPS to use this feature", "Ok");
}
else
{
// set our IsTracking = true flag
if (!IsTracking)
{
// start background listening for GPS
await StartListening();
Console.WriteLine("---------------------------------------------------------------Listening: " + CrossGeolocator.Current.IsListening);
StartService();
Console.WriteLine("---------------------------------------------------------------Service initiated");
IsTracking = true;
Console.WriteLine("---------------------------------------------------------------Tracking initiated");
GPSToggle.Text = "Stop Tracking";
}
else
{
//
// verify that the submittal wasn't done in error, before stopping services and submitting data
bool DoneInError = await DisplayAlert("Finish?", "Are you sure you want to stop services and submit?", "No", "Yes");
if (!DoneInError)
{
await StopListening();
Console.WriteLine("---------------------------------------------------------------listening:" + CrossGeolocator.Current.IsListening);
IsTracking = false;
Console.WriteLine("---------------------------------------------------------------Tracking ceased");
// stop the gps service
StopService();
Console.WriteLine("---------------------------------------------------------------Service ceased");
// stop the background listening for gps
Console.WriteLine("---------------------------------------------------------------Attempt GPX parse from buffer obj");
GPSToggle.Text = "Start Tracking";
}
}
}
}
}
Specifically the line:
StartService();
Fires this method off within the same class, specifically the MessagingCenter.Send<>, which initiates my foreground service to handle logging the gps data into a buffer:
private void StartService()
{
var startServiceMessage = new StartServiceMessage();
MessagingCenter.Send(startServiceMessage, "ServiceStarted");
Preferences.Set("LocationServiceRunning", true);
StatusLabel.Text = "Location service has been started";
Console.WriteLine("---------------------------------------------------------------location service has been started. preferences saved");
}
and
StopService();
Fires this method off to stop the services and retrieve the gps buffer data from the foreground to the main thread:
private void StopService()
{
var stopServiceMessage = new StopServiceMessage();
MessagingCenter.Unsubscribe<App, List<Location>>(this, "GPXBufferData");
MessagingCenter.Subscribe<App, List<Location>>(this, "GPXBufferData", (sender, args) =>
{
RawGPXData = args;
Generate_CreateGPX_File(RawGPXData);
RawBufferToJsonString = GPXParse.GenerateJSON_GPXPoints(RawGPXData);
Console.WriteLine("---------------------------------------------------------------:RawBufferToJsonString: " + RawBufferToJsonString);
PromptForSubmission_GPXPoints_API();
});
Console.WriteLine("--------------------------------------------------------------------------");
MessagingCenter.Send(stopServiceMessage, "ServiceStopped");
Preferences.Set("LocationServiceRunning", false);
Console.WriteLine("---------------------------------------------------------------Location service stopped. preferences saved");
}
In the above snippet, this line is subscribed to in the GPSLocationService.cs file:
MessagingCenter.Send(stopServiceMessage, "ServiceStopped");
This is a portion of my GPSLocationService.cs file that is relevant to this:
public async Task Run(CancellationToken token)
{
int ObjCount = 0;
await Task.Run(async () => {
// if the task was stopped
// check the buffer for data, if data, send to GPXGenerator
MessagingCenter.Subscribe<StopServiceMessage>(this, "ServiceStopped",
message =>
{
if (GPSBufferObj != null)
{
Device.BeginInvokeOnMainThread(() =>
{
MessagingCenter.Unsubscribe<App, List<Location>>((App)Xamarin.Forms.Application.Current, "GPXBufferData");
MessagingCenter.Send<App, List<Location>>((App)Xamarin.Forms.Application.Current, "GPXBufferData", GPSBufferObj);
});
}
});
return;
}, token);
}
I believe I have tracked down where the issue is starting. In my StopService() method, I have the following line (just to keep track of where Im at in the buffer) and it is only sent to output once.
Console.WriteLine("--------------------------------------------------------------------------");
BUT if I place that same line within the pasted portion of my GPSLocationService.cs file, I will get the incremented output. I'm leaning towards the nested task being the issue, I wrote this based losely off of this example repro:
https://github.com/jfversluis/XFBackgroundLocationSample
You don't have MessagingCenter.Unsubscribe<StopServiceMessage> anywhere in your code. StopServiceMessage is what you are accumulating subscriptions to.
You need to make sure Unsubscribe is unsubscribing the instance that was previously subscribed to. It sounds to me like there are multiple instances of GPSLocationService. [In which case, this is no longer referring to the original instance. Unsubscribe won't do anything, unless you have the this that was originally Subscribed.]
If so, instead create an instance of GPSLocationService ONCE, and store it in a static variable. Re-use it. start/stop it, but don't discard it.
Alternatively, if you only want a message ONE TIME from each Subscribe, then Unsubscribe as soon as you receive each message:
MessagingCenter.Subscribe<StopServiceMessage>(this, "ServiceStopped",
message =>
{
MessagingCenter.Unsubscribe<StopServiceMessage>(this, "ServiceStopped");
... your code ...
});
Use this same pattern EVERYWHERE you have a Subscribe (unless you Subscribe ONLY ONE TIME at app start, as Jason suggested.)
This question has been answered multiple times, but I'm still not getting it.
I am using an example from this page: https://www.codeproject.com/Articles/1124691/SignalR-Progress-Bar-Simple-Example-Sending-Live-D
However, I've modified the SendProgress method a bit, to send the message only to the specific connection, not all clients:
public static void SendProgress(string connectionId, string progressMessage, int progressCount, int totalItems)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<RealTimeProgressBar.ProgressHub>();
var percentage = (progressCount * 100) / totalItems;
hubContext.Clients.Client(connectionId).AddProgress(progressMessage, percentage + "%");
}
I am getting connection Id from overrided OnConnected method:
public override Task OnConnected()
{
Environments.ConnectionId = Context.ConnectionId;
return base.OnConnected();
}
I'm currently storing it in a static class Environments, I guess there is a better way for doing so, but I don't know it yet :)
The question is, when I open for example two instances of my web page in browser, the progress bar is still being shown for all of these instances, though the connectionIds on these pages are different (checked it in SendProgress function).
What is wrong here?
Perhaps, I should change something in js code?
$(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) {
ProgressBarModal("show", message + " " + percentage);
$('#ProgressMessage').width(percentage);
if (percentage == "100%") {
ProgressBarModal();
}
};
$.connection.hub.start().done(function () {
var connectionId = $.connection.hub.id;
console.log(connectionId);
});
});
As i'm not really sure if js funcs really uses Environments.ConnectionId or smthing else..
Thanks!
Edit: What I want to achive is to send unique progress bars for every opened page. e.g. when I press the button on a first page, I'm starting to collect data and progress bar reflects that. I also open another page in a browser, and collect data from another source, so the progress bar should reflect that for this page as well.
my controller method:
public ActionResult Generate(InputDataViewModel viewModel, string connectionId)
{
...
var Tasks = getResultsWithProgressBar(viewModel.jobId, connectionId);
...
return RedirectToAction("Details", new { id = job.JobDataId });
}
and somewhere inside this "getResult" is a function that actually calls progress bar(SendProgress)
Your change should work fine.
You are using a static ConnectionId, so only your last browser, the browser you opened last(or connected last) will see the progress bar as well as updates.
This is the only one i made apart apart from adding in the static Environment.ConnectionId.
//PUSHING DATA TO ALL CLIENTS
// hubContext.Clients.All.AddProgress(progressMessage, percentage + "%");
hubContext.Clients.Client(Environments.ConnectionId).AddProgress(progressMessage, percentage + "%");
So if you open 10 browser windows and click on the button on all those, one by one, only the browser you initially opened last will see the the progress bar as well as updates, but it will see updates from all the 10 browsers shown on that single progress bar!
It is fun to see though!
When I tried your code, I saw the behavior you observed initially. But when I opened two separate Incognito browsers, I see the correct behavior. Close the IIS express site, rebuild and retry it.
Edit:
If you need each tab to have its own progress bar, then you will need one of the two approaches below:
A way to identify which rest request came from which connectionId. If you want to continue using the current code. Just pass in the connection Id to the LongRunningProcess. Client code below:
var progress = $.connection.progressHub;
var connectionId = $.connection.hub.id;
$.getJSON("/Home/LongRunningProcess",
{"connectionId":connectionId},
function (data) {
if (!data) {
alert("Success");
}
else
{
alert(data);
}
});
Server code would be:
public JsonResult LongRunningProcess(string connectionId)
{
//THIS COULD BE SOME LIST OF DATA
int itemsCount = 100;
for (int i = 0; i <= itemsCount; i++)
{
//SIMULATING SOME TASK
Thread.Sleep(500);
//CALLING A FUNCTION THAT CALCULATES PERCENTAGE AND SENDS THE DATA TO THE CLIENT
Functions.SendProgress(connectionId, "Process in progress...", i , itemsCount);
}
return Json("", JsonRequestBehavior.AllowGet);
}
The other option is to call a signalr method on the server instead of a rest method. Create a new hub method in the ProgressHub like below:
public void LongRunningHubMethod()
{
//THIS COULD BE SOME LIST OF DATA
int itemsCount = 100;
for (int i = 0; i <= itemsCount; i++)
{
//SIMULATING SOME TASK
Thread.Sleep(500);
//CALLING A FUNCTION THAT CALCULATES PERCENTAGE AND SENDS THE DATA TO THE CLIENT
Functions.SendProgress(Context.ConnectionId, "Process in progress...", i, itemsCount);
}
}
The client would then just call in the hub method on click like below:
var progress = $.connection.progressHub;
var connectionId = $.connection.hub.id;
progress.invoke('LongRunningHubMethod');
I'm working on an asp.net project (written awhile ago), and users say they want to be able to log out on "x'ing" out the tab or browser.
I've been looking around this site and have tried a few things with no luck. It doesn't seem like there is an easy way about this with there being different browsers.
First, here's a logout function which is called from a logout button.
public void LoginStatus1_LoggingOut(object sender, LoginCancelEventArgs e)
{
Response.Cookies.Clear();
Session.Clear();
HttpContext.Current.User = null;
FormsAuthentication.SignOut();
}
It would be nice if I could somehow call that when closing the tab/browser.
There is this javascript in the master page:
<script type="text/javascript">
// Copyright 2006-2007 javascript-array.com
var timeout = 500;
var closetimer = 0;
var ddmenuitem = 0;
// open hidden layer
function mopen(id)
{
// cancel close timer
mcancelclosetime();
// close old layer
if(ddmenuitem) ddmenuitem.style.visibility = 'hidden';
// get new layer and show it
ddmenuitem = document.getElementById(id);
ddmenuitem.style.visibility = 'visible';
}
// close showed layer
function mclose()
{
if(ddmenuitem) ddmenuitem.style.visibility = 'hidden';
}
// go close timer
function mclosetime()
{
closetimer = window.setTimeout(mclose, timeout);
}
// cancel close timer
function mcancelclosetime()
{
if(closetimer)
{
window.clearTimeout(closetimer);
closetimer = null;
}
}
// close layer when click-out
document.onclick = mclose;
</script>
However, it doesn't seem to work. I messed around w/ the timeout variable and no matter how long I wait after I exit the tab/browser, the cookies/session do not clear out. I know chrome has a "continue where you left off" feature, and other browsers have similar I'm sure. Unfortunately, all of the users don't use the same browsers.
Any ideas would be most welcome!
I have an HTTP server written in C# based off the HttpListenerContext class. The server is for processing binary log files and converting them to text, and can take quite a long time to do the conversion. I would like to indicate progress back to the user, but I am unsure on the best way to do this. On the server side, in handling my HTTP request, I essentially have this function:
public async Task HandleRequest()
{
try
{
await ProcessRequest();
}
catch (HttpListenerException)
{
// Something happened to the http connection, don't try to send anything
}
catch (Exception e)
{
SendFailureResponse(500);
}
}
Currently, ProcessRequest() sends the HTML response when finished, but I would like to essentially add the IProgress interface to the function and somehow indicate that progress back to the Web client. What is the best way to do this?
One way of doing it would be to store progress on server side and periodically pull the information from client.
However, if you want the server to notify the client ( push ), then you will need to implement some kind of bi-directional communication between the server and client (I am currently using ASP.NET Web API and SignalR to achieve this at work).
Here is what I got I'll try to explain and I hope you notice its not FULL FULL complete, you'll have to understand the logic behind this and accept or not as a plausible option.
The Method: Set a custom object to store progress of your ongoing operations, make a global static list containing this metadata. Notice how I track them with Ids: I don't store that on DB, the natural act of instantiating the class will auto_increment their Id.
Then, you can add a new controller to respond the progress of a particular ongoing process.
Now that you have a controller to respond the progress of an ongoing process by Id, you can create a javascript timer to call it and update the DOM.
When creating your process, dont hold the htmlrequest until its over, open a background operation instead and just respond with the newly created ProgressTracker.Id, through that class/list you can keep track of the progress and reply accordingly.
As said in another answer, when an operation finishes you can send a push notification and the clientside javascript will interrupt the timers and proceed to the next view/result/page, or you can increment the looping timer to detect when its done and call the results from another controller. (this way you can avoid using push if needed.)
Here is the partial code:
public class ProgressTracker {
private static GlobalIdProvider = 0;
public int _id = ++GlobalIdProvider;
public int Id { get { return _id; } }
bool IsInProgress = false;
bool IsComplete = false;
float Progress;
public YourProgressObject Data;
}
public class GlobalStatic {
public static List<ProgressTracker> FooOperations = new List<ProgressTracker>();
}
public class SomeWebApiController {
[HttpGet]
[Authorize]
public HttpResponseMessage GetProgress(int Id) {
var query = (from a in GlobalStatic.FooOperations where a.Id==Id select a);
if(!query.Any()) {
return Request.CreateResponse(HttpStatusCode.NotFound, "No operation with this Id found.");
} else {
return Request.CreateResponse(HttpStatusCode.Ok, query.First());
}
}
}
// this is javascript
// ... Your code until it starts the process.
// You'll have to get the ProgressTracker Id from the server somehow.
var InProgress = true;
window.setTimeout(function(e) {
var xmlhttp = new XMLHttpRequest();
var url = "<myHostSomething>/SomeWebApiController/GetProgress?Id="+theId;
xmlhttp.setRequestHeader("Authentication","bearer "+localStorage.getItem("access_token"));
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var data = JSON.parse(xmlhttp.responseText);
updateProgressBar(data);
}
}
xmlhttp.open("GET", url, true);
xmlhttp.send();
function updateProgressBar(data) {
document.getElementById("myProgressText").innerHTML = data.Progress;
}
}, 3000);
Disclaimer: If my javascript is shitty, pardon me but I'm too used to using jQuery and all this fancy stuff x_x
I have a small MVC website which is for a friends Hair Salon. On this page I have a div which is used to display a number which it takes from a database record. This number is the current number of people sitting in the queue waiting for a haircut.
What I have currently is the ability to logon to an "admin" page and update this number using a form, from say "2" to "5", then change "5" to "6" dependant on how many people are sitting in this queue.
This is a manual operation as it currently stands. Code is below:
=============================
Controller
[HttpPost]
public ActionResult Update(Data data)
{
if (ModelState.IsValid)
{
data.ID = 1; //EF need to know which row to update in the database.
db.Entry(data).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return View(data);
}
====================================
Model code
{
public class Data
{
public int ID { get; set; }
public string Queue_Number { get; set; }
}
public class DataDBContext : DbContext
{
public DbSet<Data>Queue { get; set; }
}
}
What I would really like to happen is that once you have manually updated the Queue Number from the form on the "admin" page I'd like an automatic count down of 20 minutes (the rough time it takes for the haircut) and then have the Queue Number auto-adjust down by one till it gets to "0".
e.g. We have 5 people in the queue, 20 minutes later it is auto adjusted to 4 people and the web page will auto update / refresh, then 2 more people walk in so we manually adjust it to 6 people in the queue and the timer starts again, each 20 min passes the queue is adjusted by -1 till it gets down to "0". Once it gets to "0" it stays there until we manually add more people to the queue.
I'm afraid I have no idea how to even begin with such a request, or even if it is possible?
I'd be really thankful for any help from the experts here that might be able to "babystep" it for me. Any information I've not provided I'll endeavour to add - I realise I'm not the best at explaining myself :-(
Have you considered Ajax? are you storing the last updated time on manually setting the flag? You can use Ajax request to simultaneously run using jquery Set interval. which will trigger the ajax request every 2 minutes. Find the last time it was updated, if that is passed 20 minutes then remove one from the database, your return would be the new number and jquery can update that number for you.
Quite a simple process actually but need more detail on the underlying data.
Here is how I can see it working from your question
In Controller
public ActionResult ajaxUpdate()
{
//open connection
dbcontext db = new dbcontext();
db.Connection.Open();
// get the last updated record in the database.
var entry = db.Entry.OrderByDecending(m=> m.LastUpdatedDate).FirstOrDefault();
//clean up
db.Connection.Close();
db.Dispose();
//return -1 as error
if(entry == null){
return Json(-1,JsonRequestBehavior.AllowGet);
}
// get current number of people in queue
Int32 numberOfPeople = entry.QueueNumber;
TimeSpan span = DateTime.Now.Subtract(entry.LastUpdatedDate);
if(span.Minutes >= 20){
// if 20 mins have passed assume a person has been completed since manual update
numberOfPeople--;
}
//this returns a number, alternatively you can return a Partial
return Json(numberOfPeople, JsonRequestBehavior.AllowGet);
}
Jquery and Ajax
$(document).ready(function () {
// run function every x minutes
setInterval(function () {
UpdateQueue();
}, 100000);
});
function UpdateQueue() {
$.ajax({
cache: true,
type: 'POST',
url: "/ControllerName/ajaxUpdate",
async: false,
dataType: "json",
success: function (result) {
// on success result will be the number returned
// -1 is error
if (result == -1) {
return;
}
// check the -- didn't return a negative
if (result < 0) {
result = 0;
}
//find your element in the HTML to update
$('#NumberElement').text().replaceWith(result);
}
});
}
You must ensure you include your jquery libraries before you include this code or you will have Jquery not defined.
I have made up for you server side solution with a little bit threading. Hope I am correct on critical sections locks.
It has an advantage that admin of your application does not have to hang on the page to get number of current customers downcounted (like he should with ajax requests).
How it works
On 'number of customers' update it is starting (if necessary) new counting-down thread, which waits (sleeps) for predefined interval and then decreases the number.
public class CustomerAdminService
{
// time in milliseconds it will take to decrease number of waiting customers
const int sleepTime = 10000;
// current number of customers (just for simplicity - you can have it in db or somewhere else)
static int numberOfCustomers;
static Thread updaterThread;
// object lock
static readonly object locker = new Object();
public int GetNumberOfCustomers()
{
return numberOfCustomers;
}
public void UpdateCustomers(int value)
{
lock (locker)
{
if (updaterThread == null)
{
//start new downcounting thread
updaterThread = new Thread(new ThreadStart(UpdateWorker));
updaterThread.Start();
}
SetNumberOfWaitingCustomers(value);
}
}
private void SetNumberOfWaitingCustomers(int value)
{
numberOfCustomers = value;
}
// downcounting thread method
private void UpdateWorker()
{
while (true)
{
// sleep for predefined time
Thread.Sleep(sleepTime);
lock (locker)
{
var number = GetNumberOfCustomers();
if (number <= 1)
{
// if number of currents customers is now zero - end the downcounting thread
SetNumberOfWaitingCustomers(0);
updaterThread = null;
return;
}
SetNumberOfWaitingCustomers(number - 1);
}
}
}
}
Comment: You can consider using jQuery for some timer down-counting script. Showing something like: You can be served in 40 minutes ;-)
Yes Ajax is the key. It can be used by your website to communicate with your server unnoticeably.
An alternative approach would be to not update the count in the database but simply use a query to determine the number of customers within a certain time period. You can do this by modifying the model so that instead of QueueNumber it uses an arrival time and changing the controller so that it inserts a new Data record.
{
public class Data
{
public int ID { get; set; }
public DateTime Arrival_Time { get; set; }
}
public class DataDBContext : DbContext
{
public DbSet<Data> Queue { get; set; }
}
}
This way, as others have suggested you can use AJAX to poll for the number of people in the queue with a controller action that might look something like this:
[HttpGet]
public ActionResult NumberOfPeopleInQueue()
{
var result = db.NumberOfCustomersSince(DateTime.Now.AddMinutes(-20));
return Json(result);
}
The nice thing about this approach is that should haircuts start to take longer (say 30 minutes) you can simply change the query and the application continues to work.