how to make service act dynamically based on service running condition - c#

hi friends i was trying to make my service act dynamically... i have set time for my service about for 2 min ,if suppose it was doin huge amount of work means it will exceeds that 2 min time limit then we need to check the service condition if work is pending means we need to run that instance until upto finish
so that i have tried this below code on googling ... i m having method were i need to cooperate in below service, can any one help me
public static void StartService(string serviceName, int timeoutMilliseconds)
{
ServiceController service = new ServiceController(serviceName);
try
{
TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);
service.Start();
service.WaitForStatus(ServiceControllerStatus.Running, timeout);
}
catch
{
// ...
}
}
as of now i m doing this below logic
protected override void OnStart(string[] args)
{
// my service name
Workjob("FTSCSVGenerator");
// ad 1: handle Elapsed event and CsvGenFromDatabase is method which i have to executed
timerjob.Elapsed += new ElapsedEventHandler(CsvGenFromDatabase);
// ad 2: set interval to 1 minute (= 60,000 milliseconds)
timerjob.Interval = Convert.ToDouble(DueTime);
// ////ad 3: enabling the timer
timerjob.Enabled = true;
eventLog1.WriteEntry("my service started");
}
protected override void OnStop()
{
eventLog1.WriteEntry("my service stopped");
}
private void Workjob(string servicename )
{
ServiceController servicecsv = new ServiceController(servicename);
if ((servicecsv.Status.Equals(ServiceControllerStatus.Stopped)) || (servicecsv.Status.Equals(ServiceControllerStatus.StopPending)))
{
// Start the service if the current status is stopped.
servicecsv.Start( );
}
else
{
// Stop the service if its status is not set to "Stopped".
servicecsv.Stop();
}
}

