Quartz.Net scheduler works locally but not on remote host - c#

I have a timed quartz.net job working fine on my dev machine, but once deployed to a remote server it is not triggering. I believe the job is scheduled ok, because if I postback, it tells me the job already exists (I normally check for postback however). The email code definitely works, as the 'button1_click' event sends emails successfully.
I understand I have full or medium trust on the remove server. My host says they don't apply restrictions that they know of which would affect it. Any other things I need to do to get it running?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Quartz;
using Quartz.Impl;
using Quartz.Core;
using Aspose.Network.Mail;
using Aspose.Network;
using Aspose.Network.Mime;
using System.Text;
namespace QuartzTestASP
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
ISchedulerFactory schedFact = new StdSchedulerFactory();
IScheduler sched = schedFact.GetScheduler();
JobDetail jobDetail = new JobDetail("testJob2", null, typeof(testJob));
//Trigger trigger = TriggerUtils.MakeMinutelyTrigger(1, 3);
Trigger trigger = TriggerUtils.MakeSecondlyTrigger(10, 5);
trigger.StartTimeUtc = DateTime.UtcNow;
trigger.Name = "TriggertheTest";
sched.Start();
sched.ScheduleJob(jobDetail, trigger);
}
}
protected void Button1_Click1(object sender, EventArgs e)
{
myutil.sendEmail();
}
}
class testJob : IStatefulJob
{
public testJob() { }
public void Execute(JobExecutionContext context)
{
myutil.sendEmail();
}
}
public static class myutil
{
public static void sendEmail()
{
// tested code lives here and works fine when called from elsewhere
}
}
}

The scheduler factory should be global to your application. In other words, declare it in Global.asax or similar that effectively gives you a global instance to operate with. You should start the scheduler in your application start if running ASP.NET.
Beware thought. ASP.NET recycles its processes which effective causes shutdown of scheduler (no jobs will run) until next next request comes in to web server to start the scheduler again. The recommended way is to have a windows service for running Quartz.NET jobs.

Related

how to make a c# program run in background and at startup [duplicate]

