How to update status in db on webpage leave/closed? - c#

I want to update status flag 1 if user working on say 'XYZ' page and immediately update 0 when user leaves this page
i can able to update flag 1 by simple update query with linq in respective controller when user enters in.
but the problem is how to update flag 0 when user leaves or closes browser window directly
i tried adding following code :
window.onload = function () {
window.addEventListener("beforeunload", function (e) {
----------AJAX FUNCTION TO UPDATE DATA HERE--------------
});
};
But the problem is how to get callback of this alert-box
see the image here

You can't be certain that this will work consistently just by using clientside functionality, as there could be many ways a session ends, which would NOT be caught by this. Examples could be:
losing internet connection
losing power
server endpoint being down
If you want consistency in this, you have to back it up with something server-side, that "expires" sessions that have not been heard from in x minutes.
So my suggestion:
Set flag to 1 when user enters page.
Implement the code you already have to update flag to 0.
Run a server function (using hangfire for background jobs could be an option) and update all sessions with flag 1 to 0 when it has not been active for x minutes.

Related

Making async call to Controller function (void). Not getting control back to UI

I am working on a MVC 5 based report generating web application (Excel files). On one "GenerateReports" page, on button click, I am calling StartMonthly function. This takes control to a void method "GenerateReportMainMonthly" in the controller. This method calls another void method "GenerateReportMain". In GenerateReportMain, there are 5 other void functions that are being called.
I do not want the control to get stuck at back-end until the report generation is completed. On button click, an alert box should show "Report Generation started." and the control should come back to the "GenerateReports" page.
I have tried ajax but have not been able to get the control back to UI. How can I get the control back to the same page without waiting for the back-end process to complete?
$('#btnStart').on('click', StartMonthly);
function StartMonthly() {
var url = '/GenerateReport/GenerateReportMainMonthly';
window.location.href = url;
}
public void GenerateReportMainMonthly()
{
_isDaily = false;
GenerateReportMain();
}
It seems you are after running background tasks in your controllers. This is generally a bad idea (see this SO answer) as you might find that your server process has been killed mid-way and your client will have to handle it somehow.
If you absolutely must run long-ish processes in your controller and cannot extract it into a background worker of some sort, you can opt for something like this SO answer suggests. Implementation will vary depending on your setup and how fancy you are willing/able to go, but the basics will ramain the same:
you make an instantaneous call to initiate your long action and return back a job id to refer back to
your backend will process the task and update the status accordingly
your client will periodically check for status and do your desired behaviour when the job is reported complete.
If I were to tackle this problem I'd try to avoid periodic polling and rather opt for SignalR updates as describled in this blog post (this is not mine, I just googled it up for example).

How can i show connection status in real time on my form?

I'm creating an OPC client, that reads data from the server. I need to show status of connection with server in real time on my form. Can this be done?
For example:
private void checkStatus()
{
testValue.Text = cl.GetConnectionState().ToString();
}
cl.GetConnectionState() - method that shows connection status.
Add a timer to call that function. Function itself is fine, and should update the status correctly.
Although, if you want to do it right, I will say put this code in StateChange event handler. That way, your code will not run this function forever, and instead wait for the state to change.

BackgroundWorker in MVC3 application freezes UI