I have built services that operate in a similar manner before, my advice would be to NOT start and stop the service from external code. Instead, apply the Timer methodology within the service itself, which should always be running. On TimerElapsed, do work and then return to an idle state. Thus alleviating the need to start and stop.
Further, I would protect the "stop" of a service to not allow the stop if the service is "working"
Sample Code
Note: I employ a process I call "zeroing" with my timer. Zeroing, in my context, is the process of getting the events to fire on zero seconds of every minute. To do that, I first set the time to fire every second and I check to see if the seconds part of the current time is zero, once that occurs I switch the timer elapse to every minute. I do this to give myself some sanity while testing.
Also, my scheduling is configurable so every minute when it "ticks" i check my config to see if the process "should" execute. I do so with the following Xml Schema:
<?xml version="1.0" encoding="utf-8"?>
<ScheduleDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ScheduleInterval>1</ScheduleInterval>
<ScheduleUnits>min</ScheduleUnits>
<DailyStartTime>1753-01-01T08:00:00</DailyStartTime>
<ExcludedWeekDays>
<string>Sunday</string>
<string>Saturday</string>
</ExcludedWeekDays>
<ExcludedDates>
<string>12/25</string>
<string>02/02</string>
<string>03/17</string>
</ExcludedDates>
<DailyRunTimes>
<!-- code ommitted for size // -->
</DailyRunTimes>
</ScheduleDefinition>
Finally, this code sample is for a DataSync Services, so any references to "DataMigrationService" or "DataMigrationManager" are my own custom classes and are used as an abstraction to give me an object to control within the service.
... here's the code:
using System;
using System.Diagnostics;
using System.Reflection;
using System.ServiceProcess;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;
using DataMigration.Configuration;
using DataMigration.ObjectModel;
namespace DataSyncService
{
public partial class DataSyncService : ServiceBase
{
#region Private Members
private System.Timers.Timer _timer = null;
private SimpleScheduleManager.ScheduleDefinition _definition = null;
private DataMigrationManager _manager = new DataMigrationManager();
#endregion
#region Constructor(s)
public DataSyncService()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(AssemblyResolver.Resolve);
InitializeComponent();
}
~DataSyncService()
{
_manager = null;
_definition = null;
_timer = null;
}
#endregion
#region Public Method(s)
protected override void OnStart(string[] args)
{
Assembly assembly = Assembly.GetExecutingAssembly();
_manager.ProcessMonitor.Logger.Debug("Assembly Version: ", assembly.GetName().FullName);
assembly = null;
SetScheduleFromConfigurationFile();
_timer = new System.Timers.Timer(1000);
_timer.AutoReset = true;
_timer.Enabled = true;
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_ZeroingProcess);
_timer.Start();
}
protected override void OnStop()
{
_timer.Stop();
_timer.Enabled = false;
_timer = null;
// block if the Process is active!
if (_manager.State == DataMigrationState.Processing)
{
// I invented my own CancellableAsyncResult (back in the day), now you can use CancellationTokenSource
CancellableAsyncResult result = _manager.RequestCancel() as CancellableAsyncResult;
while (!result.IsCompleted) { Thread.Sleep(ServiceConstants.ThreadSleepCount); }
try
{
result.EndInvoke();
}
catch (Exception ex)
{
ProcessMonitorMessage message = ProcessMonitorMessage.GetErrorOccurredInstance();
message.EventType = ProcessMonitorEventType.ProcessAlert;
message.Severity = ProcessMessageSeverity.ErrorStop;
message.SubjectLine = "Error while stopping service. ";
message.EventDescription = ex.Message;
_manager.ProcessMonitor.ReportError(message);
}
}
}
#endregion
#region Private Method(s)
private bool MigrationIsScheduledToRunNow()
{
DateTime now = DateTime.Now;
foreach (string dowString in _definition.ExcludedWeekDays)
{
if (now.DayOfWeek.ToString().Equals(dowString))
{
Trace.WriteLine("Today is " + dowString, "Excluded by Schedule definition");
return false;
}
}
foreach (string datePart in _definition.ExcludedDates)
{
string dateString = datePart + "/2008"; // 2008 is a leap year so it "allows" all 366 possible dates.
DateTime excludedDate = Convert.ToDateTime(dateString);
if (excludedDate.Day.Equals(now.Day) && excludedDate.Month.Equals(now.Month))
{
Trace.WriteLine("Today is " + datePart, "Excluded by Schedule definition");
return false;
}
}
foreach (DateTime runTime in _definition.DailyRunTimes)
{
if (runTime.Hour.Equals(now.Hour) && runTime.Minute.Equals(now.Minute))
{
Trace.WriteLine("Confirmed Scheduled RunTime: " + runTime.TimeOfDay.ToString(), "Included by Schedule definition");
return true;
}
}
return false;
}
/// <summary>
/// Load Scheduling Configuration Options from the Xml Config file.
/// </summary>
private void SetScheduleFromConfigurationFile()
{
string basePath = AppDomain.CurrentDomain.BaseDirectory;
if (basePath.EndsWith("\\")) { basePath = basePath.Substring(0, basePath.Length - 1); }
string path = string.Format("{0}\\Scheduling\\scheduledefinition.xml", basePath);
_manager.ProcessMonitor.Logger.Debug("Configuration File Path", path);
XmlSerializer serializer = new XmlSerializer(typeof(SimpleScheduleManager.ScheduleDefinition));
XmlTextReader reader = new XmlTextReader(path);
reader.WhitespaceHandling = WhitespaceHandling.None;
_definition = serializer.Deserialize(reader) as SimpleScheduleManager.ScheduleDefinition;
reader = null;
serializer = null;
}
#endregion
#region Timer Events
private void _timer_ZeroingProcess(object sender, System.Timers.ElapsedEventArgs e)
{
if (DateTime.Now.Second.Equals(0))
{
_timer.Interval = 60000;
_timer.Elapsed -= new System.Timers.ElapsedEventHandler(_timer_ZeroingProcess);
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_Elapsed);
_timer_Elapsed(sender, e);
}
}
private void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
_manager.ProcessMonitor.Logger.Info("Timer Elapsed", DateTime.Now.ToString());
if (MigrationIsScheduledToRunNow())
{
switch (_manager.State)
{
case DataMigrationState.Idle:
_manager.ProcessMonitor.Logger.Info("DataMigration Manager is idle. Begin Processing.");
_manager.BeginMigration();
break;
case DataMigrationState.Failed:
_manager.ProcessMonitor.Logger.Warn("Data Migration is in failed state, Email <NotificationRecipients> alerting them.");
break;
default:
_manager.ProcessMonitor.Logger.Warn("DataMigration Manager is still processing. Skipping this iteration.");
break;
}
}
}
#endregion
}
}