I have console application and would like to run it as Windows service. VS2010 has project template which allow to attach console project and build Windows service.
I would like to not add separated service project and if possible integrate service code into console application to keep console application as one project which could run as console application or as windows service if run for example from command line using switches.
Maybe someone could suggest class library or code snippet which could quickly and easily transform c# console application to service?
I usually use the following techinque to run the same app as a console application or as a service:
using System.ServiceProcess
public static class Program
{
#region Nested classes to support running as service
public const string ServiceName = "MyService";
public class Service : ServiceBase
{
public Service()
{
ServiceName = Program.ServiceName;
}
protected override void OnStart(string[] args)
{
Program.Start(args);
}
protected override void OnStop()
{
Program.Stop();
}
}
#endregion
static void Main(string[] args)
{
if (!Environment.UserInteractive)
// running as service
using (var service = new Service())
ServiceBase.Run(service);
else
{
// running as console app
Start(args);
Console.WriteLine("Press any key to stop...");
Console.ReadKey(true);
Stop();
}
}
private static void Start(string[] args)
{
// onstart code here
}
private static void Stop()
{
// onstop code here
}
}
Environment.UserInteractive is normally true for console app and false for a service. Techically, it is possible to run a service in user-interactive mode, so you could check a command-line switch instead.
I've had great success with TopShelf.
TopShelf is a Nuget package designed to make it easy to create .NET Windows apps that can run as console apps or as Windows Services. You can quickly hook up events such as your service Start and Stop events, configure using code e.g. to set the account it runs as, configure dependencies on other services, and configure how it recovers from errors.
From the Package Manager Console (Nuget):
Install-Package Topshelf
Refer to the code samples to get started.
Example:
HostFactory.Run(x =>
{
x.Service<TownCrier>(s =>
{
s.ConstructUsing(name=> new TownCrier());
s.WhenStarted(tc => tc.Start());
s.WhenStopped(tc => tc.Stop());
});
x.RunAsLocalSystem();
x.SetDescription("Sample Topshelf Host");
x.SetDisplayName("Stuff");
x.SetServiceName("stuff");
});
TopShelf also takes care of service installation, which can save a lot of time and removes boilerplate code from your solution. To install your .exe as a service you just execute the following from the command prompt:
myservice.exe install -servicename "MyService" -displayname "My Service" -description "This is my service."
You don't need to hook up a ServiceInstaller and all that - TopShelf does it all for you.
So here's the complete walkthrough:
Create new Console Application project (e.g. MyService)
Add two library references: System.ServiceProcess and System.Configuration.Install
Add the three files printed below
Build the project and run "InstallUtil.exe c:\path\to\MyService.exe"
Now you should see MyService on the service list (run services.msc)
*InstallUtil.exe can be usually found here: C:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.ex‌​e
Program.cs
using System;
using System.IO;
using System.ServiceProcess;
namespace MyService
{
class Program
{
public const string ServiceName = "MyService";
static void Main(string[] args)
{
if (Environment.UserInteractive)
{
// running as console app
Start(args);
Console.WriteLine("Press any key to stop...");
Console.ReadKey(true);
Stop();
}
else
{
// running as service
using (var service = new Service())
{
ServiceBase.Run(service);
}
}
}
public static void Start(string[] args)
{
File.AppendAllText(#"c:\temp\MyService.txt", String.Format("{0} started{1}", DateTime.Now, Environment.NewLine));
}
public static void Stop()
{
File.AppendAllText(#"c:\temp\MyService.txt", String.Format("{0} stopped{1}", DateTime.Now, Environment.NewLine));
}
}
}
MyService.cs
using System.ServiceProcess;
namespace MyService
{
class Service : ServiceBase
{
public Service()
{
ServiceName = Program.ServiceName;
}
protected override void OnStart(string[] args)
{
Program.Start(args);
}
protected override void OnStop()
{
Program.Stop();
}
}
}
MyServiceInstaller.cs
using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;
namespace MyService
{
[RunInstaller(true)]
public class MyServiceInstaller : Installer
{
public MyServiceInstaller()
{
var spi = new ServiceProcessInstaller();
var si = new ServiceInstaller();
spi.Account = ServiceAccount.LocalSystem;
spi.Username = null;
spi.Password = null;
si.DisplayName = Program.ServiceName;
si.ServiceName = Program.ServiceName;
si.StartType = ServiceStartMode.Automatic;
Installers.Add(spi);
Installers.Add(si);
}
}
}
Here is a newer way of how to turn a Console Application to a Windows Service as a Worker Service based on the latest .NET 6.
In Visual Studio 2022 you have out of the box Worker Service as a project template.
This gives you a main method and a Worker.cs on which you need a few more lines
Worker.cs on which I've added the StartAsync and StopAsync overrides to chose what my service does at start/stop.
namespace WorkerService
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//do some operation
}
public override Task StartAsync(CancellationToken cancellationToken)
{
return base.StartAsync(cancellationToken);
}
public override Task StopAsync(CancellationToken cancellationToken)
{
return base.StopAsync(cancellationToken);
}
}
}
and Program.cs on which you will need to add .UseWindowsService()
using WorkerService;
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
})
.UseWindowsService()
.Build();
await host.RunAsync();
And you will need to install the following NuGet for this method
Install-Package Microsoft.Extensions.Hosting.WindowsServices
Old answer -> .NET Core 3.1
If you create a Worker Service from Visual Studio 2019 it will give you almost everything you need for creating a Windows Service out of the box, which is also what you need to change to the console application in order to convert it to a Windows Service.
Here are the changes you need to do:
Install the following NuGet packages
Install-Package Microsoft.Extensions.Hosting.WindowsServices -Version 3.1.0
Install-Package Microsoft.Extensions.Configuration.Abstractions -Version 3.1.0
Change Program.cs to have an implementation like below:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ConsoleApp
{
class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).UseWindowsService().Build().Run();
}
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
});
}
}
and add Worker.cs where you will put the code which will be run by the service operations:
using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp
{
public class Worker : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//do some operation
}
public override Task StartAsync(CancellationToken cancellationToken)
{
return base.StartAsync(cancellationToken);
}
public override Task StopAsync(CancellationToken cancellationToken)
{
return base.StopAsync(cancellationToken);
}
}
}
Installing the app as a Windows Service
When everything is ready, and the application has built successfully, you can use sc.exe to install your console application exe as a Windows Service with the following command:
sc.exe create DemoService binpath= "path/to/your/file.exe"
Firstly I embed the console application solution into the windows service solution and reference it.
Then I make the console application Program class public
/// <summary>
/// Hybrid service/console application
/// </summary>
public class Program
{
}
I then create two functions within the console application
/// <summary>
/// Used to start as a service
/// </summary>
public void Start()
{
Main();
}
/// <summary>
/// Used to stop the service
/// </summary>
public void Stop()
{
if (Application.MessageLoop)
Application.Exit(); //windows app
else
Environment.Exit(1); //console app
}
Then within the windows service itself I instantiate the Program and call the Start and Stop functions added within the OnStart and OnStop. See below
class WinService : ServiceBase
{
readonly Program _application = new Program();
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] servicesToRun = { new WinService() };
Run(servicesToRun);
}
/// <summary>
/// Set things in motion so your service can do its work.
/// </summary>
protected override void OnStart(string[] args)
{
Thread thread = new Thread(() => _application.Start());
thread.Start();
}
/// <summary>
/// Stop this service.
/// </summary>
protected override void OnStop()
{
Thread thread = new Thread(() => _application.Stop());
thread.Start();
}
}
This approach can also be used for a windows application / windows service hybrid
I hear your point at wanting one assembly to stop repeated code but, It would be simplest and reduce code repetition and make it easier to reuse your code in other ways in future if...... you to break it into 3 assemblies.
One library assembly that does all the work.
Then have two very very slim/simple projects:
one which is the commandline
one which is the windows service.
You can use
reg add HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v ServiceName /d "c:\path\to\service\file\exe"
And it will appear int the service list. I do not know, whether that works correctly though. A service usually has to listen to several events.
There are several service wrapper though, that can run any application as a real service. For Example Microsofts SrvAny from the Win2003 Resource Kit
Maybe you should define what you need, as far as I know, you can't run your app as Console or Service with command line, at the same time. Remember that the service is installed and you have to start it in Services Manager, you can create a new application wich starts the service or starts a new process running your console app. But as you wrote
"keep console application as one project"
Once, I was in your position, turning a console application into a service. First you need the template, in case you are working with VS Express Edition. Here is a link where you can have your first steps: C# Windows Service, this was very helpful for me. Then using that template, add your code to the desired events of the service.
To improve you service, there's another thing you can do, but this is not quick and/or easily, is using appdomains, and creating dlls to load/unload. In one you can start a new process with the console app, and in another dll you can just put the functionality the service has to do.
Good luck.
You need to seperate the functionality into a class or classes and launch that via one of two stubs. The console stub or service stub.
As its plain to see, when running windows, the myriad services that make up the infrastructure do not (and can't directly) present console windows to the user. The service needs to communicate with the user in a non graphical way: via the SCM; in the event log, to some log file etc. The service will also need to communicate with windows via the SCM, otherwise it will get shutdown.
It would obviously be acceptable to have some console app that can communicate with the service but the service needs to run independently without a requirement for GUI interaction.
The Console stub can very useful for debugging service behaviour but should not be used in a "productionized" environment which, after all, is the purpose of creating a service.
I haven't read it fully but this article seems to pint in the right direction.
I use a service class that follows the standard pattern prescribed by ServiceBase, and tack on helpers to easy F5 debugging. This keeps service data defined within the service, making them easy to find and their lifetimes easy to manage.
I normally create a Windows application with the structure below. I don't create a console application; that way I don't get a big black box popping in my face every time I run the app. I stay in in the debugger where all the action is. I use Debug.WriteLine so that the messages go to the output window, which docks nicely and stays visible after the app terminates.
I usually don't bother add debug code for stopping; I just use the debugger instead. If I do need to debug stopping, I make the project a console app, add a Stop forwarder method, and call it after a call to Console.ReadKey.
public class Service : ServiceBase
{
protected override void OnStart(string[] args)
{
// Start logic here.
}
protected override void OnStop()
{
// Stop logic here.
}
static void Main(string[] args)
{
using (var service = new Service()) {
if (Environment.UserInteractive) {
service.Start();
Thread.Sleep(Timeout.Infinite);
} else
Run(service);
}
}
public void Start() => OnStart(null);
}

How to implement mutex in windows service

Hello I'm new in the threading topic, I need to add a Mutex in my windows service because whenever I run it, it pops over and over the awesome.exe that a fantastic.bat opens if it's closed.
Fantastic.bat
#echo off
:1
"C:\awesome.exe"
goto :1
I made a C# project to create a windows service, I followed up this guide, following up through it was pretty simple and voila! I got my windows service as expected, however I think a mutex would be an appropiate apporach in order to avoid getting lots of processes opening over and over again
MyService.cs
using System;
using System.ServiceProcess;
using System.Timers;
namespace Good_enough_service
{
public partial class GoodService : ServiceBase
{
private Timer _syncTimer = null;
public GoodService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
_syncTimer = new Timer();
this._syncTimer.Interval = 1000;
this._syncTimer.Elapsed +=
new System.Timers.
ElapsedEventHandler(this.syncTimerTicker);
_syncTimer.Enabled = true;
}
protected override void OnStop()
{
_syncTimer.Enabled = false;
}
private void syncTimerTicker(object sender, EventArgs e)
{
System.Diagnostics.Process.Start(#"C:\fantastic.bat");
}
}
}
I was able to install the service but it pops up a lot of times the bat and therefor it opens a lot of times my awesome.exe
I'm looking at a lot of examples of how to use a Mutex in stackoverflow, microsoft docs and google queries I find, however to be honest since I'm very new to this topic I'm kind of confused in how to build this up, can someone assist me in how to implement this?
Program.cs This is part of the service project
using System.ServiceProcess;
namespace Good_enough_service
{
static class Program
{
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new GoodService()
};
ServiceBase.Run(ServicesToRun);
}
}
}
Given your goal is just to start an .exe and make sure it keeps running, all you need to do is use a Process object to start the executable directly, and then monitor it for completion via the HasExited property. When the process exits, just start a new one (or restart the existing one).
Process.HasExited Property

