I'm trying to call a function at a regular interval.
The code below is written in the global.asax.cs file and it's working is ScheduleTaskTrigger() calls CheckLicenseExpiry() after 1 min. But I also want to call this function every , let's say 4 hrs.
is there anyway for achieving this by changing this code:
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//used here formating API//
HttpConfiguration config = GlobalConfiguration.Configuration;
config.Formatters.JsonFormatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
ScheduleTaskTrigger();
}
static void ScheduleTaskTrigger()
{
HttpRuntime.Cache.Add("ScheduledTaskTrigger",
string.Empty,
null,
Cache.NoAbsoluteExpiration,
// TimeSpan.FromMinutes(60), // Every 1 hour
// TimeSpan.FromMinutes(0.0167),
TimeSpan.FromMinutes(1),
CacheItemPriority.NotRemovable,
new CacheItemRemovedCallback(CheckLicenseExpiry));
}
static void CheckLicenseExpiry(string key, Object value, CacheItemRemovedReason reason)
{
CompanyHelper.CheckLicenseExpired();
}
If you're willing to use Windows Forms, use the "Timer" class.
using System.Windows.Forms;
//[...]
Timer timer;
private void Initialiser()
{
Timer timer = new Timer();
timer.Interval = 3600000;//Set interval to an hour in the form of milliseconds.
timer.Tick += new EventHandler(timer_Tick);
}
private void timer_Tick(object sender, EventArgs e)
{
//The function you wish to do every hour.
}
You can change the interval to anything given that it is in the form of milliseconds (1000 per second). (e.g. 1 minute: 60000, 4 hours: 14400000).
P.S You can just use the "System.Windows.Forms" library without necessarily having a Windows Forms program, I imagine you could use this code in WPF also).
Please use the System.Threading.Timer(MSDN link). It provides a mechanism for executing a method at specified intervals and does not depends on platform:
new System.Threading.Timer(_ =>
{
// do something here
},
null,
TimeSpan.FromMinutes(1) /*dueTime*/,
TimeSpan.FromHours(1) /*period*/
);
I recommend using Quartz.Net.
Global.asax
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
Scheduler.Start();
}
void Application_End(object sender, EventArgs e)
{
Scheduler.Stop();
}
}
Scheduler.cs (helper class)
using Quartz;
using Quartz.Impl;
public class Scheduler
{
private static IScheduler _scheduler;
public static void Start()
{
_scheduler = StdSchedulerFactory.GetDefaultScheduler();
_scheduler.Start();
ITrigger myTrigger = TriggerBuilder.Create()
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInHours(4)
.RepeatForever())
.Build();
IJobDetail myJob = JobBuilder.Create<MyJobClass>().Build();
_scheduler.ScheduleJob(myJob, myTrigger);
}
public static void Stop()
{
if (_scheduler != null)
{
_scheduler.Shutdown();
}
}
}
MyJobClass.cs
[DisallowConcurrentExecution]
public class MyJobClass : IJob
{
public void Execute(IJobExecutionContext context)
{
//do something
}
}
I hope this helps.
As I read from your sample, you want to check whether a license has expired on a regular basis. Solving this using the ASP.NET cache - which might work but maybe not in all scenarios (ASP.NET cache was built for a different purpose). Also, using timers is not a good idea as the application pool of the web application might not be running thus the task will not be executed.
Instead you could check on each request whether the license has expired. In order to save performance, you could only check if the last check was done more than 4 hours ago.
In ASP.NET MVC you can create an ActionFilter or AuthorizationFilter that implements this logic. You can apply this filter either globally to all routes or only to specific routes/controllers/actions.
As you want to block access to your site if the check is not successful, a custom AuthorizationFilter is a good approach. For details on ActionFilters, see this link, for a sample on implementing a custom AuthorizationFilter, see this link.
In the filter, you should be aware that there can be parallel requests that access the variable that stores the time of the last check. So you need to lock the resources in a appropriate way.
Related
To make it simple,
Let say I have a gRPC service:
At Startup.cs, I added the dependencies the services needed.
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
// Add dependencies
services.AddSingleton<IDoSomething1, DoSomething1>();
services.AddSingleton<IDoSomething2, DoSomething2>();
services.AddSingleton<IDoSomething3, DoSomething3>();
...
}
It is working fine. But now, I want to do have some jobs running at the background (like querying a DB/another service for some updates).
Hence, I do something like below (using Timer + BackgroundWorker) at Program.cs.
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
...
// Use the Startup class as shown above, so that I can using the dependencies added
webBuilder.UseStartup<Startup>();
}
public static void Main(string[] args)
{
// CreateHostBuilder -> Startup -> Dependencies added
using (var app = CreateHostBuilder(args).Build())
{
app.Start();
// Use Timer + BackgroundWorker here
BackgroundWorker backgroundWorker = new();
backgroundWorker.DoWork += BackgroundWorker_DoWork;
System.Timers.Timer timer = new();
timer.Interval = 2000;
timer.Enabled = true;
timer.Elapsed += (object sender, System.Timers.ElapsedEventArgs args) => backgroundWorker.RunWorkerAsync(app.Services);
app.WaitForShutdown();
}
}
private static void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
IServiceProvider sp = (IServiceProvider)e.Argument;
// Need to call GetRequiredService explicitly instead of automatically done by automatic constructor dependency injection
IDoSomething1 doSomething1 = sp.GetRequiredService<IDoSomething1>();
IDoSomething2 doSomething2 = sp.GetRequiredService<IDoSomething2>();
IDoSomething3 doSomething3 = sp.GetRequiredService<IDoSomething3>();
...
}
Although the code above should work, it does not seems elegant to me, because it needs to call GetRequiredService explicitly instead of automatically done by automatic constructor dependency injection like the gRPC service class.
Is any better way to do so? I mean something like app.AddBackgroundJob(...)?
Check out BackgroundService class and especially Timed background tasks example which might be what are you looking for. Once your logic is implemented in such class it is plugged into ASP.NET via:
services.AddHostedService<TimedHostedService>();
I'm trying build a worker service on Core 5.0. My tree is basically like that =>
1 -) Program.cs 2-) Worker.cs 3-) MyStartUp.cs 4-) Client.cs
In MyStartUp.cs I am getting a list and calling Client class some servers according to list.
In the Client class, I connect to the devices and write the data I read to the database.
Device count nearly 1200, server way is TCP/IP.
What is your best suggestion for write a worker service like that?
How can I use threads in it best form?
Below is my first try. This form is working but it's so slow for 1000 different client because there is so much reads in client.
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
StartUp startUp = new StartUp();
}
}
public class StartUp
{
public StartUp()
{
//... get client datas and initialize client object
StartClients();
}
public void StartClients()
{
foreach (var item in ClientList)
{
new Thread(item.Run).Start();
}
}
}
public class Client
{
System.Timers.Timer timer ;
public Client()
{
timer = new Timer();
timer.Interval = 100;
timer.Elapsed += Timer_Elapsed;
//... initialize client connection and database
}
public void Run()
{
timer.Start();
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
//... write values of read client to database
}
}
Say that you have 1k timers that run every 100ms, and say that each timer tick takes 50ms to execute. That means each timer needs 500ms/s, or 50% of one core, and you would need 500 cores to keep up with the work. You probably do not have that many cores, nor IO to process the requests, and that means the work will start piling up and your computer will more or less freeze since it does not have time to update the UI.
50ms might be an overestimation of the time used, but even at 5ms you would probably have issues unless you are running this on a monster server.
The solution would be to decrease the polling frequency to something more reasonable, say every 100s instead of every 100ms. Or to have one or more threads that polls your devices as fast as they can. For example something like:
private BlockingCollection<MyClient> clients = new ();
private List<Task> workers = new ();
public void StartWorker(){
workers.Add(Task.Run(Run));
void Run(){
foreach(var client in clients.GetConsumingEnumerable()){
// Do processing
clients.Add(client); // re-add client to queue
}
}
}
public void CloseAllWorkers(){
clients.CompleteAdding();
Task.WhenAll(workers).Wait();
}
I would note that usages of Thread is mostly superseded by tasks. And that creating a thread just to start a System.Timers.Timer is completely useless since the timer will run the tick event on the threadpool, regardless of the thread that started it. At least unless a synchronization object was specified.
I want to make a timer running in the server that runs a method() every 60 seconds
now i have done -sort of- that using the code below
public class Alarm
{
public Alarm(AppDbContext _db)
{
db = _db;
}
private static Timer aTimer;
private AppDbContext db;
public void StartTimerEvent()
{
// Create a timer and set a 60 second interval.
aTimer = new Timer();
aTimer.Interval = 60000;
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += (source, e) => CheckDBEvents(source, e);
// Have the timer fire repeated events (true is the default)
aTimer.AutoReset = true;
// Start the timer
aTimer.Enabled = true;
}
private void CheckDBEvents(Object source, ElapsedEventArgs e)
{
//get data from db with matching queries
List<Grocery> DataList = db.Grocery.Where(G => G.basic).Select(G => new Grocery
{
Id = G.Id,
Timeout = G.Timeout
}).ToList();
}
}
the method() is CheckDBEvents() and what it does is it accesses the dbcontext instance and looks for some data to save to to a local constant variable Called DataList
problem : every time i try passing the context (Dbcontext) instance --in the controller or any other class-- to the CheckDBEvents() method, the context is disposed -DbContext Disposed Exception.
the caller
var t = new Alarm(db);
t.StartTimerEvent();
My tires :-
making alarm a static class :
Now if i can do that It would be amazing ... but can not operate on DbContext since you can't call instance on DbContext in a static class you have to pass it from who ever calls it, which leads to the same problem :db is Disposed and don't get passed
public static void StartTimerEvent(AppDbContext db)
{
.....
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += (source, e) => CheckDBEvents(source, e, db
//DbContext is Disposed here and
//don't get passed'
);
also constant classes and Dbcontext don't get along very well from what i have read.
Long Story Short
-I want to keep instance of dbContext in another class rather than controllers.
without it being disposed.
if anyone have and idea what how to do this or have a source code to server side timer or anything like that please comment, I have been stuck for 2 days
finally I have found the problem after many test,
I needed to take advantage of the strong Dependency Injection that asp.net has, and add the class as a service. Also, I used IHostedService as an interface for my service class, here is an example of the service FinalTest (renamed Alarm to FinalTest)
internal class FinalTest : IHostedService, IDisposable
{
private Timer aTimer;
public static List<Grocery> List;
private AppDbContext db;
// variable to test that timer really works
public static int test2;
public FinalTest( AppDbContext _db )
{
db = _db;
}
//This method runs at the start of the application once only as FinalTest was set as Singleton in services
public Task StartAsync(CancellationToken cancellationToken)
{
test2 = 1;
aTimer =new Timer(CheckDBEvents, null , TimeSpan.Zero , TimeSpan.FromSeconds(10) );
return Task.CompletedTask;
}
//Method runs every TimeSpan.FromSeconds(10)
private void CheckDBEvents(object state)
{
var users = from u in db.Grocery where u.basic == true select u;
List = users.ToList();
//increase test2 To see changes in the real world
test2++;
}
//--------shutdown operations---------//
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public void Dispose()
{
aTimer?.Dispose();
}
}
Now if I injected this in the service services.AddSingleton(FinalTest) as it is I would get a scoped exception because using AppDbContext which is a scoped service in Singleton service is not good and effectively promote AppDbContext to Singleton which is goint to cause problems in the future, so I had to create another constructor for AppDbContext.
public class AppDbContext : DbContext
{
//Scoped constructor
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
//Singletone constructor
public AppDbContext(DbContextOptions<AppDbContext> options,string connection)
{
connectionString = connection;
}
private string connectionString;
//this is an override to OnConfiguring that's
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (connectionString != null)
{
var config = connectionString;
optionsBuilder.UseSqlServer(config);
}
base.OnConfiguring(optionsBuilder);
}
//DbSet
public DbSet<Grocery> Grocery { get; set; }
}
finally the adding both AppDbContext and FinalTest to services
var connection = #"Server=(localdb)\mssqllocaldb;Database=FridgeServer.AspNetCore.NewDb;Trusted_Connection=True;ConnectRetryCount=0";
services.AddDbContext<AppDbContext>(
options => {
//options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
options.UseSqlServer(connection);
//var config = Configuration["Data:DefaultConnection:ConnectionString"];
//options.UseInMemoryDatabase("Grocery")
}
);
services.AddSingleton<IHostedService,FinalTest>(s => new FinalTest(new AppDbContext(null, connection) ));
Now that's my experience and it was all in all a fun experience reading all about Dependency injection and Ioc and other concept and pattern of programing
if anyone face some of those problem or even want to know more about asp.net, here are some that helped ,the first one is the most important
http://deviq.com/category/principles/
http://deviq.com/dependency-inversion-principle/
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1
Use DbContext in ASP .Net Singleton Injected Class
https://blogs.msdn.microsoft.com/cesardelatorre/2017/11/18/implementing-background-tasks-in-microservices-with-ihostedservice-and-the-backgroundservice-class-net-core-2-x/
thanks to #KienChu for telling me about IHostedService , I Hope this helps someone
I would be weary of injecting DbContext directy into the hosted service.
My advice would be to inject IServiceScopeFactory. And every time your alarm goes off, create a scope, and resolve the DbContext just for that CheckDBEvents action.
Even your links to other questions recommend doing so. This way you manage the DbContext lifetime a bit more gracefully.
Something like this:
var scope = serviceFactory.CreateScope();
await using var dbContext = scope.ServiceProvider.GetService<MyDbContext>();
I have function which reads Data out of an Webservice. With that Data i create Bitmaps. I send the Bitmaps to Panels (Displays) which displays the created Bitmaps. Manually its working like charm. What i need now is, that my Application run this function every 5 min automtically in the Backround.
My Application is running under IIS. How can i do that? Can someone help me with that?
You don't have to be depended on asp.net project, but you can use Cache Callback to do it.
I have found a nice approach, to do it.
actually i don't remember the link so i'll give you a code that i use:
public abstract class Job
{
protected Job()
{
Run();
}
protected abstract void Execute();
protected abstract TimeSpan Interval { get; }
private void Callback(string key, object value, CacheItemRemovedReason reason)
{
if (reason == CacheItemRemovedReason.Expired)
{
Execute();
Run();
}
}
protected void Run()
{
HttpRuntime.Cache.Add(GetType().ToString(), this, null,
Cache.NoAbsoluteExpiration, Interval, CacheItemPriority.Normal, Callback);
}
}
Here is the implementation
public class EmailJob : Job
{
protected override void Execute()
{
// TODO: send email to whole users that are registered
}
protected override TimeSpan Interval
{
get { return new TimeSpan(0, 10, 0); }
}
}
An Asp.Net application is not the correct framework for a task like this.
You should probably create a dedicated service for this type of tasks.
Another option is to create a scheduled task that will run every X minutes
On a side note, if you must do this through your asp.net application, I recommend reading on how to Simulate a Windows Service using ASP.NET to run scheduled jobs
I need to perform periodically a certain task in my asp.net app so did like this:
protected void Application_Start()
{
Worker.Start();
}
...
public static class Worker
{
public static void Start()
{
ThreadPool.QueueUserWorkItem(o => Work());
}
public static void Work()
{
while (true)
{
Thread.Sleep(1200000);
//do stuff
}
}
}
is this approach good ?
I saw a blog about the badge awarding on this site is done using an asp.net cache hack:
https://blog.stackoverflow.com/2008/07/easy-background-tasks-in-aspnet/
You can use Timer class for task like this. I'm using this class in my own ASP.NET chat module for closing rooms after some expiration time and it works fine.
And I think, it's better approach than using Thread.Sleep
Below example code:
using System;
using System.IO;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Worker.Start();
Thread.Sleep(2000);
}
public static class Worker
{
private static Timer timer;
public static void Start()
{
//Work(new object());
int period = 1000;
timer = new Timer(new TimerCallback(Work), null, period, period);
}
public static void Work(object stateInfo)
{
TextWriter tw = new StreamWriter(#"w:\date.txt");
// write a line of text to the file
tw.WriteLine(DateTime.Now);
// close the stream
tw.Close();
}
}
}
}
Your method will work, but as Lucasus said, better approach will be using Timer class.
Other than that if you own the computer where your site is running I would recommend using Windows service for your scheduling tasks. This approach will proove itself more beneficial than timers of any kind inside of asp.net infrastructure. That is because everything that is working inside asp.net is going to be managed by asp.net engine and it is not something you want. For example worker process can be recycled and at this moment your task will break.
Detailed information about timers in windows services can be found here: Timers and windows services.
Information about windows services can be found here: Windows services
To hoock timer into windows service you need to create it at the start and handle events that it fires.
If you want do do a scheduled work, why not use Windows Task Scheduler ?
Some info I found, may be useful: http://www.codeproject.com/KB/cs/tsnewlib.aspx
Kris