Related

C# - Timer ticks shorter than specified in the interval causing duplicate jobs to run in parallel

I have a windows service that ticks over every 4 minutes. When the timer ticks if runs a DataImporter, the DataImporter has a number of "Jobs" that it can run on each tick, for example, there is a ProcessData job and a RetreiveData job:
RetreiveData job will reach out to 3rd party API and store data in DB for processing.
ProcessData job will take data from DB and process it into our usable DB etc.
As soon as the DataImporter is run it checks a DB table called ScheduledJob - this has a number of scheduling functionality such as FrequencyInterval, ActiveStart/Stop times, StartedLastRun time. The ScheduledJob table has flag called "InProgress", this flag will stop the DataImport picking up that job when it's already running.
There is a continuous issue where a job is picked up twice, a few seconds apart from each other and then both run simultaneously which cause a number of DB constraints when trying to insert identical records. I am not really sure how it can pick two jobs up at the same time, the tick is 4 minutes apart, so in theory it shouldn't be able to even look at the potential jobs to run, how can it run them both a few seconds apart?
Both the RetrieveData and ProcessData jobs need to be able to run in parallel so I can't pause the Timer whilst I execute the job.
Service:
public partial class DataImport : ServiceBase
{
private int _eventId = 0;
readonly Timer _serviceTimer = new Timer(240000);
public DataImport()
{
InitializeComponent();
ImportServiceEventLog.Source = ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource(); ;
}
protected override void OnStart(string[] args)
{
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " started", EventLogEntryType.Information, _eventId++);
_serviceTimer.AutoReset = true;
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " timer interval = " + _serviceTimer.Interval / 1000 + " seconds", EventLogEntryType.Information, _eventId++);
_serviceTimer.Elapsed += new ElapsedEventHandler(OnTimer);
_serviceTimer.Start();
}
protected override void OnStop()
{
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " stopped", EventLogEntryType.Information, _eventId++);
}
public void OnTimer(object sender, ElapsedEventArgs args)
{
try
{
Run();
}
catch (System.Exception ex)
{
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " error: " + ex.ToString(), EventLogEntryType.Information, _eventId++);
}
}
public void Run()
{
using (var dataImportController = new DataImportController())
{
dataImportController.Run();
}
}
}
DataImportController:
public class DataImportController
{
public void Run()
{
// Gets all the jobs from the ScheduledJob table in the DB
var jobs = GetJobsToRun();
//Get all Processes (from DB)
foreach (var job in jobs)
{
//Check the time it was last run - do this for each process
if (RunJob(job))
{
_messaging.EventMessage("Process " + job.Name + " finished : " + DateTime.Now, ServiceSource.DATA_IMPORT_SERVICE);
}
}
}
public bool RunJob(ScheduledJob job)
{
// Checks if the job is ready to run, i.e. is the InProgress flag set to false and the interval long enough since the StartedLastRun DateTime
if (!job.IsReadyToRun())
{
return false;
}
// Set job to in progress
job.InProgress = true;
job.StartedLastRun = DateTime.Now;
_scheduledJobRepository.Update(job);
_scheduledJobRepository.SaveChanges();
try
{
switch (job.Name.ToUpper())
{
case "RetreiveData":
// RUN JOB
break;
case "ProcessData":
// RUN JOB
break;
}
job.InProgress = false;
job.EndedLastRun = DateTime.Now;
_scheduledJobRepository.Update(job);
_scheduledJobRepository.SaveChanges();
}
catch (Exception exception)
{
_messaging.ReportError("Error occured whilst checking we are ready to run " + exception.Message, exception, null, 0, ServiceSource.DATA_IMPORT_SERVICE);
}
return true;
}
}
EDIT:
Include Program.cs
static void Main()
{
if (!Environment.UserInteractive)
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new DataImport()
};
ServiceBase.Run(ServicesToRun);
}
}
If overlapping is a concern, ditch the timer and make an async loop, leveraging Task.Delay:
async Task SomeFunc(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
DoWork();
await Task.Delay(timeInterval, token);
}
}
try to stop the timer inside the OnTimer function then re-start timer after it has finished executing your task.
You subsribe to timer event in OnStart, and didn't unsubscribe in OnStop.
Move _serviceTimer.Elapsed += new ElapsedEventHandler(OnTimer); and initialization of AutoReset to constructor. Stop timer in OnStop. That should fix your issue. I believe your service is started (restarted) more than once.

