I have a windows service that communicates with a DB residing on a different server.The service is installed and always running.The service will look out if there any new records in the DB Table and get 1000 records at a time via stored proc and process records (updates/creates in the CRM system).
Logic is working perfectly alright but the problem is with service going to idle state(service is in running state but won't execute the method 'ProcessNewOtranRecords' that calls stored proc) after few hours. When the service is restarted it again works as expected for few more hours.
Please suggest me if there is any good approach to keep service active all the time.
Here is the code :
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Security.Permissions;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using CRM.Objects.BusinessLogic;
namespace CRM
{
partial class CrmProcessOtran : ServiceBase
{
OtranBL _otranBL = new OtranBL();
private System.Timers.Timer mainTimer;
int eventID = 0;
public CrmProcessOtran()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
eventLog1.WriteEntry("Service Start");
mainTimer = new System.Timers.Timer(30000); // 30 seconds
mainTimer.Elapsed += PerformOtranOperations;
mainTimer.AutoReset = false;
mainTimer.Start();
}
protected override void OnStop()
{
eventLog1.WriteEntry("Service Stopped");
mainTimer.Stop();
mainTimer.Dispose();
mainTimer = null;
}
public void PerformOtranOperations(object sender, EventArgs e)
{
eventID++;
eventLog1.WriteEntry(DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss") + " - checking for new otran records", EventLogEntryType.Information, eventID);
// Check for new otran records
int otranRecords = _otranBL.GetOtranRecordCount(eventLog1);
if (otranRecords == 0)
{
eventLog1.WriteEntry("0 new otran records", EventLogEntryType.Information, eventID);
return;
}
eventLog1.WriteEntry(otranRecords.ToString("N0") + " new otran records found with proc_status = 0", EventLogEntryType.Information, eventID);
// Process new records
eventLog1.WriteEntry(DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss") + " - Begin processing new otran records", EventLogEntryType.Information, eventID);
int processedrecords = _otranBL.ProcessNewOtranRecords(eventLog1);
eventLog1.WriteEntry(processedrecords.ToString() + " processed records", EventLogEntryType.Information, eventID);
mainTimer.Start();
}
}
}
You should add some Try/Catch/Finally logic into PerformOtranOperations. You're propably experiencing some exception after some iterations and your mainTimer.Start(); is never called from that moment.
So the service is running but the trigger is not active any more.
The Timer component catches and suppresses all exceptions thrown by event handlers
for the Elapsed event; see MSDN.
Because of this you probably don't see any errors in the Windows event viewer.
Can you rework your code to use a System.Threading.Timer instance instead, which doesn't swallow the exception if something goes wrong.
As Pavlinll suggest, you should implement some error handling in order to keep your service running.
Related
I am developing an application that runs on a FEZ Spider board that communicate with a WCF hosted on my PC. I have followed this example, but instead of using a WCFServiceHost, I've hosted the WCF service in IIS.
The problem is that when I'm contacting the WCF service, the application always raises an IO Exception.
Here is the code of my simple Gadgeteer application that tests the WCF connection.
using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Presentation.Shapes;
using Microsoft.SPOT.Touch;
using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
using tempuri.org;
using schemas.datacontract.org.WcfServiceTest;
using Ws.Services.Binding;
using Ws.Services;
namespace GadgeteerClient
{
public partial class Program
{
private bool networkUp = false;
private IService1ClientProxy proxy;
//*****
// TODO: change this to the IP address of your machine that is running the WCF service
//*****
string hostRunningWCFService = "169.254.65.183";
// This method is run when the mainboard is powered up or reset.
void ProgramStarted()
{
/*******************************************************************************************
Modules added in the Program.gadgeteer designer view are used by typing
their name followed by a period, e.g. button. or camera.
Many modules generate useful events. Type +=<tab><tab> to add a handler to an event, e.g.:
button.ButtonPressed +=<tab><tab>
If you want to do something periodically, use a GT.Timer and handle its Tick event, e.g.:
GT.Timer timer = new GT.Timer(1000); // every second (1000ms)
timer.Tick +=<tab><tab>
timer.Start();
*******************************************************************************************/
// Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
Debug.Print("Program Started");
// USE UpdateClientProxy.cmd to create the proxy classes
ethernetJ11D.NetworkUp += ethernetJ11D_NetworkUp;
ethernetJ11D.UseThisNetworkInterface();
ethernetJ11D.UseStaticIP("169.254.141.168", "255.255.0.0", hostRunningWCFService);
button.ButtonPressed += button_ButtonPressed;
}
void ethernetJ11D_NetworkUp(GTM.Module.NetworkModule sender, GTM.Module.NetworkModule.NetworkState state)
{
Debug.Print("NETWORK IS UP!");
networkUp = true;
proxy = new IService1ClientProxy(new WS2007HttpBinding(), new ProtocolVersion11());
// NOTE: the endpoint needs to match the endpoint of the servicehost
proxy.EndpointAddress = "http://" + hostRunningWCFService + "/Service1";
}
void button_ButtonPressed(Button sender, Button.ButtonState state)
{
if (!networkUp)
return;
button.TurnLedOn();
try
{
GetData data = new GetData();
data.value = 123;
// call test
string result = proxy.GetData(data).GetDataResult;
// should print 123, IO Exception is instead generated
Debug.Print("Received: " + result);
displayTE35.SimpleGraphics.DisplayText(result, Resources.GetFont(Resources.FontResources.NinaB), GT.Color.Gray, 30, 30);
}
catch (System.IO.IOException ex)
{
Debug.Print("Error making WCF call");
Debug.Print(ex.ToString());
}
finally
{
button.TurnLedOff();
}
}
}
}
How could I solve this issue?
EDIT:
Details about the exception are:
Exception was thrown: System.IO.IOException
There is no inner exception and the stack trace is:
Ws.Services.Binding.HttpTransportBindingElement::OnProcessInputMessage
Ws.Services.Binding.BindingElement::ProcessInputMessage
Ws.Services.Binding.BindingElement::ProcessInputMessage
Ws.Services.Binding.RequestChannel::ReceiveMessage
Ws.Services.Binding.RequestChannel::Request
tempuri.org.IService1ClientProxy::GetData
GadgeteerClient.Program::button_ButtonPressed
Gadgeteer.Modules.GHIElectronics.Button::OnButtonEvent
System.Reflection.MethodBase::Invoke
Gadgeteer.Program::DoOperation
Microsoft.SPOT.Dispatcher::PushFrameImpl
Microsoft.SPOT.Dispatcher::PushFrame
Microsoft.SPOT.Dispatcher::Run
Gadgeteer.Program::Run
I want to start a Task when my Main Page is loaded, which get my position using Xamarin.Mobile in background. And the difficult point is to wait, if this task isn't finished, when the user clicked on a button.
On Xamarin iOS, I managed to do it but when I try to do exactly the same on Windows Phone 8.0 I get an AggregateException with as message : "One or more errors occurred".
There is the code i use :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Diagnostics;
using System.IO;
using Microsoft.Phone.Scheduler;
using System.Threading;
using System.Threading.Tasks;
using System.ComponentModel;
using Xamarin.Geolocation;
namespace Application.WinPhone
{
public partial class Connexion : PhoneApplicationPage
{
static Task w;
// Constructor
public Connexion()
{
InitializeComponent();
w = new Task (() =>
{
Debug.WriteLine("Start");
Geolocator geolocator = null;
geolocator = new Geolocator() { DesiredAccuracy = 50};
var t = geolocator.GetPositionAsync(8000).ContinueWith(x =>
{
Debug.WriteLine(string.Format("Latitude : {0} Longitude : {1}", x.Result.Latitude, x.Result.Longitude)); //Visual Studio's debugger indicate this line with the exception
});
t.Wait();
Debug.WriteLine("Finished");
});
w.Start();
}
private void Connexion_Click(object sender, RoutedEventArgs e)
{
w.Wait();
//Here use the position find by the task to know on which page send the user
NavigationService.Navigate(new Uri("/Inscription.xaml", UriKind.RelativeOrAbsolute));
}
}
}
If there are some grammatical errors in my post Sorry i'm French. :)
Thanks in advance for help.
First of all, you shouldn't do any heavy lifting in the constructor. That's a design flaw. Second, Windows Phone expects your app to start in a limited amount of time, i.e. 10 seconds max. So, starting the app and waiting 8 seconds on the Geolocator is probably taking too much time, hence the canceled task.
What you could do is create the Geolocator in the constructor of the page and get the position in the OnNavigatedTo event.
One thing you should really do is to rearange it with asnyc/await as you really don't need creating tasks in this case, i.e. (out of my head):
public Connexion()
{
Connexion.IsEnabled = false;
var ignore = InitAsync();
}
private async Task InitAsync()
{
Debug.WriteLine("Start");
Geolocator geolocator = null;
geolocator = new Geolocator() { DesiredAccuracy = 50};
var result = await geolocator.GetPositionAsync(8000);
Debug.WriteLine(string.Format("Latitude : {0} Longitude : {1}", result.Latitude, result.Longitude)); //Visual Studio's debugger indicate this line with the exception
Connexion.IsEnabled = true;
}
Note that button should be disabled unless action succeeds. You should also add try/catch handler in there and you'll get clearer exception (perhaps Geolocator can't be created in a non UI thread?)
Besides what Geolocator class are you actually using - Forms Labs one?
I am trying to get a test application to work using Oracle Change Notification with C#, but I am not receiving the callback notification in my application. Oracle DB version is 11.2.0.1.0. Oracle.DataAccess v.2.112.30. I can confirm the query gets registered in Oracle by viewing SYS.USER_CHANGE_NOTIFICATION_REGS and SYS.USER_CQ_NOTIFICATION_QUERIES. However, nothing ever appears in SYS.DBA_CHANGE_NOTIFICATION_REGS.
The registration persists until I commit a transaction on the table. The registration disappears after a few seconds after the commit and my application does not received the notification.
I have made sure my computer is listening on the correct port and have even tried turning off any firewall that could be blocking the port.
I do have GRANT CHANGE NOTIFICATION to MYSCHEMA, GRANT EXECUTE ON DBMS_CHANGE_NOTIFICATION TO MYSCHEMA, and the JOB_QUEUE_PROCESSES is set to 1.
Questions:
1) Should the registration be visible in SYS.DBA_CHANGE_NOTIFICATION_REGS and, if so, what could be causing it not to be when it is visible in SYS.USER_CHANGE_NOTIFICATION_REGS and SYS.USER_CQ_NOTIFICATION_QUERIES?
2) What could be causing the registration to disappear after a commit?
3) What could be causing the failure of the notification to my application?
Here is the C# code I am using and it is basically the same as from the Oracle website:
using System;
using System.Threading;
using System.Data;
using Oracle.DataAccess.Client;
namespace NotifyTest
{
public class Program
{
public static bool IsNotified = false;
public static void Main(string[] args)
{
string constr = "User Id=mySchema;Password=myPassword;Data Source=myOracleInstance";
OracleDependency dep = null;
try
{
using (var con = new OracleConnection(constr))
{
Console.WriteLine("Registering query...");
var cmd = new OracleCommand("select * from mySchema.NOTIFY_TEST", con);
con.Open();
OracleDependency.Port = 1005;
dep = new OracleDependency(cmd);
dep.OnChange += OnMyNotificaton;
int queryRegistered = cmd.ExecuteNonQuery();
// If oracle returns -1, then the query is successfully registered
if (queryRegistered == -1)
{
Console.WriteLine("Query Registered...");
Console.WriteLine("Listening for Callback...");
}
else
{
Console.WriteLine("There was an error...");
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
// Loop while waiting for notification
while (IsNotified == false)
{
Thread.Sleep(100);
}
}
public static void OnMyNotificaton(object sender, OracleNotificationEventArgs arg)
{
Console.WriteLine("Table change notification event is raised");
Console.WriteLine(arg.Source.ToString());
Console.WriteLine(arg.Info.ToString());
Console.WriteLine(arg.Source.ToString());
Console.WriteLine(arg.Type.ToString());
IsNotified = true;
}
}
}
Just wanted to provide an update as to how I resolved this issue. I changed my Oracle.DataAccess.dll from v.2.112.3.0 to v.2.112.1.2 and it works fine.
In SYS.CHNF$_REG_INFO attribute QOSFLAGS there is QOS_DEREG_NFY, which specifies that the database should unregister the registration on the first notification.
Not sure but the value on job_queue_processes (1) is a bit low. Oracle performs all kinds of maintenance and event handling tasks internally. For this they also use Job slaves. Raise job_queue_processes (default 1000) and check Troubleshooting CQN Registrations
I followed the instructions in the marked answer here to create a service. The service gets installed correctly. When I start the service after a while it throws a message
"Windows could not start the xxx service on Local Computer.
Error 1053: The service did not respond to the start or control request in a timely fashion.
"
After I click ok, its status stays at "Starting" for ever. When I checked the application and system logs, there were no errors.
When I check the SQL trace, the service is actually running correctly and doing what its supposed to do. So why does its status stay at "Starting" ?
Update:
This is the code in OnStart method
protected override void OnStart(string[] args)
{
Loader loader = new Loader();
loader.StartProcess();
}
Update 2:
based on WiktorZychla's comment I did this and it worked :)
protected override void OnStart(string[] args)
{
Loader loader = new Loader();
ThreadStart threadDelegate = new ThreadStart(loader.StartProcess);
Thread newThread = new Thread(threadDelegate);
newThread.Start();
}
Based on WiktorZychla's comment this is what I did
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using IndexLoader;
using System.Threading;
namespace myNameSpace
{
public partial class LoaderService : ServiceBase
{
Thread newThread;
public LoaderService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
Loader loader = new Loader();
ThreadStart threadDelegate = new ThreadStart(loader.StartProcess);
newThread = new Thread(threadDelegate);
newThread.Start();
}
protected override void OnStop()
{
if ((newThread != null) && (newThread.IsAlive))
{
Thread.Sleep(5000);
newThread.Abort();
}
}
}
}
It would be instructive to know exactly what is in your OnStart() method. The OnStart() method is a callback from the OS that is used to start your service, but it must return within 30 seconds or so (I remember reading that somewhere). Otherwise, the OS gives the message you're seeing. In short, limit the OnStart() method to getting things initialized, and defer the actual work your service is to perform to a thread of some kind.
In your Program.cs file in the Main() function make sure that you have:
ServiceBase.Run(new ServiceClassHere());
I've been guilty many times when creating a windows form app of keeping
Application.Run(new Class()); in my Main() function
I am creating this windows service by following the instructions at MSDN Walkthrough: Creating a Windows Service and after successful installation, I go to Services.msc to Start the Windows service and before it finishes starting up I get the following message:
The EIWindowsService service on Local Computer started and then stopped. Some services stop automatically if they are not in use by other services or programs.
I know the Windows Service starts ok because there is an entry to the log file stating that the service started. I did some research before posting on here and the answer from Some Services Stop Automatically states that the problem could either be that the OnStart method is throwing an error, or that the OnStart is not kicking off a thread. So I modified my code so that the only thing within the OnStart is the starting of two timers and the log entry therefore needing no exception handling. I also added a thread to "jump" to another method.
I tried the windows service again and I know that it "moved" to the new method that the thread pointed to because I had a log entry in there that threw aFormatException error due to some conversion I was doing. I commented out the conversion and the windows service still just began to start up and then stopped automatically.
Further research indicated to me that I might need a loop to keep the processing within the method, so I took information from C - Windows Service the service on and set up an infinite while loop. I also found that there might be Garbage Collection going on and established a KeepAlive statement for the timers as suggested in Examples section of MSDN Timer Class. Still the same issues.
At this point I feel I've exhaused all the research I can do so it would be appropriate to post my question here. All my code is below and I will note that before I performed any change I uninstalled the Windows Service, removed the Setup Project, and deleted the installers from the C# code. I then made changes and started back over with the instructions in the Walkthrough starting at the point where it instructs how to setup the installers. I did this each time because I found that if I made changes and did not uninstall the Windows Service, remove the Setup Project, and delete the installers, then my changes would not take effect on the currently installed windows service.
Any assistance you can give would be most appreciated. I will be here for another 15min and then I will check this first thing tomorrow.
SERVICE1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Timers;
namespace EIWindowsService
{
public partial class Service1 : ServiceBase
{
Logs.ErrorLog logFile = new Logs.ErrorLog();
private System.Threading.Thread onStartThread;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
try
{
iTimer.Start();
iTimer.Elapsed += new ElapsedEventHandler(iTimer_Elapsed);
pTimer.Start();
pTimer.Elapsed += new ElapsedEventHandler(pTimer_Elapsed);
onStartThread = new System.Threading.Thread(TimerValue);
onStartThread.Start();
logFile.SendToLog("EIWindows Service started on " + GetDate());
}
catch (ArgumentOutOfRangeException ex)
{
logFile.SendToLog("ArgumentOutOfRangeException", "EIWindowsService\\Service1.cs", "OnStart()", ex);
} //end of ArgumentOutOfRangeException CATCH statement
}
protected override void OnStop()
{
iTimer.Stop();
pTimer.Stop();
logFile.SendToLog("EIWindowsService\\Service1.cs", "OnStop()", "EIWindows Service stopped on " + GetDate());
}
private void TimerValue()
{
try
{
/*commented out because it was throwing an exception error*/
//double iTimerValue = Convert.ToDouble(iTimer.ToString());
//double pTimerValue = Convert.ToDouble(pTimer.ToString());
while (1 > 0)
{
//if (iTimerValue % 1800000 == 0) //if the timer hits the 30min mark
//{
// logFile.SendToLog("Current iTimer Value = " + iTimerValue.ToString());
//}
//if (pTimerValue % 1800000 == 0) //if the timer hits the 30min mark
//{
// logFile.SendToLog("Current pTimer Value = " + pTimerValue.ToString());
//}
GC.KeepAlive(iTimer);
GC.KeepAlive(pTimer);
}
//TimerValue();
}
catch (OverflowException ex)
{
logFile.SendToLog("OverflowException", "EIWindowsService\\Service1.cs", "TimerValue()", ex);
} //end of OverflowException CATCH statement
catch (ArgumentException ex)
{
logFile.SendToLog("ArgumentException", "EIWindowsService\\Service1.cs", "TimerValue()", ex);
} //end of ArgumentException CATCH statement
catch (FormatException ex)
{
logFile.SendToLog("FormatException", "EIWindowsService\\Service1.cs", "TimerValue()", ex);
} //end of FormatException CATCH statement
}
private string GetDate()
{
string current = "No Date Recorded";
try
{
current = DateTime.Now.ToString("F");
}
catch (FormatException ex)
{
logFile.SendToLog("FormatException", "EIWindowsService\\Service1.cs", "GetDate()", ex);
} //end of FormatException CATCH statement
return current;
} //end of method GetDate
private void iTimer_Elapsed(object source, ElapsedEventArgs e)
{
try
{
iTimer.Stop();
ImportI();
iTimer.Start();
}
catch (ArgumentOutOfRangeException ex)
{
logFile.SendToLog("ArgumentOutOfRangeException", "EIWindowsService\\Service1.cs", "iTimer_Elapsed()", ex);
} //end of ArgumentOutOfRangeException CATCH statement
} //end of method iTimer_Elapsed
private void pTimer_Elapsed(object source, ElapsedEventArgs e)
{
try
{
pTimer.Stop();
ImportP();
pTimer.Start();
}
catch (ArgumentOutOfRangeException ex)
{
logFile.SendToLog("ArgumentOutOfRangeException", "EIWindowsService\\Service1.cs", "pTimer_Elapsed()", ex);
} //end of ArgumentOutOfRangeException CATCH statement
} //end of method pTimer_Elapsed
private void ImportI()
{
//does some action but commented out because it never gets here and is not relavant to this question.
} //end of method ImportI
private void ImportP()
{
//does some action but commented out because it never gets here and is not relavant to this question.
} //end of method ImportP
}
}
SERVICE1.DESIGNER.CS (the relavant stuff)
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.pTimer = new System.Timers.Timer(10800000); //3hrs
this.iTimer = new System.Timers.Timer(3600000); //1hr
//
// pTimer
//
this.pTimer.Enabled = true;
//
// iTimer
//
this.iTimer.Enabled = true;
//
// Service1
//
this.ServiceName = "EIWindowsService";
}
#endregion
private System.Timers.Timer pTimer;
private System.Timers.Timer iTimer;
You don't need to create a separate thread or worry about the garbage collector. The framework handles all that for you. Just create the timers and they will be called. Here's an example.
public partial class Service1 : ServiceBase
{
private Timer timer;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
timer = new Timer(1000);
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
using (StreamWriter writer = File.AppendText(#"C:\Users\alfonso\Desktop\log.txt"))
{
writer.WriteLine(string.Format("{0} : {1}", DateTime.Now, "Logging from the service"));
}
}
protected override void OnStop()
{
}
}
Something else that may help someone coming across this post and the above solutions do not work. When I had this problem, I had added this to the config of my Windows Service:
<system.web>
<compilation debug ="true" />
</system.web>
I added this so that I could attach the debugger to the service when running it locally, however when I tried to move the service to another server it gave the specified error. By removing this from the config the service worked again.