How do you track custom events from Azure WebJobs in Application Insights?

I have an Azure WebJobs (v2.2.0) project that I would like to monitor with Application Insights (AI), and there are events that I would like to be able to track. In a normal web app that is configured to use AI you can just use this:
TelemetryClient tc = new TelemetryClient();
tc.TrackEvent("EventName");
However this seems not to work in the context of a WebJob! I have configured my WebJob project as per the instructions on the WebJob SDK repo which ends up looking like this:
Program
using System.Configuration;
using System.Diagnostics;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
namespace WebJobs
{
public class Program
{
public static void Main()
{
JobHostConfiguration config = new JobHostConfiguration();
config.UseTimers();
using (LoggerFactory loggerFactory = new LoggerFactory())
{
string key = ConfigurationManager.AppSettings["webjob-instrumentation-key"];
loggerFactory.AddApplicationInsights(key, null);
loggerFactory.AddConsole();
config.LoggerFactory = loggerFactory;
config.Tracing.ConsoleLevel = TraceLevel.Off;
if (config.IsDevelopment)
config.UseDevelopmentSettings();
JobHost host = new JobHost(config);
host.RunAndBlock();
}
}
}
}
Functions
This is just a test function that will run every minute for half an hour.
using Core.Telemetry;
using Microsoft.ApplicationInsights;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Timers;
using System;
using System.Collections.Generic;
namespace WebJobs.Functions
{
public class TestFunctions
{
public void TelemetryTest([TimerTrigger(typeof(Schedule))] TimerInfo timer)
{
TelemetryClient tc = new TelemetryClient();
tc.TrackEvent("TelemetryTestEvent");
}
// schedule that will run every minute
public class Schedule : DailySchedule
{
private static readonly string[] times =
{
"12:01","12:02","12:03","12:04","12:05","12:06","12:07","12:08","12:09","12:10",
"12:11","12:12","12:13","12:14","12:15","12:16","12:17","12:18","12:19","12:20",
"12:21","12:22","12:23","12:24","12:25","12:26","12:27","12:28","12:29","12:30"
};
public Schedule() : base(times) { }
}
}
}
This seems to partially work in that I can see some telemetry in AI but not the custom events. For example I can see a Request show up each time TestFunctions.TelemetryTest() runs and various Traces during the initialisation of the WebJob.
I have probably not configured something properly or am not getting the TelemetryClient in the correct manner, but I cannot find any documentation on tracking custom events in WebJobs.
Any help would be appreciated.
Try setting the instrumentationkey explicit:
tc.Context.InstrumentationKey = "<your_key>";
According to the docs you should be able to get the key using
System.Environment.GetEnvironmentVariable(
"APPINSIGHTS_INSTRUMENTATIONKEY", EnvironmentVariableTarget.Process)
if you have set up application insights integration.