Thread creation in method which is being called for several times using timer

I have a timer using which i am calling a method - {OnElapsedTime} And this should responsible for database access and updation for every interval of time.
protected override void OnStart(string[] args)
{
try
{
ServiceLogFile("Service is started at " + DateTime.Now);
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = Int32.Parse(ConfigurationManager.AppSettings["tracktime"]); //number in miliseconds
timer.Enabled = true;
}
catch(Exception ex)
{
ServiceLogFile("Error in {OnStart} :" + ex.ToString());
}
}
private void OnElapsedTime(object source, ElapsedEventArgs e)
{
try
{
ServiceLogFile("Check for Database values - " + DateTime.Now);
th = new Thread(new ThreadStart(Autocancellation));
int ThreadID = Thread.CurrentThread.ManagedThreadId;
ServiceLogFile("Thread ID = " + ThreadID);
th.Start();
}
catch (Exception ex)
{
ServiceLogFile("Error in {OnElapsedTime} :" + ex.ToString());
}
}
public void Autocancellation()
{
try
{
lock (this)
{
//connection to database
//select Query and update
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message());
}
}
If we can see the above code - I am creating a new thread for every OnElapsedTime call.
Please help me with creating a single thread outside of this method or anywhere and using the same instance inside {OnElapsedTime} whenever this method is being called (In my case - 5 seconds)
Assuming
that we properly implement cooperative cancellation (otherwise there are no reliable ways to stop the currently running job)
And we are not forced to use threads - https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-parallel-library-tpl
And we are not required to run exactly once per 5 seconds (to accommodate the cooperative cancellation from bullet 1) - C# Timer elapsed one instance at a time
the simplest (or rather the simplest without any additional libraries) way would be to use Task, TaskCancellationSource and a timer with AutoReset = false
public class Service
{
private Task _job;
private CancellationTokenSource _cancellationTokenSource;
private readonly System.Timers.Timer _timer;
public Service()
{
_timer = new System.Timers.Timer();
{
_timer.AutoReset = false;
_timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;
_timer.Elapsed += OnElapsedTime;
}
}
public void Start()
{
Console.WriteLine("Starting service");
_timer.Start();
}
private void OnElapsedTime(object source, ElapsedEventArgs e)
{
Console.WriteLine("OnElapsedTime");
if (_job != null)
{
CancelAndWaitForPreviousJob();
}
Console.WriteLine("Starting new job");
_cancellationTokenSource = new CancellationTokenSource();
_job = Task.Run(
() => ExecuteJob(_cancellationTokenSource.Token),
_cancellationTokenSource.Token);
_timer.Start();
}
private void CancelAndWaitForPreviousJob()
{
_cancellationTokenSource.Cancel();
try
{
Console.WriteLine("Waiting for job to complete");
_job.Wait(
millisecondsTimeout: 5000); // Add additional timeout handling?
}
catch (OperationCanceledException canceledExc)
{
Console.WriteLine($"Cancelled the execution: {canceledExc}");
}
catch (Exception exc)
{
Console.WriteLine($"Some unexpected exception occurred - ignoring: {exc}");
}
}
private void ExecuteJob(CancellationToken cancellationToken)
{
Console.WriteLine("ExecuteJob start");
try
{
for (var i = 0; i < 10; i++)
{
Console.WriteLine($"Job loop Iteration {i}");
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Cancellation requested - ending ExecuteJob");
return;
}
Thread.Sleep(1000);
}
}
finally
{
Console.WriteLine("ExecuteJob end");
}
}
}
While it is a working solution you may be interested in Quartz.net - it has only a moderate learning curve and it is designed exactly for such scenarios.
P.S.: Also it seems that your application is a service based on https://learn.microsoft.com/en-Us/dotnet/api/system.serviceprocess.servicebase?view=netframework-4.8. In such a case you may be interested in topshelf - it greatly simplifies a lot of things related to services.

Call a method in a specific time of the day

