Hi,
I need to develop an application which performs a data transfer one data base to another. I can not use DTS as a solution due to the complex requiremtns of the application. This application needs to run every 30 secs. So I decided to go for windows service. I am new to windows serivces and multithreading. I have developed an application design and would like to get feed back on it.
General over view of the design :
Application contains the following classes - AccountCreationDTS (WInService class), DTSTimer, DTSController, DTSREquestBin and DTS. AccountCreationDTS.OnStart() starts the DTSTimer. DTSTimer._onTimeElapsed() invokes DTSController.startTask() which initiates a DTS.
The code for all the classes is given below :
////////////////////////////////AccountCreationDTS///////////////////////////////////
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.IO;
using System.Text;
using System.Threading;
using System.Timers;
namespace WinService
{
public class AccountCreationDTS : System.ServiceProcess.ServiceBase
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
private DTSTimer _dtsTimer = null;
public AccountCreationDTS()
{
// This call is required by the Windows.Forms Component Designer.
InitializeComponent();
// TODO: Add any initialization after the InitComponent call
}
// The main entry point for the process
static void Main()
{
System.ServiceProcess.ServiceBase[] ServicesToRun;
// More than one user Service may run within the same process. To add
// another service to this process, change the following line to
// create a second service object. For example,
//
// ServicesToRun = new System.ServiceProcess.ServiceBase[] {new Service1(), new MySecondUserService()};
//
ServicesToRun = new System.ServiceProcess.ServiceBase[] { new AccountCreationDTS() };
System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
this.ServiceName = "Servicename";
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
/// <summary>
/// Set things in motion so your service can do its work.
/// </summary>
protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
this._onStartService();
this._dtsTimer = new DTSTimer();
this._dtsTimer.start();
}
/// <summary>
/// Stop this service.
/// </summary>
protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary to stop your service.
this._dtsTimer.stop();
this._onStopService();
}
private void _onStartService()
{
//servivce start up
}
private void _onStopService()
{
//service shutdown
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////DTSTimer///////////////////////////////////////////////////
using System;
using System.Timers;
using System.IO;
using System.Text;
using System.Threading;
using System.Collections;
namespace WinService
{
/// <summary>
/// Summary description for ServiceTimer.
/// </summary>
public class DTSTimer
{
public System.Timers.Timer _timer;
private DTSController _dtsController;
public DTSTimer()
{
this._timer = new System.Timers.Timer();
this._timer.Elapsed += new ElapsedEventHandler(this._onTimeElapsed);
this._timer.AutoReset = true;
this._timer.Interval = 30000;
this._dtsController = new DTSController();
}
public void start()
{
this._timer.Start();
}
public void stop()
{
this._timer.Stop();
this._dtsController.shutdown();
}
private void _onTimeElapsed(object sender, ElapsedEventArgs e)
{
this._dtsController.startTask();
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////DTSController///////////////////////////////////////
using System;
using System.Timers;
using System.IO;
using System.Text;
using System.Threading;
using System.Collections;
namespace WinService
{
/// <summary>
/// Summary description for DTSController.
/// </summary>
public class DTSController
{
private DTSRequestBin _requestQueue;
private DTSRequestBin _runningQueue;
public DTSController()
{
this._requestQueue = new DTSRequestBin();
this._runningQueue = new DTSRequestBin();
}
public void shutdown()
{
if(this._runningQueue != null && this._runningQueue.Count > 0)
{
foreach(DTS dts in this._runningQueue)
{
if(dts != null)
{
dts.shutdown();
}
}
this._runningQueue.Clear();
this._runningQueue = null;
this._requestQueue.Clear();
this._requestQueue = null;
}
}
public void addToRequestQueue(DTS dts)
{
if(this._requestQueue != null)
{
int nPendingRequests = this._requestQueue.Count;
if(nPendingRequests > 3)
{
return;
}
this._requestQueue.add(dts);
}
}
public void startTask()
{
this._createDTSRequest();
this._startTask();
}
private void _startTask()
{
if(this._runningQueue.Count > 0)
{
return;
}
DTS dts = null;
if(this._requestQueue != null && this._requestQueue.Count > 0)
{
dts = this._requestQueue[0] as DTS;
this._requestQueue.remove(dts);
}
else
{
dts = new DTS();
}
if(dts != null)
{
dts.DTSCompleted += new DTSCompletedEventHandler(onDTSCompleted);
this._runningQueue.add(dts);
dts.execute();
}
}
private void _createDTSRequest()
{
DTS dts = new DTS();
this._dtsController.addToRequestQueue(dts);
}
private void onDTSCompleted(object sender, DTSCompletedEventArgs e)
{
DTS dts = sender as DTS;
if(dts != null)
{
this._runningQueue.remove(dts);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////DTSRequestBin/////////////////////////////////////
using System;
using System.Collections;
namespace WinService
{
/// <summary>
/// Summary description for Tak.
/// </summary>
public class DTSRequestBin : CollectionBase
{
private ArrayList _bin;
public DTSRequestBin()
{
}
public void add(DTS dts)
{
this.List.Add(dts);
}
public void remove(DTS dts)
{
if(this.List.Contains(dts))
{
this.List.Remove(dts);
}
}
public DTS this[int index]
{
get
{
if(index <= 0 && index < this.List.Count)
{
return this.List[index] as DTS;
}
return null;
}
set
{
if(index <= 0 && index < this.List.Count)
{
this.List[index] = value;
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////DTS///////////////////////////////////////////////////////
using System;
using System.IO;
using System.Text;
using System.Threading;
namespace WinService
{
public delegate void DTSCompletedEventHandler(object sender, DTSCompletedEventArgs e);
/// <summary>
/// Summary description for DTS.
/// </summary>
public class DTS
{
private volatile bool _continueExecution = true;
private volatile bool _running;
private Thread _currentThread;
public event DTSCompletedEventHandler DTSCompleted;
public DTS()
{
}
public void execute()
{
if(this._running)
{
return;
}
if(!this._running)
{
this._running = true;
}
this._currentThread = new Thread(new ThreadStart(this._performDTS));
this._currentThread.Start(); //started on a new thread so that shutdown can interrupt it in case of a service shutdown.
}
private void _performDTS()
{
try
{
foreach(itemtobetransferred)
{
if(!this._continueExecution)
{
break;
}
//perform data transfer.
}
}
finally
{
if(!this._continueExecution)
{
}
else
{
this.OnDTSCompleted(new DTSCompletedEventArgs());
}
this._running = false;
}
}
public void shutdown()
{
try
{
this._continueExecution = false;
this._currentThread.Join();
}
finally
{
}
}
protected void OnDTSCompleted(DTSCompletedEventArgs e)
{
if(this.DTSCompleted != null)
{
this.DTSCompleted(this, e);
}
}
}
public class DTSCompletedEventArgs : EventArgs
{
public DTSCompletedEventArgs()
{
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
Any comments and suggestions about the design and use of windows service model would be highly appreciated.
Thanks in advance
Shameer

.net windows service application architecture
CL0CKW0RK
Hi Salva,
Many thanks for your response. I would like to explain why I did what I did with reference to your suggestions.
1. I agree that DTSTimer can be moved to the service itself.
2. I implemented DTSRequestBin as a custom collection so that it is type safe. I wanted to queue only DTS objects.
3. Currently the application allows only one thread to execute at a time. Then you might ask why do I need threads I started a data transfer process on a thread becuase during service shutdown, the application can wait for the current thread to complete the basic requirements for a safe exit. Data transfer runs in a loop. When service shutdown command is issued, I let the loop to complete the current iteration and exit skipping the rest of the iterations.
If I use thread pool, would I be able to do the same thing Anyway, as you suggested I will try out using thread pool instead of creating new threads.
regards
Shameer
Adam Miles
Hi,
Thanks for the suggestion. I am using volatile variable which is checked in each iteration of the loop. I think I can apply the same thing with thread pool also.
Btw, I have another issue :) I want to start my service on system start up. I set ServiceInstaller.StartType to Automatic and ServiceProcessInstaller.Account to LocalSystem. But it doesn't seem to work. I still have to start my service manually. Am I missing anything
regards
Shameer
marco.ragogna
Hi,
Generally ok, I would prefer to do something simpler to achieve the task, couple of comments:
1) You can get rid of the DTSTimer, moving the timer to the service
2) You can get rid of the DTSRequestBin using a generic List
3) Instead of creating new threads use the threadpool, it will run faster your application as creating threads is very expensive, queue the tasks on the pool
4) Check what happens if the DTS tasks takes longer than 30 minutes, you can have both tasks running at the same time, consider using thread safe methods like using Locks.
Hope this helps!
Salva
Carver42
Hi,
I understand what you mean, when you have loops and you want to stop them on the middle I will suggest to put a shared variable on the loop like:
while (_ServiceRunning)
{
// do your stuff
}
By this way, when you stop the service you just flag _ServiceRunning to false and all your threads will elegantly exit.
Thanks for your posts, let me know if I can help you on any other issue.
Regards
Mithat Mese
Hi,
When you check your service on the service viewer is set to "Automatic" I have a service that starts automatically when I set:
this.serviceProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller = new System.ServiceProcess.ServiceInstaller();
this.serviceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
It does not start automatically once is installed but as soon as you restart your machine it should restart (unless is failing, have you check your event viewer )
Cheers