File permissions for Background Tasks in Win10

I have been trying to write a C# application on Win10 that only runs a background task that writes into a file.
Running the code below throws UnauthorizeAccessException exception after the trigger, 'Access to the path 'C:\temp' is denied'.
The file and directory both have full access for Everyone.
Also, what can background tasks access/run? I am trying to run a background task while in modern standby and for it to read some registers and/or run another tool. Is that even possible while still in modern standby?
Here is the code of my attempt in doing so:
Background task:
using Windows.ApplicationModel.Background;
using System.IO;
namespace RuntimeComponent2
{
public sealed class Class1 : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
File.WriteAllText(#"C:\temp\test.txt", "test");
}
}
}
Main page:
using System;
using Windows.ApplicationModel.Background;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace App3
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
var exampleTaskName = "MyTask1";
foreach (var t in BackgroundTaskRegistration.AllTasks)
{
t.Value.Unregister(true);
}
await BackgroundExecutionManager.RequestAccessAsync();
var builder = new BackgroundTaskBuilder();
builder.Name = exampleTaskName;
builder.TaskEntryPoint = "RuntimeComponent2.Class1";
builder.SetTrigger(new SystemTrigger(SystemTriggerType.TimeZoneChange, false));
BackgroundTaskRegistration task = builder.Register();
}
}
}
You cannot do that, and the problem is not regarding the BackgroundTask. Inside a UWP application you cannot write on your hard-disk. The only places you can read & write is accessible using ApplicationData.Current (LocalCacheFolder, LocalFolder and so on), or any folder the user choose explicitly using SaveFilePicker.
Furthermore, you need to use this syntax (Intellisense suggests you to use the class File, but in UWP isn't really available)
FileIO.ReadTextAsync(StorageFile file);

Invoking a method when WCF service is started

I have a WCF service (VS 2010, .Net 4.0) hosted as a Windows service.
What I want to do is this: I want a method which is in the service to be executed when the service is started.
I am not sure how you have used Windows Service to host your WCF service(s) but I would expect something like #SSamra described.
Anyway, below the line wherever you do .Open(); to open your service, you could initialize your wcf proxy and call your method.
Say your proxy is FirstWcfProxy, then you can do something like,
var firstWcfProxy = new FirstWcfProxy();
// or IFirstWcfService firstWcfProxy = new FirstWcfProxy();
firstWcfProxy.YourMethod();
EDIT:
If you want to ensure the method is called as soon as the service is started, initialize your proxy below the line sHost.Open(); and invoke the method there, like I described above
how about
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.ServiceProcess;
using System.ServiceModel;
namespace Windows_Service
{
public partial class WCFWindowsService : ServiceBase
{
ServiceHost m_serviceHost;
protected override void OnStart(string[] args)
{
m_serviceHost = new ServiceHost(typeof(FirstWcfService.Service));
m_serviceHost.Open();
}
protected override void OnStop()
{
if (m_serviceHost != null)
{
m_serviceHost.Close();
}
m_serviceHost = null;
}
}
}

Categories