How to create tasks in the background - asp.net

I was wondering if it is possible if it is possible to create background tasks, what I mean is, in asp.net every user has his own instance so if I was to create a timer, when 2 users are on it will create 2 instances of a timer which I don't want.
I basically want a timer that is one instance and is independent. The reason I want to do this is because I want to do a SQL query every 5 minutes or so, and I don't want 2 users running it at the same time because then it wouldn't be 5 minutes apart it could be 2 mins and 30 secs apart.
USER1 --- ASP.NET INSTANCE
USER2 --- ASP.NET INSTANCE
TIMER --- NOTHING TO DO WITH USERS
I have hear of things like web services and Quartz.net but I am unsure that it is what I want.
Hopefully it makes sense :)

Actually, Quartz.NET is the best solution because it's made for it: running tasks on a scheduled basis.
If, for some reasons, you don't want to use it, the simplest way is to use a Timer object inside a singleton class; you can start the timer in your global.asax and this will provide you with a single timer that will be shared between all your users.
Something like:
public class SingletonTimer
{
private static SingletonTimer _instance = null;
private Timer _myTimer;
protected SingletonTimer()
{
_myTimer = new Timer(Callback, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
}
private void Callback(object state)
{
// SQL QUERY
}
public static SingletonTimer GetInstance()
{
return _instance ?? (_instance = new SingletonTimer());
}
}
This is however a not stable solution because after 20 minutes of inactivity IIS will kill your pool and and the timer will stop running; the best thing to do is to host a Quart.NET scheduler in a different process, like a windows service.

Related

Android JobScheduler job not running

My hybrid Cordova Android app uses one custom plugin in which I do a great deal of background work at intervals of ca 30 minutes. Up until now I have been using AlarmManger with a setInexact alarm to perform the work. The only real issue I have with that route is that the alarm does not survive a reboot. Given that I am now only supporting Android 6 (API 23)+ devices I am now experimenting with replacing AlarmManager with JobScheduler. My efforts thus far are shown below
public class UnyService extends JobService
{
#Override
public boolean onStartJob(JobParameters params)
{
UnyHandler.sendMessage(Message.obtain(UnyHandler,1,params));
return true;
}
#Override
public boolean onStopJob(JobParameters params)
{
UnyHandler.removeMessages(1);
return false;
}
where I am using a Handler to perform the actual work. The code for Handler is shown below
private Handler UnyHandler = new Handler(new Handler.Callback()
{
#Override
public boolean handleMessage(Message msg)
{
Feedback.postBackInfo("Handled it!");
jobFinished((JobParameters)msg.obj,false);
return true;
}
});
I then use the following code to get the job up and running
private void launchTimerJob()
{
timerJob =
(JobScheduler)context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(1,new
ComponentName(Utils.packName,UnyService.class.getName()));
builder.setPeriodic(30000);
builder.setPersisted(true);
builder.setBackoffCriteria(30000,JobInfo.BACKOFF_POLICY_LINEAR);
if (0 >= timerJob.schedule(builder.build()))
Feedback.postBackInfo("Job Build Error");
else Feedback.postBackInfo("Job Created and Scheduled");
}
where Feedback and Utils are other classes in my app which provide support services. To facilitate testing I am using a relatively small period of 30 seconds.
When I install and start the app the plugin init code calls launchTimerJob() and I get the "Job Created and Scheduled" notification back as expected.
From that point forward I had expected to get Handled It! notifications from Handler above at intervals of roughly 30s. A notification has turned up on the odd occasion but a totally arbitrary time measuring from App startup and has not obliged by repeating. Clearly, I am doing something wrong here.
Android docs could do a better job of mentioning that the minimum interval allowed for periodic jobs is 900,000 milliseconds, i.e.
15 minutes !!!!
I gather that prior to API 24 (Nogat) it was possible to use smaller intervals but no longer. Be wary of the various JobScheduler tutorials you will find out there. There are many that are quite dated and Android Jobs appears to be a still evolving API.
My own reason for originally using AlarmManager was to enable background tasks to be performed when the app was, well, backgrounded. However, with the coming of doze mode this strategy fails since the app will simply not get broadcast messages when the device is dozing.
Consider the following strategy instead
When the app is foregrounded you can quite simply use a Handler to manage periodic tasks - even those that happen at an interval of a few seconds.
No normal app should ever have to carry out background tasks at that frequency (every few seconds) when the phone is dozing - and when that is required there is a route via a specific request for the app to be exempted from battery optimizations. In such instances a periodic JobScheduler with a 15 minute period is the best you can do.

Running a background process/task updating database

I'm trying to find the way to keep the database updated, but the method which does it consumes a lot of time so I try to create a background task to do it.
I searched for solutions and I read this article of different options to run background processes: https://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx
But I don't know what's is the best solution out of those, like I'm trying to execute it outside the application. I found something about creating a Windows Service too, but I don't know how, I didn't manage to find some good examples.
What is the best way to keep my database updated everytime I access the application without losing the time it consumes? If you can help me to see the light I would appreciate that so much.
I'm really happy with FluentScheduler, which is handling all my mission-critical scheduling. As well as firing jobs on a scheduled basis, it can also do them on demand, like so:
// Define your job in its own class
public abstract class MyJob : IJob
{
public void Execute()
{
// Do stuff here...
}
}
// Schedule your job at startup
var runAt = DateTime.Today.AddHours(1); // 1am
if (runAt<DateTime.Now)
runAt = runAt.AddDays(1);
Schedule<MyJob>()
.WithName("My Job Name") // Job name, required for manually triggering
.NonReentrant() // Only allow one instance to run at a time
.ToRunOnceAt(runAt) // First execution date/time
.AndEvery(1).Days().At(runAt.Hour, runAt.Minute); // Run every day at the same time
// To manually trigger your job
ScheduledJobRegistry.RunTaskAsync("My Job Name");
I have the scheduled jobs running in a windows Service and use SignalR as a means of triggering them remotely from an MVC Web App when required.
You can use an async method. Just use a void instead of Task.
public async void LongRunningMethod () {
...
// Insert long running code here
...
}
Then call it and it will execute in the background. Be aware that you can have hidden exceptions without proper were handling.
You can also use Hangfire which is a pretty awesome background task scheduler
Here is an example of using Hangfire to run a daily task
RecurringJob.AddOrUpdate(() => Console.Write("Easy!"), Cron.Daily);

C# code to increment by 1 an item in Application state

How to write the C# code to increment by 1 an item in Application state named “total” in ASP.net?
In order to modify any Application variables, you need to lock them before modifying it to ensure no inadvertent changes between parallel requests happen.
An example
Application.Lock();
var userCount = Convert.ToInt32(Application["OnlineUserCount"]);
Application["OnlineUserCount"] = ++userCount;
Application.UnLock();
Application.Lock ensures that only one thread or request has access to the variables and other requests wait in queue. You modify the values as per the need and Application.Unlock to release your lock so other requests can work on Application variables.
Please note that there may be a performance hit, if you depend on this!!
Note: A page does not need to lock the application object to edit the
application collection. If one page tries to edit the application
collection without locking and a second page also tries to edit the
collection, no error is sent by IIS and the Application object ends up
in an inconsistent state.
Better use a
static variable
and
Interlocked.Increment
like this:
private static int total= 0;
public static void Increment()
{
Interlocked.Increment(ref total);
}

Using signalR to broadcast results from a timerjob?

I'm just getting started with SignalR and I'm wondering if it's a good tool for the task I'm working on.
In short, I have objects with properties that change over time. A timer job runs every once in a while to update these properties. For the sake of explanation, let's say I have MilkJugs with a property "isExpired" that changes once a certain DateTime is hit.
When my timerjob hits a MilkJug and flips it to isExpired = true, I want all clients to get a notification instantly. If a client is looking at seven MilkJugs in Chrome, I want them to see all seven MilkJugs turn yellow (or something like that).
Could I use signalR to "broadcast" these notifications to the clients from the timerJob? I just ran through the chat example they have up and it seems super simple to get working... I think I could do something like this serverside:
public class ChatHub : Hub
{
public void Send(List<MilkJugUpdate> updates)
{
// Call the broadcastMessage method to update milkJugs.
Clients.All.broadcastMessage(updates);
}
}
And then clientside just iterate over the serialized array, updating the appropriate fields in my JS viewModels.
Does this sound about right?
You have got the basic idea there. However there are probably some improvements you could make.
Here I assume you send the message every time you run the timer job. This isn't necessary. You only really need to send a message to the clients if something changes.
Firstly you could handle the onconnected event, and send the current state of the milk jugs.
Now when you run the timer job, you only need to call send if something has changed. Then you send the message to the clients, telling them what has changed. On the clients side, the function handles the change something like the following
Server
public class ChatHub : Hub
{
public override Task OnConnected()
{
//some code here to fetch current state of jugs.
return base.OnConnected();
}
public void JugExpired(MilkJugUpdate update)
{
// Call the broadcastMessage method to update milkJugs.
Clients.All.updateJug(update);
}
}
Client
ChatHub.client.updateJug = function(update) {
// code to update jug here
}
This saves you sending messages to the client if nothing has changed.
Similarly as pointed out in another answer, you can call the client method directly from your timer job, but again, I would only recommend sending updates, rather than the entire state every time.
Absolutely, ShootR does this already (HTML5 multiplayer game). This is also done in the Stock Ticker Sample on nuget.
Ultimately, you can grab the hub context outside of the hub and use it to send messages:
public void MyTimerFunction(object state)
{
GlobalHost.ConnectionManager.GetHubContext<ChatHub>().Clients.All.broadcastMessage(updates);
}

Quartz.NET trigger not firing

i am using Quartz.NET in my ASP.NET web application. i put the following code in a button click handler to make sure that it executes (for testing purposes):
Quartz.ISchedulerFactory factory = new Quartz.Impl.StdSchedulerFactory();
Quartz.IScheduler scheduler = factory.GetScheduler();
Quartz.JobDetail job = new Quartz.JobDetail("job", null, typeof(BackupJob));
Quartz.Trigger trigger = Quartz.TriggerUtils.MakeDailyTrigger(8, 30); // i edit this each time before compilation (for testing purposes)
trigger.StartTimeUtc = Quartz.TriggerUtils.GetEvenSecondDate(DateTime.UtcNow);
trigger.Name = "trigger";
scheduler.ScheduleJob(job, trigger);
scheduler.Start();
here's "BackupJob":
public class BackupJob : IJob
{
public BackupJob()
{
}
public void Execute(JobExecutionContext context)
{
NSG.BackupJobStart();
}
}
my question: why is "BackupJobStart()" not firing? i've used similar code before and it worked fine.
EDIT: #Andy White, i would have it in Application_Start in Global.asax. this doesn't work which is why i moved it to a button click handler to narrow down the problem.
Do you have the Quartz.NET logging hooked up? I once had a problem with a job not executing (I forget why), but once I got the Quartz.NET logging going, the problem was obvious.
It's worth a try (if you're not already doing it):
https://www.quartz-scheduler.net/documentation/quartz-2.x/quick-start.html
http://netcommon.sourceforge.net/
http://netcommon.sourceforge.net/documentation.html
Update: Simply add this to your program.cs to enable console logging:
Common.Logging.LogManager.Adapter = new Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter { Level = Common.Logging.LogLevel.Info};
Maybe it's a problem of time.
I've had the same problem as you, and I live in a country which time is UTC + 2. So, when I set the StartTimeUtc to the trigger, I used DateTime.Now, so the trigger didn't have to fire until two hours later, and I thought it has to be fired in the very moment my code started.
Look carefully the time of the trigger's execution and its StartTime
Another possibility is the way you're running the scheduler. I'm not totally sure, but you may run into problems trying to run a scheduling threads in an ASP.NET application. Putting the SchedulerFactory/Scheduler objects in a button click handler doesn't seem like it would give you the desired results.
You may need to create the scheduler at a more "global" level, so that it can run in the "background" of the application. It might also make sense to move any scheduled work into a separate windows service, so that you don't have to maintain the scheduler in the web app.
When you had success in the past, how were you invoking the scheduler?
In my case, there was an issue with IoC - there were some Interfaces that weren't implemented. I could see what was wrong with mine by adding logging:
Common.Logging.LogManager.Adapter = new Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter { Level = Common.Logging.LogLevel.Info};
to Program.cs as suggested by Andy White

Resources