I need to capture the time a user spends on a page in my application.
I wish the time is in hour: minutes: seconds.
Using this I can track the user activities. I do some research but didn't found anything useful.
Is there any way to track the time a user spends on a page?
use a Stopwatch
Stopwatch timer;
protected override void OnAppearing()
{
timer = new Stopwatch();
timer.Start();
}
protected override void OnDisappearing()
{
timer.Stop();
TimeSpan ts = timer.Elapsed;
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}",
ts.Hours, ts.Minutes, ts.Seconds);
}
Related
I have a Xamarin.Forms project which uses a local database file Sqlite (the file is called datas.lite) from sqlite-net-pcl nuget package (version 1.2.0).
I have a table called Item:
public class Item
{
[PrimaryKey]
public int ID { get; set; }
public bool IsActive { get; set; }
}
And a repository ItemRepository with a static connection SQLite, I use to update the Item rows:
public class ItemRepository
{
private static SQLite.SQLiteConnection _conn;
private static SQLite.SQLiteConnection Conn
{
get
{
if (_conn == null)
_conn = new SQLite.SQLiteConnection("myPath/datas.lite");
return _conn
}
}
public ItemRepository()
{
Conn.CreateTable<Item>();
}
public Item GetById(int id)
{
return Conn.Get<Item>(id);
}
// Retrieves all items from table Item
public IEnumerable<Item> GetAll()
{
return Conn.Table<Item>();
}
// Updates the provided item
public int InsertOrReplace(Item item)
{
return Conn.InsertOrReplace(item, typeof(Item));
}
}
The app modifies the IsActive property for all items every 5 minutes by updating the Item table (The method TimerHelper.StartTimerInNewThread is called once at startup).
TimerHelper.StartTimerInNewThread(TimeSpan.FromSeconds(delais), ()
=>
{
try
{
// retrieve all items with DB
List<Item> items = repo.GetAll();
foreach (Item item in items)
{
item.IsActive = !item.IsActive;
if (repo.InsertOrReplace(item) == 1)
{
Log?.Info($"Item {item} has been updated in DB: IsActive = {repo.GetItem(item).IsActive}.");
}
else
{
throw new Exception($"InsertOrReplace() method returned a value != 1.");
}
}
}
catch (Exception ex)
{
// Log exception here
}
});
And immediately after updating the rows of table Item, I check (I log every IsActive property value for every Item) that the IsActive property of all items actually changed. So far, so good.
But if I let the application runs for several hours, sometimes, the check does not reflect the previous update...for instance, the application set the IsActive property for all items to TRUE, but the immediate request to the IsActive property returns FALSE for all items.
If I read via DbBrowser for Sqlite the table Item from the database local file (datas.lite), every item has its IsActive property set to TRUE, which is correct. So why the immediate read request I made after the update returned FALSE for all items, is there any caching that is active with sqlite? Or is it due to the fact that I have a static connection, and it is never closed, (it is moreover the recommanded way of doing according to microsoft documentation: https://learn.microsoft.com/en-us/xamarin/get-started/quickstarts/database?pivots=windows)
Thank you for any help
Here is how to protect timer code from being re-entered while it is still running.
Compare these three ways of running code repeatedly using a timer.
This is "typical" timer code. "seconds" is the time interval at which the work is done. Works fine if the code always finishes before the timer delay fires it again. Problematic if the timer code might take the ENTIRE time interval. (or if some background work, such as GC, takes enough time that your timer code eats the remaining time.) Really problematic if timer code takes so long that timer events start "piling up", starving the rest of your app from getting any time.
TYPICAL "NAIVE" TIMER CODE:
// Works fine if action is shorter than time delay.
// DON'T USE THIS CODE, if action might take longer than time delay.
using Timer = System.Timers.Timer;
private void StartFixedDelayTimer(float seconds, Action action)
{
_timer = new Timer(1000 * seconds);
_timer.Elapsed += (sender, e) => action();
// The timer event repeats until explicitly stopped.
_timer.Start();
}
SKIP-IF-BUSY TIMER CODE:
This is similar, but it protects itself by "skipping" work, if the work is still being done:
// For long running "action", increase "idleSeconds" to guarantee more time for other background tasks.
private void StartFixedDelayTimerSkipIfBusy(float seconds, Action action, float idleSeconds = 0.1f)
{
_timer = new Timer(1000 * seconds);
bool entered = false;
_timer.Elapsed += (sender, e) => {
if (entered)
// Timer code already running! Skip this one.
return;
entered = true;
try {
action();
// IMPORTANT: This is needed to "see and skip" next timer event,
// if it happens during "action". Without this, timer events can "pile up",
// starving other background tasks.
System.Threading.Thread.Sleep((int)(1000 * idleSeconds));
} finally {
entered = false;
}
};
// The timer event repeats until explicitly stopped.
_timer.Start();
}
VARIABLE-DELAY TIMER CODE:
This is an alternative approach. It doesn't request that the work be done again, until AFTER it finishes the first time. Here "seconds" is the amount of time between FINISHING the work, and the start of the next work. This is useful if you don't need the work done on a rigorous clock schedule. It has the advantage that no matter how long "action" takes, the rest of your app code gets "seconds" of cpu time before this work starts again - won't "starve" your app.
private void StartDelayBetweenWorkTimer(float seconds, Action action)
{
_timer = new Timer(1000 * seconds);
// Only fire the timer once. (But in Elapsed, we fire it again.)
_timer.AutoReset = false;
_timer.Elapsed += (sender, e) => {
action();
// Fire the timer again. Work will be done again "seconds" seconds after this statement is called.
_timer.Start();
};
_timer.Start();
}
Credit to the author from this site:
https://codinginfinite.com/creating-scheduler-task-seconds-minutes-hours-days/
I can define the task execution parameter in 24hr time to run my method, but I see no logic in the code that is clear to me as to why it fires my method by a large multiplicity of times. The issue is that where my method should be executed only but once per minute starting at the specified time, it is actually executed 20+ times in less than one minute. I've tried altering parameters and repeat intervals, but nothing seems to resolve the issue and different execution intervals always fire the method many times over than what is specified - i.e. similarly, 5min interval, but I get 30+ order placements within one minute whereas I would expect not more than one order execution every 5 minutes..
Any ideas on why this could be happening with this code?
Here is the service class:
public class SchedulerService
{
private static SchedulerService _instance;
private List<Timer> timers = new List<Timer>();
private SchedulerService() { }
public static SchedulerService Instance => _instance ?? (_instance = new SchedulerService());
public void ScheduleTask(int hour, int min, double intervalInHour, Action task)
{
DateTime now = DateTime.Now;
DateTime firstRun = new DateTime(now.Year, now.Month, now.Day, hour, min, 0, 0);
if (now > firstRun)
{
firstRun = firstRun.AddDays(1);
}
TimeSpan timeToGo = firstRun - now;
if (timeToGo <= TimeSpan.Zero)
{
timeToGo = TimeSpan.Zero;
}
var timer = new Timer(x =>
{
task.Invoke();
}, null, timeToGo, TimeSpan.FromHours(intervalInHour));
timers.Add(timer);
}
}
Here is the scheduler class:
public class Scheduler
{
public static void IntervalInSeconds(int hour, int sec, double interval, Action task)
{
interval = interval / 3600;
SchedulerService.Instance.ScheduleTask(hour, sec, interval, task);
}
public static void IntervalInMinutes(int hour, int min, double interval, Action task)
{
interval = interval / 60;
SchedulerService.Instance.ScheduleTask(hour, min, interval, task);
}
public static void IntervalInHours(int hour, int min, double interval, Action task)
{
SchedulerService.Instance.ScheduleTask(hour, min, interval, task);
}
public static void IntervalInDays(int hour, int min, double interval, Action task)
{
interval = interval * 24;
SchedulerService.Instance.ScheduleTask(hour, min, interval, task);
}
}
Instantiated on page load with defined start time parameters and repeat interval:
protected void Page_Load(object sender, EventArgs e)
{
Scheduler.IntervalInMinutes(20, 15, 1,
() => {
buyOrder();
});
}
At 20:15, call this method and repeat every minute:
private static void buyOrder()
{
//This is only a basic POST method
}
Result:
I get the multiplicity of orders executed in <1min as mentioned.
The issue appears not to be in the code but rather the fact that you are running the scheduling instruction in the Page_Load event.
My educated guess is if you debug this, you will find that your Page_Load event is firing multiple times which causes multiple jobs to be scheduled.
There are a number of reasons why this could happen (like multiple event handlers, Ajax requests, or even the act of debugging through the code itself as some browsers will reissue requests if they don't receive a timely response) but the bottom line is you should rethink how/where you trigger the scheduling.
I have scenario like this: Have two themes Light and Dark . I have a binding from the viewmodel what theme to add. I just don't know how to get time from device and organize . I need after 7 PM everyday the theme change to Dark , its Light by default. How to organize this?
I Suggest that you create an property in the App.xaml.cs, in the app Start/Resume methods, verify the hour of the day, and then on your ViewModel you will have access to this value.
App.xaml.cs:
public bool IsDarkTheme;
public App()
{
InitializeComponent();
MainPage = new MainPage();
}
public void SetTheme()
{
TimeSpan day = DateTime.Now.TimeOfDay;
if (!(day.Hours < 19 && day.Hours > 6))
{
IsDarkTheme = true;
}
}
protected override void OnStart()
{
// Handle when your app starts
SetTheme();
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
SetTheme();
}
Note: if the user opened the app before 7pm and resumes it after, and you want the page that was resumed to change apearence, you will need to add this to the pages (if you do this, you no longer need to define the value in the ViewModel constructor):
On the Pages xaml.cs:
protected override void OnAppearing()
{
((TheViewModelAssociated)BindingContext).TheViewModelProperty = App.IsDarkTheme;
base.OnAppearing();
}
Your ViewModel you access it like this:
App.IsDarkTheme
protected void Button1_OnClick(object sender, EventArgs e)
{
FineTuneDB();// Long Task running in db
SendSMStoAllClients();// Using Twolio API to send sms to all client long task
lblText.Text = "Button click is completed our system threads working on your request";
}
Is this possible that on button click I can response to client and independent long task going on separately.
If you don't care about whether task is completed or not, you call FineTuneDB method like this.
Action fineTuneDB = FineTuneDB;
fineTuneDB.BeginInvoke(null, null);
Asynchronous Method Invocation
Updated:
Action<int, string> fineTuneDB = FineTuneDB;
fineTuneDB.BeginInvoke((int)Session["id"],
Session["name"].ToString(), null, null);
// Your method will be like this
public void FineTuneDB(int id, string)
{
}
I'm writing an ASP.NET application. When a specific kind of request is being handled, I want to schedule a method to be called in a certain number of minutes after the request is handled. The postponed method does not need to communicate with the client that made the original request, it is just intended to do some "housekeeping" work. What is the best way to do this in an ASP.NET context? (It's ok to not fire the event if the application domain dies for some reason).
In Global.asax use this to check your incoming request:
protected void Application_BeginRequest(object sender, EventArgs e)
{
CheckRequest(HttpContext.Current.Request);
}
if your request is valid, register a cache entry:
private void CheckRequest(HttpRequest request)
{
if (request)
RegisterCacheEntry();
}
private void RegisterCacheEntry()
{
if (HttpRuntime.Cache[CacheItemKey] == null)
{
HttpRuntime.Cache.Add(CacheItemKey, "your key", null,
DateTime.Now.AddSeconds(60), //change to fire in whatever time frame you require
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
new CacheItemRemovedCallback(CacheItemRemovedCallback));
}
}
then process your function in the callback:
private void CacheItemRemovedCallback(string key, object value, CacheItemRemovedReason reason)
{
// execute your function
}
You could start a timer (System.Timers.Timer) from one of the application event in global.asax.cs (e.g. in Application_BeginRequest) after checking that it is required for that request.
Then, in the handler of the timer's Elapsed event, make sure that you stop the timer.
E.g. put something like this into global.asax.cs:
System.Timers.Timer _timer = null;
void Application_BeginRequest(object sender, EventArgs e)
{
// check if cleanup must be initiated
bool mustInitCleanup = RequestRequiresCleanup();
if ((_timer == null) && mustInitCleanup)
{
_timer = new System.Timers.Timer(5000);
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_Elapsed);
_timer.Start();
}
}
void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
_timer.Stop();
_timer = null;
// do cleanup task
}
Simply create a new thread to do the housekeeping work and at its beginning have it sleep for however long you want the server to wait before doing the action.
For example, somewhere in that specific request you want to call DoSomething:
aNewThread = new Thread(Foo);
aNewThread.Start();
public void Foo()
{
Thread.Sleep(5000);
DoSomething();
}