I'm developing MVC3 based web application, which at one point needs to cache large amount of data from some external database. Process takes about 10-30 min (depending on a traffic) so I put it in BackgroundWorker. When you click particular button on the webpage, using ajax it just access other method in controller, depending on the returned value proper information is displayed on user interface.
Controller:
if (IsDbLocked())
{
return this.Json(new
{
success = false,
message = "There is already an update requested by other user on the way."
});
}
this.model.DataUpdateBegin();
return this.Json(new { success = true });
Model:
public void DataUpdateBegin()
{
var backgroundWorker = new BackgroundWorker
{
WorkerSupportsCancellation = false,
WorkerReportsProgress = true
};
backgroundWorker.DoWork += this.DataUpdateWorkerDoWork;
backgroundWorker.ProgressChanged += this.DataUpdaterWorkerProgressChanged;
backgroundWorker.RunWorkerCompleted += this.DataUpdaterWorkerRunWorkerCompleted;
if (this.DataUpdateLockDb(true))
{
backgroundWorker.RunWorkerAsync();
}
}
Now when I do update, UI still freezes. While debuging controller I can see, that it starts BackgroundWorker and instantly continues to return statement (with success = true), but then it just finishes, and nothing else happens (returned message never reaches webpage).
I can see page from other browser/user and everything works ok, but this particular thread is locked for several minutes (not entire 10-30 min, as it's get unlocked after about 5 min - timeout?)
My question is, what I did wrong and how to fix it. I expect backgroundWorker to just run in the background, and free user to move around page wherever he wish. Also making an entry in database and making some external application just fetch it and do all the work (in real background) is not an option for me.
Do not use Background worker like this. Yes, the work will be done within another thread, but still in scope of that web request. Web requests are not ment to be alive for 30 minutes, there are plenty of things that can go wrong (timeouts, app pool restart, other IIS behaviour..)
If you have this type of long-running task, you should do it in some worker - windows service, maybe console application, etc. and from web you just start it (console) or set it to be done (message queue, azure queue)
Also, i hope you are not locking database (you method IsDbLocked()) for 30 minutes? Just do your import in transaction and use proper isolation level (read commited) so DB work normally all the time and the changes are there instantly when import finishes.
I'm developing MVC3 based web application
... so I put it in BackgroundWorker
BackgroundWorker is designed to work with Windows (forms) application; to achieve similar in web application, use Task.Factory.StartNew or Thread (more primitive)

scrollTo Paging called too fast

I am using the scrollTop function to detect how close a user is from the bottom of the screen to add more content. My javascript function is like this:
if ($(window).scrollTop() >= $(document).height() - $(window).height() - 75) {
var url = '<%=Url.Action("MoreView", "Status", new { id = ViewData["Id"],
page = Convert.ToInt32(ViewData["PageNumber"])+1 })%>';
NextPage(url); //this does an ajax call to the action method on the server
}
Everything is working fine, the problem is that the window.ScrollTop >75 is happeng sometimes 2 or 3 times so fast that it loads the same content 2 or 3 times. I can probably fix this by using a session variable on the server side but didn't want to go that route because that can get ugly IMO. Any suggestions?
As it stands right now you will always be making a request to the same URL (resulting in the same data every time, unless I mis-understand what you are doing). Once you fix that, you'll probably want to wrap your function that is called when the user scrolls in a debounce function (I'm using Underscore.js' debounce function for this example):
// Only call yourOnScroll once it hasn't been called
// for more than 100 milliseconds
yourOnScroll = _.debounce(youOnScroll, 100);

What's the correct way of posting this data asynchronously, and canceling/queuing new requests?

I am attempting to improve the stability of the web dashboard I have created. I have noticed that the main cause of getting into a bad state is when the user moves too quickly and requests get cut short.
For instance, I have the following javascript:
//When a resize occurs a lot of handling needs to happen to convert the user's action into
//a visibly-pleasing result. In addition, the server has to be spoken with in order to save the controls.
function OnClientResizing(pane, eventArgs) {
eventArgs.set_cancel(true);
var parameters = new Array();
parameters.push("Resize");
parameters.push(pane.get_id());
parameters.push(eventArgs.get_delta());
__doPostBack(pane.get_splitter()._uniqueID, parameters);
}
This function passes the hard work back to the server, so that it can calculate the appropriate ways to resize the controls on the page during resizes. This takes X seconds. If the user then resizes the page again before X seconds has elapsed -- I enter into a bad state. Either the old request gets cut off prematurely, or the new one runs at the same time. Either way, controls become mishapen on the page.
As such, I would like to queue future resizes, or play around with canceling current requests. I read that the best way to do something like this is to simply set a flag outside the scope of this function. I can do that, but I am not sure how to detect the end of a doPostBack. Am I supposed to change the javascript variable from the server-side somehow in PageRequestManager - EndRequest?
Cheers
First off, don't let your server participate in UI resize algorithms. Do that entirely client side. You can send resulting data to the server at any time, but don't make a real-time UI positioning depend upon a server response. That should be handled client-side with CSS or javascript logic.
Second off, if your code can't handle two ajax calls in flight at the same time, then your options are as follows:
Fix your code so it can handle sequential ajax responses in flight at the same time.
Cancel/ignore the first ajax response the moment you send a second one so that you ignore the response from the first and wait for the response from the second.
Prevent a second ajax request until the first one completes. I wouldn't suggest queueing them because that's just going to lead to an even worse user experience.
The details of how to do 1, 2 or 3 depend upon how your code works which you have not yet shared.
The easiest is option 3). That can be done with just a global flag. Just define a global variable, set it to true when you start an ajax call and clear it when the ajax call completes (in a completion function):
var ajaxInFlight = false; // global declaration
function OnClientResizing(pane, eventArgs) {
if (ajaxInFlight) return; // do nothing if ajax call already in flight
ajaxInFlight = true;
eventArgs.set_cancel(true);
var parameters = new Array();
parameters.push("Resize");
parameters.push(pane.get_id());
parameters.push(eventArgs.get_delta());
__doPostBack(pane.get_splitter()._uniqueID, parameters);
}
function postBackCompletionHandler(id, parms) {
ajaxInFlight = false; // clear global flag, ajax call done
// ... rest of your function here
}
You will also have to make sure that error conditions are handled so that the global flag is reset if the ajax call fails for any reason.

Categories