1

I have a windows service which, among other things, needs to do some database maintenance every 24 hours. (SQL express, so can't schedule it inside the database)

For this I have created a Timer with a callback to the database maintenance method, but it appears to only get hit once (upon creation of the timer, I assume). I assumed this was due to the timer itself getting out of scope and being GC'd, but none of the sollutions I've tried seem to be working

The basic layout of the service is like this:

WindowsService.cs:

    protected override void OnStart(string[] args)
    {
        ThreadPool.QueueUserWorkItem(StartServices);
    }

    private void StartServices() { Manager.Start(); }

Manager.cs:

public class Manager
{
  private static Timer MaintenanceTimer;
  public static void Start()
  {
    MaintenanceTimer = new Timer(DoMaintenance);
    MaintenanceTimer.Change(new TimeSpan(0), new TimeSpan(24,0,0,0)); //I use 1 minute for testing
  }
}

Obviously this code is severely simplified, but this is pretty much what happens. As stated before, I believe GC is the problem, which made me try the following 2 things:

  • Use the constructor Timer(callback), so it will provide a self-reference to the callback. However, this would still not prevent it from running out of scope, so that's a no go.
  • Define the timer as a static variable inside the Manager class. This should prevent it from ever being GC'd, but still doesn't appear to have it be called every 24 hours.

Any help in this matter would be greatly appreciated

Kippie
  • 3,760
  • 3
  • 23
  • 38
  • are you aware of: http://www.codeproject.com/Articles/15407/SQL-Agent-A-Job-Scheduler-Framework ? – Mitch Wheat Feb 25 '13 at 09:17
  • failing that: lookup GC.KeepAlive() – Mitch Wheat Feb 25 '13 at 09:18
  • What happens in DoMaintenance? Is it possible an exception occurs? Does this behaviour happen in your testing? – Justin Harvey Feb 25 '13 at 09:18
  • @MitchWheat I'm not, and though I assume the suggestion, I feel that is a bit overkill. I'm also curious as to the reason why it's failing. About your second comment: Doesn't KeepAlive() prevent garbage collection up to the point where you make the statement? These are void methods, so I don't think this would be any good (though I may be wrong here) – Kippie Feb 25 '13 at 09:24
  • 1
    See http://stackoverflow.com/questions/4962172/why-does-a-system-timers-timer-survive-gc-but-not-system-threading-timer. Can you use a `System.Timers.Timer` instead of a `System.Timers.Timer` – Matthew Watson Feb 25 '13 at 09:24
  • @JustinHarvey DoMaintenance just calls a SP on SQL Server, and any errors are logged (none appear to occur) – Kippie Feb 25 '13 at 09:24
  • @MatthewWatson I could. The main reason for me to use the Threading.Timer was so it would be non-blocking, though I assume I could just create a new thread, in which I spawn a System.Timers.Timer – Kippie Feb 25 '13 at 09:32
  • Is it even enabled, It wont start, without being enabled. – Derek Feb 25 '13 at 09:36

2 Answers2

0

In the end I used a regular System.Timers.Timer which solved my problems. Still not sure where I went wrong with the System.Threading.Timer, though.

Kippie
  • 3,760
  • 3
  • 23
  • 38
0

Since you cannot use the SQL Server agent in SQL Server Express, the best solution is to create a SQL Script, and then run it as a scheduled task.

It i easy to verify and mantain, you could have multiple scheduled tasks to fit in with your backup schedule/retention.

The command I use in the scheduled task is:

C:\Program Files\Microsoft SQL Server\90\Tools\Binn\SQLCMD.EXE" -i"c:\path\to\sqlbackupScript.sql
Emanuele Greco
  • 12,551
  • 7
  • 51
  • 70