Call a method in a specific time of the day - c#

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());

Related

C# Thread.Sleep(ms) Freezes UI and I can't use Alternative Options

I am Making C# WPF Application That contains CSV Writing.
But, I wanna give some delays. But When i use
Thread.Sleep(ms),
UI freezes and Windows says that This Program has been Stopped.
So, I found some Alternatives like
private static DateTime Delay(int MS)
{
DateTime ThisMoment = DateTime.Now;
TimeSpan duration = new TimeSpan(0, 0, 0, 0, MS);
DateTime AfterWards = ThisMoment.Add(duration);
while (AfterWards >= ThisMoment)
{
if (System.Windows.Application.Current != null)
{
System.Windows.Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new Action(delegate { }));
}
ThisMoment = DateTime.Now;
}
return DateTime.Now;
}
But,when I use this method, The Program just Stops.
This is Button Code that makes, writes CSV File.
private void button_Click(object sender, RoutedEventArgs e)
{
try
{
System.IO.StreamWriter file = new System.IO.StreamWriter(#"New.csv");
int a = 1;
file.WriteLine("Delta, Theta");
while (a < 20)
{
file.WriteLine("{0}, {1}", TGLatestData.EegPowerDelta, TGLatestData.EegPowerTheta);
a++;
file.Close();
Thread.Sleep(3000); //Delay(3000); When I use Alternative way.
}
}
catch (Exception ex)
{
throw new ApplicationException("Broken", ex);
}
}
The Main Question that I want to know is how to delay some seconds without Freezing UI. Sorry for Bad Grammar.
Make your click handler async. Then you can use Task.Delay, which will not block the thread.
private async void button_Click(object sender, RoutedEventArgs e)
{
try
{
System.IO.StreamWriter file = new System.IO.StreamWriter(#"New.csv");
int a = 1;
file.WriteLine("Delta, Theta");
while (a < 20)
{
file.WriteLine("{0}, {1}", TGLatestData.EegPowerDelta, TGLatestData.EegPowerTheta);
a++;
file.Close();
await Task.Delay(3000);
}
}
catch (Exception ex)
{
throw new ApplicationException("Broken", ex);
}
}
By the way, here's one way to fix your exception:
private async void button_Click(object sender, RoutedEventArgs e)
{
const string fileName = #"New.csv";
try
{
File.AppendAllText(fileName, "Delta, Theta");
for (var a=1; a<20; a++)
{
var text = string.Format("{0}, {1}", TGLatestData.EegPowerDelta, TGLatestData.EegPowerTheta);
File.AppendAllText(fileName, text);
await Task.Delay(3000);
}
}
catch (Exception ex)
{
throw new ApplicationException("Broken", ex);
}
}
private async void CSVexport_Click(object sender, RoutedEventArgs e) //to use Await Task.Delay(ms) Option
{
try
{
SaveFileDialog saveAsfile = new SaveFileDialog(); //Save As File
saveAsfile.InitialDirectory = #"C:\";
saveAsfile.Title = "Save As File";
saveAsfile.Filter = "CSV Document(*.csv)|*.csv|All Files(*.*)|*.*";
saveAsfile.DefaultExt = "csv";
saveAsfile.AddExtension = true;
if (saveAsfile.ShowDialog() == System.Windows.Forms.DialogResult.OK) //Save As File Function
{
GaugeSignal.Value = 1;
if (TGLatestData.PoorSignal != -1)
{
var SignalQuality = Math.Round((200D - TGLatestData.PoorSignal) / 2D, 1);
if (SignalQuality < 0) SignalQuality = 0;
GaugeSignal.Value = SignalQuality;
TGLatestData.PoorSignal = TGLatestData.PoorSignal;
}
SQZero.Visibility = 0;
CSVexport.Visibility = Visibility.Hidden;
FileStream filestream = new FileStream(saveAsfile.FileName, FileMode.Create, FileAccess.Write);
System.Windows.MessageBox.Show("CSV Writing...\nIf you want to stop Writing CSV, Click SQ to 0 Button,", "CSV Writing...", MessageBoxButton.OK, MessageBoxImage.Warning);
System.IO.StreamWriter file = new System.IO.StreamWriter(filestream);
int time = 1000;
file.WriteLine("Signal Quality, Delta, Theta, Alpha1, Alpha2, Beta1, Beta2, Gamma1, Gamma2, Attention, Meditation, Mental Effort, Task Familiarity, Task Difficulty, Blink Strength");
while (true) //infinite loop
{
double Check_Signal_Zero_To_Stop = GaugeSignal.Value;
file.WriteLine("{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}", GaugeSignal.Value, TGLatestData.EegPowerDelta, TGLatestData.EegPowerTheta, TGLatestData.EegPowerAlpha1, TGLatestData.EegPowerAlpha2, TGLatestData.EegPowerBeta1, TGLatestData.EegPowerBeta2, TGLatestData.EegPowerGamma1, TGLatestData.EegPowerGamma2, TGLatestData.Attention, TGLatestData.Meditation, TGLatestData.MentalEffort, TGLatestData.TaskFamiliarity, TGLatestData.TaskDifficulty, TGLatestData.BlinkStrength);
await Task.Delay(time); //Delay Without FreezingUI (need async)
if (Check_Signal_Zero_To_Stop == 0)
{
file.Close(); //if you click SQ to 0, Do file.Close
while (true) await Task.Delay(1000000000);
}
}
}
}
catch (ApplicationException ex)
{
throw new ApplicationException("This Program is Get Broken", ex);
}
}
I changed private void to 'private async void' to use Await Task.Delay(ms) instead of Thread.Sleep(ms) and I also Made Stop Button to get out of infinite loop.
Here is SQ to 0 Button Source. If you Click this button, One variable(GaugeSignal.Value) will be 0 and it will stop Writing CSV and Saving modified file.
private async void SQzero_Click(object sender, RoutedEventArgs e)
{
int EndTime = 4000;
SQZero.Visibility = Visibility.Hidden;
System.Windows.MessageBox.Show("Stop Writing CSV... \nWait a Seconds..", "CSV Saving..", MessageBoxButton.OK, MessageBoxImage.Warning);
GaugeSignal.Value = 0;
await Task.Delay(EndTime); //Wait for Delay of CSVexport_btn
System.Windows.MessageBox.Show("CSV Saved!", "CSV Saved", MessageBoxButton.OK, MessageBoxImage.Warning);
CSVexport.Visibility = Visibility.Visible; //to Save another one Again
}
I was Foolish. I used file.Close(); in loop. So, It says IOException. I found other method to avoid them and Finally got it. Thanks to John Wu, Çöđěxěŕ to help me.

How to delete current task in pool threads?

I have this event handler in asp.net page:
protected void SetDescPoint(object sender, EventArgs e)
{
try
{
ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(foo));
}
catch (Exception)
{ }
}
private void foo(object a)
{
try
{
System.Diagnostics.Debug.WriteLine("Start - " + DateTime.Now.ToString("h:mm:ss tt"));
TimeSpan minutes = TimeSpan.FromMinutes(10);
System.Threading.Thread.Sleep(minutes);
string path = UniquePath();
File.Delete(path);
System.Diagnostics.Debug.WriteLine("Deleted - " + DateTime.Now.ToString("h:mm:ss tt"));
}
catch (Exception ex)
{
Debug.WriteLine("EXCEPTION - " + ex.Message);
}
}
SetDescPoint is event handler and fired in response to client event.As you can see the function foo has Thread.Sleep(10minutes) there is might be situation when event handler fired in time interval less than 10 minutes, so in that situation I need to delete current task(foo()) in pool threads.
Any Idea how can I implement it?
Rewrite your code to use Task.Delay. This has two benefits: you're not holding up a thread anymore (as Task.Delay uses a timer internally), and you can use a cancellation token to cancel the wait:
protected CancellationTokenSource CancellationToken { get; private set; }
protected void SetDescPoint(object sender, EventArgs e)
{
try
{
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
this.CancellationToken = new CancellationTokenSource();
Task.Run(() => foo(this.CancellationToken.Token), this.CancellationToken.Token);
}
catch (Exception)
{ }
}
private async Task foo(CancellationToken token)
{
try
{
System.Diagnostics.Debug.WriteLine("Start - " + DateTime.Now.ToString("h:mm:ss tt"));
TimeSpan minutes = TimeSpan.FromMinutes(10);
await Task.Delay(minutes, token);
string path = UniquePath();
File.Delete(path);
System.Diagnostics.Debug.WriteLine("Deleted - " + DateTime.Now.ToString("h:mm:ss tt"));
}
catch (Exception ex)
{
Debug.WriteLine("EXCEPTION - " + ex.Message);
}
}
Whenever you want to cancel your task, just call CancellationToken.Cancel()
A rather simplified way of dealing with this would be to keep track of a CancellationTokenSource for each path your create (I'm not sure if there a multiple paths or a simple path, but just in case), and then look it up once the event fires again.
Using this with Task.Delay, which asynchronously yields control in a non-blocking fashion, can achieve what you want:
private ConcurrentDictionary<string, CancellationTokenSource> pathsToTokens =
new ConcurrentDictionary<string, CancellationTokenSource>();
protected async void SetDescPointAsync(object sender, EventArgs e)
{
CancellationTokenSource existingTokenSource;
var path = UniquePath();
if (pathsToTokens.TryGetValue(path, out existingTokenSource))
{
existingTokenSource.Cancel();
}
var cancellationTokenSource = new CancellationTokenSource();
pathsToTokens.AddOrUpdate(path, cancellationTokenSource,
(pathToFile, token) => cancellationTokenSource);
try
{
await Task.Delay(TimeSpan.FromMinutes(10), cancellationTokenSource.Token)
.ConfigureAwait(false);
}
catch (TaskCanceledException tce)
{
// Token was cancelled, do something?
}
Foo(path);
pathsToTokens.TryRemove(path, out cancellationTokenSource);
}
private void Foo(string path)
{
try
{
File.Delete(path);
Debug.WriteLine("Deleted - " + DateTime.Now.ToString("h:mm:ss tt"));
}
catch (Exception ex)
{
Debug.WriteLine("EXCEPTION - " + ex.Message);
}
}
What happens with this code is that for each path you create, you allocate a new CancellationTokenSource. Every time the event is triggered, you check for an existing token. If it is in place, that means the event still hasn't finished, and you want to cancel it. Then, you asynchronously wait the amount of time you need. Note that Task.Delay is wrapped in a try-catch as calling CancellationTokenSource.Cancel will cause it to throw an exception once completed.

Invoke a method with parameters

I'm developing a Windows Form app with C# and .NET Framework 4.0.
I'm using Task to run a long running task and I need to update UI with some log messages every time my task process a code.
There is a Queue processing that code, I need to show that a code has been processed.
private Task taskReadCodeAuto;
private delegate void RefreshTextBox();
private Queue CodesReceived;
public MainForm()
{
InitializeComponent();
logMessages = new List<string>();
CodesReceived = new Queue();
taskReadCodeAuto = new Task(() => ProcessCodesReceived());
}
private void ProcessCodesReceived()
{
int result;
try
{
while (CodesReceived.Count > 0)
{
string code = CodesReceived.Dequeue().ToString();
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), string.Format("Sending code {0} to ReadCodeAuto...", code));
if (trzic == null)
{
result =
TRZIC.ReadCodeAuto(
ConnStringTextBox.Text,
byte.Parse(AggregationNumeric.Value.ToString()),
code);
}
else
{
result =
trzic.ReadCodeAuto(
byte.Parse(AggregationNumeric.Value.ToString()),
code);
}
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), string.Format("Code sent {0}. Result: {1}", code, result));
}
}
catch (Exception ex)
{
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), "Error: " + ex.Message);
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
finally
{
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), "END BG-WORKER");
}
}
private void InsertProfileMessage(string time, string message)
{
string profileString =
string.Format("{0} - {1}", time, message);
logMessages.Add(profileString);
if (this.InvokeRequired)
{
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d);
}
else
{
RefreshTextBoxResults(profileString + "\n");
}
}
private void RefreshTextBoxResults(string text)
{
LogTextBox.AppendText(text);
}
My problem is that I don't know how to pass the text to show on LogTextBox using Invoke.
How can I do it?
Use the overload of Invoke which takes an Object[] as a parameter for the arguments to be supplied to your method.
You can add the parameters after the invoke:
Action<string> d = RefreshTextBoxResults;
this.Invoke(d, profileString + "\n");
Or invoke an action where the parameter is already included (which is this case is suitable regarding re usability)
Action d= () =>RefreshTextBoxResults(profileString + "\n");
if (this.InvokeRequired)
{
Invoke(d);
}
else
{
d();
}
PS, if you want to use your RefreshTextBox delegate instead of an Action, the RefreshTextBox delegate should be altered to include a string parameter
You would have to use the overload of Invoke which uses an array:
MSDN documentation
Pass the text value like below.
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d,new object[] {“Pass value here”});

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.

how to make service act dynamically based on service running condition

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
}
}

Categories