views:

161

answers:

7

I need a method to run every so often that does some database processing. However, I may need it to be triggerable by an admin on the site. But I don't want this method being run more than once at the same time, as this could cause issues with the way it hits the database.

For example, could I...

Create a singleton class that runs the method on a timer, and instantiate it in the global.asax file. Then, since it's a singleton, I can call it from my normal .aspx pages and call the method whenever I want. I would probably need to use that "lock" feature of C# to check to see if the method is already running.

I heard some talk lately that Singletons are "evil", but this seems like the perfect fit for it. What do you think? Thanks in advance.

A: 

What about just setting up a flag in the database and checking that to determine if the job is running or not? Seems simpler IMO.

Gavin Miller
+8  A: 

Timers and locks (that are intended to synchronize access to the database) are a bad idea on the web; you may have zero, one or many app-pools on different servers. They may recycle at any time, and won't be spun up until needed. Basically, this won't prevent you hammering the db from multiple sources.

Personally, I'd be tempted to either write a service to do this work (either db-polling, or via WCF etc), or use the db (a SP or similar) - set a flag in a table-row to say "in progress", do the work at the db, and clear the flag (duplicate attempts exit immediately while in progress).

Marc Gravell
+1  A: 

I would do it this way

  1. Build a normal ASP.NET page which does the processing
  2. StealBorrow LFSR Consultings idea for a flag in the DB which does the work of checking if the process is currently running
  3. Use normal cronjob or windows task scheduler to call the web page on a regular basis.

And Singletons aren't evil they just get abused easily.

Robert MacLean
Ahh credit where credit is due! :P
Gavin Miller
A: 

The canonical way to write a singleton ends up not being thread safe. Especially in a webby environment, where threads needn't even be on the same machine!

If you really want to do a "singleton", think of it as a service that you only ever deploy to one machine. Then use the transactional semantics of your database like Marc Gravell suggests to synchronize the locks.

Daren Thomas
A: 

We've done similar things by using a Web Service to do the backend processing, then writing a Desktop App to call it on whatever schedule we need. We can then run that app on a server, or an admin can run it directly from their PC to trigger the job.

Edit: After I saw your revision that you don't want them to run simulatenously, we have usually just controlled that with a database flag like a few others have said, nothing fancy but it gets the job done

Josh W.
A: 

Another option which Joel Spolsky mentioned in one of the SO Podcasts, i believe it was #20 something. Is to set an empty Cache object on application start with a certain expiration date, and in the CacheItemRemovedCallback make a call out to page or do some work and then reset the empty cache object.

I'm probably horribly mis-quoting him, so I recommend you listen or look through the transcripts for yourself.

bendewey
Is using the Cache class like using the Application class?
SkippyFire
You would setup the Cache on a Application event in Global.asax or a random page class and the Cache class is used to Add an empty object and handle the ItemRemovedCallback.
bendewey
A: 

Set an Application wide variable to denote that the process is running. That should be a little easier than storing the variable in the database, right?

SkippyFire
If you're using a Singelton it's the same difference. However, as Marc suggests above, it would be better to utilize a web service instead of a web app to run the scheduled task.
Gavin Miller