I want to call a method in a specific time of the day without any need to request to any page :
UPDATE: i done it with the following class and without Task Schedule or something else
Something like windows schedule
I did it in a class:
public class Class1 {
private const string DummyCacheItemKey = "GagaGuguGigi";
protected void Application_Start(Object sender, EventArgs e) {
var Result = RegisterCacheEntry();
if (!Result) {
Debug.WriteLine("The DummyCacheItem is Alive!");
}
}
public bool RegisterCacheEntry() {
if (null != HttpContext.Current.Cache[DummyCacheItemKey])
return false;
try {
HttpContext.Current.Cache.Add(DummyCacheItemKey, "Test", null, DateTime.MaxValue, TimeSpan.FromMinutes(1), CacheItemPriority.Normal, new CacheItemRemovedCallback(CacheItemRemovedCallback));
}catch( Exception Ex) {
Debug.WriteLine("Exeption Error: " + Ex);
}
return true;
}
public void CacheItemRemovedCallback(string key, object value, CacheItemRemovedReason reason) {
Debug.WriteLine("Cache item callback: " + DateTime.Now.ToString() + " Removed!");
try {
HitPage();
}catch(Exception Ex) {
Debug.WriteLine("HitPage Was unsuccessful: " + Ex);
}
// Do the service works
DoWork();
//SendMail();
}
private const string DummyPageUrl = "http://localhost:53509/Page.cshtml";
private void HitPage() {
WebClient client = new WebClient();
client.DownloadData(DummyPageUrl);
}
protected void Application_BeginRequest(Object sender, EventArgs e) {
// If the dummy page is hit, then it means we want to add another item
// in cache
if (HttpContext.Current.Request.Url.ToString() == DummyPageUrl) {
// Add the item in cache and when succesful, do the work.
RegisterCacheEntry();
}
}
private void DoWork() {
Debug.WriteLine("Begin DoWork...");
Debug.WriteLine("Running as: " +
WindowsIdentity.GetCurrent().Name);
DoSomeFileWritingStuff();
Debug.WriteLine("End DoWork...");
}
private void DoSomeFileWritingStuff() {
Debug.WriteLine("Writing to file...");
try {
using (StreamWriter writer =
new StreamWriter(#"c:\temp\Cachecallback.txt", true)) {
writer.WriteLine("Cache Callback: {0}", DateTime.Now);
writer.Close();
}
} catch (Exception x) {
Debug.WriteLine("Error: " + x);
}
Debug.WriteLine("File write successful");
}
}
And here is the explanation of why i did this?
Is there a simpler way to do it?
use "Task Scheduler" to run the programme in a specific time.
Find it by typing "task scheduler" in start menu.
It depends what you are actually trying to do here. If you just need to execute some code at given times, use windows scheduler.
If you for some reason need to do this from yur web application you could use http://www.quartz-scheduler.net/ and host it within your web application. Just make sure your application pool is set to always run so the task scheduler stays alive.
24 hours timer is working perfectly
var DailyTime = "16:59:00";
var timeParts = DailyTime.Split(new char[1] { ':' });
var dateNow = DateTime.Now;
var date = new DateTime(dateNow.Year, dateNow.Month, dateNow.Day,
int.Parse(timeParts[0]), int.Parse(timeParts[1]), int.Parse(timeParts[2]));
TimeSpan ts;
if (date > dateNow)
ts = date - dateNow;
else
{
date = date.AddDays(1);
ts = date - dateNow;
}
//waits certan time and run the code
Task.Delay(ts).ContinueWith((x) => OnTimer());

Threads in c# to be executed at 1 minute intervals of server time

I have created a thread in my c# application. its code is given below.
[WebMethod]
public void start()
{
Thread thread = new Thread(new ThreadStart(WorkThreadFunction));
thread.Start();
}
[WebMethod]
public void stop()
{
Thread thread = new Thread(new ThreadStart(WorkThreadFunction));
thread.Abort();
}
public void WorkThreadFunction()
{
try
{
TimeZone zone = TimeZone.CurrentTimeZone;
DateTime dt = DateTime.Now.AddHours(12);
dt = dt.AddMinutes(30);
TimeSpan offset = zone.GetUtcOffset(DateTime.Now);
String s = "insert into tb_log(timestamp) values('" + dt + "')";
Class1 obj = new Class1();
string res = obj.executequery(s);
}
catch
{
}
}
When I run this code the value enters only at one time into the table. I need to execute this thread at 1 min intervals throughout the day, week and year. How to make this possible? Also correct me if the code which I had written is correct or not. I'm new to threads in c#. So someone please help me out. Thanks and Regards..
public WebServiceClass : WebService
{
private boolean terminated = false;
private boolean running = false;
[WebMethod]
public void start()
{
if (running)
{
//Already Running!
}
else
{
running = true;
terminated = false;
//Start a new thread to run at the requested interval
Thread thread = new Thread(new ThreadStart(WorkThreadFunction));
thread.Start();
}
}
[WebMethod]
public void stop()
{
//tell the thread to stop running after it has completed it's current loop
terminated = true;
}
public void WorkThreadFunction()
{
try
{
DateTime nextLoopStart = DateTime.Now;
while (!terminated)
{
TimeZone zone = TimeZone.CurrentTimeZone;
DateTime dt = DateTime.Now.AddHours(12);
dt = dt.AddMinutes(30);
TimeSpan offset = zone.GetUtcOffset(DateTime.Now);
String s = "insert into tb_log(timestamp) values('" + dt + "')";
Class1 obj = new Class1();
string res = obj.executequery(s);
while (DateTime.Now < nextLoopStart)
{
System.Threading.Thread.Sleep(100);
}
nextLoopStart += new TimeSpan(0,1,0);
}
//Reset terminated so that the host class knows the thread is no longer running
}
catch (ThreadAbortException)
{
//LogWarning("INFO: Thread aborted");
}
catch (Exception e)
{
//LogError("Error in Execute: " + e.Message);
}
finally
{
running = false;
}
}
}
I would use the Timer class in C#. I am not familiar with ASP.NET but I presume the following link would help. http://msdn.microsoft.com/en-us/library/system.web.ui.timer(v=vs.110).aspx
Create an instance of the timer, set the elapsed time in milliseconds and attach your method to the timer's tick event. This method would then be invoked after every x milliseconds.
EDIT: To run the task on a different thread, run it as a task(.NET 4.0 or upwards)
timer.tick += (s,e)=>{
TaskFactory.StartNew(()=> WorkThreadFunction());
};
Please note, exception handling has been ignored for simplicity.
For a simple solution I would use Timer class.
Actually there are 3 Timer classes in .NET, so it depends on your use. The most general is - System.Threading.Timer
For more robust and full solution I would use a timing framework, for example Quartz.NET
http://www.quartz-scheduler.net/
It all depends on your specific needs.
Try the following UPDATED
public class myApp
{
public System.Diagnostics.EventLog myEventLog { get; set; }
private Thread appThread;
public int TimerIntervalSeconds {get; set;}
public void Start()
{
appThread = new Thread(new ThreadStart(WorkThreadFunction));
appThread.Start();
}
public void Stop()
{
if (appThread != null)
{
appThread.Abort();
appThread.Join();
}
}
private void WorkThreadFunction()
{
// Loop until the thread gets aborted
try
{
while (true)
{
WriteToDatabase();
// Sleep for TimerIntervalSeconds
Thread.Sleep(TimerIntervalSeconds * 1000);
}
}
catch (ThreadAbortException)
{
myEventLog.WriteEntry("INFO: Thread aborted", System.Diagnostics.EventLogEntryType.Warning);
}
catch (Exception e)
{
myEventLog.WriteEntry("Error in Execute: " + e.Message, System.Diagnostics.EventLogEntryType.Error);
}
}
}
This is the 'complete' class. Call Start to set it off and Stop to end it. Set TimerIntervalSeconds for the frequency you want the event to happen.
I didn't have time initially to give the whole solution.

Multiple windows services in a single project = mystery

I'm having a bizarre issue that I haven't seen before and I'm thinking it MUST be something simple that I'm not seeing in my code.
I have a project with 2 windows services defined. One I've called DataSyncService, the other SubscriptionService. Both are added to the same project installer. Both use a timer control from System.Timers.
If I start both services together, they seem to work fine. The timers elapse at the appropriate time and everything looks okay. However, if I start either service individually, leaving the other stopped, everything goes haywire. The timer elapses constantly and on the wrong service. In other words, if I start the DataSyncService, the SubscriptionService timer elapses over and over. ...which is obviously strange.
The setup is similar to what I've done in the past so I'm really stumped. I even tried deleting both service and starting over but it doesn't seem to make a difference. At this point, I'm thinking I've made a simple error in the way I'm defining the services and my brain just won't let me see it. It must be creating some sort of threading issue that causes one service to race when the other is stopped. Here the code....
From Program.cs:
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new DataSyncService(),
new SubscriptionService()
};
ServiceBase.Run(ServicesToRun);
}
From ProjectInstaller.designer.cs:
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
this.dataSyncInstaller = new System.ServiceProcess.ServiceInstaller();
this.subscriptionInstaller = new System.ServiceProcess.ServiceInstaller();
//
// serviceProcessInstaller1
//
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
//
// dataSyncInstaller
//
this.dataSyncInstaller.DisplayName = "Data Sync Service";
this.dataSyncInstaller.ServiceName = "DataSyncService";
this.dataSyncInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
//
// subscriptionInstaller
//
this.subscriptionInstaller.DisplayName = "Subscription Service";
this.subscriptionInstaller.ServiceName = "SubscriptionService";
this.subscriptionInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
//
// ProjectInstaller
//
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
this.serviceProcessInstaller1,
this.dataSyncInstaller,
this.subscriptionInstaller});
}
private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
private System.ServiceProcess.ServiceInstaller dataSyncInstaller;
private System.ServiceProcess.ServiceInstaller subscriptionInstaller;
From DataSyncService.cs:
public static readonly int _defaultInterval = 43200000;
//log4net.ILog log;
public DataSyncService()
{
InitializeComponent();
//log = LogFactory.Instance.GetLogger(this);
}
protected override void OnStart(string[] args)
{
timer1.Interval = _defaultInterval; //GetInterval();
timer1.Enabled = true;
EventLog.WriteEntry("MyProj", "Data Sync Service Started", EventLogEntryType.Information);
//log.Info("Data Sync Service Started");
}
private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
EventLog.WriteEntry("MyProj", "Data Sync Timer Elapsed.", EventLogEntryType.Information);
}
private void InitializeComponent()
{
this.timer1 = new System.Timers.Timer();
((System.ComponentModel.ISupportInitialize)(this.timer1)).BeginInit();
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);
//
// DataSyncService
//
this.ServiceName = "DataSyncService";
((System.ComponentModel.ISupportInitialize)(this.timer1)).EndInit();
}
From SubscriptionService:
public static readonly int _defaultInterval = 300000;
//log4net.ILog log;
public SubscriptionService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
timer1.Interval = _defaultInterval; //GetInterval();
timer1.Enabled = true;
EventLog.WriteEntry("MyProj", "Subscription Service Started", EventLogEntryType.Information);
//log.Info("Subscription Service Started");
}
private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
EventLog.WriteEntry("MyProj", "Subscription Service Time Elapsed", EventLogEntryType.Information);
}
private void InitializeComponent() //in designer
{
this.timer1 = new System.Timers.Timer();
((System.ComponentModel.ISupportInitialize)(this.timer1)).BeginInit();
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);
//
// SubscriptionService
//
this.ServiceName = "SubscriptionService";
((System.ComponentModel.ISupportInitialize)(this.timer1)).EndInit();
}
Again, the problem is that the timer1_elapsed handler runs constantly when only one of the services is started. And it's the handler on the OPPOSITE service.
Anybody see anything?
In the Service.Designer.cs files InitializeComponent() methods, I'm missing
this.CanShutdown = true;
...and I shouldn't be enabling the timers there since I do it in the OnStart handlers.
So it should be something like:
private void InitializeComponent()
{
this.timer1 = new System.Timers.Timer();
((System.ComponentModel.ISupportInitialize)(this.timer1)).BeginInit();
//
// timer1
//
this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);
//
// DataSyncService
//
this.ServiceName = "DataSyncService";
this.CanShutdown = true;
((System.ComponentModel.ISupportInitialize)(this.timer1)).EndInit();
}

Categories