You have probably created a Windows service or other types of applications where you have code which needs to be run in the background to not block the main thread. To do that, you have different types of solutions in .NET, for instance Threads, Timers and Tasks. However, you need to construct them manually, make sure that they do not throw unhandled exceptions, etc.
Griffin.Framework provides a simpler solution where you only need to implement a small interface. The library also has full support for your favorite inversion of control container, which enables to use dependency injection in your application services.
Application Services
Application services are classes that should be treated as single instances, i.e., they are created and started when your application is started and are stopped and disposed when your application is stopped.
This library has two different base classes for application services. Either inherit one of them or just implement the interface IApplicationService
.
To enable IoC support, you either have to install one of our IoC adapter packages from nuget or implement two interfaces for your favorite container.
IApplicationService
This contract allows you to start background services when your application starts and stop them when the application ends. They are treated as single instances and are also monitored by this library (if they implement IGuardedService
).
public class RequestQueueReader : IApplicationService
{
private readonly ILog _logger = LogManager.GetLogger(typeof (RequestQueueReader));
private readonly IContainer _container;
private readonly QueueReader _reader;
private int _retryCounter;
public RequestQueueReader(IContainer container)
{
_container container;
var queueName = ConfigurationManager.AppSettings["RequestQueue.Name"];
if (queueName == null)
throw new ConfigurationErrorsException(
"Did not find 'RequestQueue.Name' in appSettings. Configure it.");
_reader = new QueueReader(queueName, new XmlMessageSerializer(new[] {typeof (RequestMsg)}));
_reader.MessageReceived += OnMessageRead;
_reader.Faulted += OnFaulted;
}
public void Start()
{
_reader.Start();
}
public void Stop()
{
_reader.Stop();
}
private void OnMessageRead(object sender, MessageReceivedEventArgs e)
{
var message = (Anno) e.Message;
_annoMessageHandler.Handle(message);
_retryCounter = 0;
}
}
ApplicationServiceThread
A base class which uses a Thread for your processing. It will also take care of starting/stopping your service and handle uncaught exceptions. If an uncaught exception is detected, it will shutdown and wait for the ApplicationServiceManager
to restart it.
public class YourService : ApplicationserviceThread
{
protected void Run(WaitHandle shutdownHandle)
{
while (true)
{
try
{
if (shutdownHandle.WaitOne(100))
break;
}
catch (DataException ex)
{
throw;
}
catch (Exception ex)
{
_log.Error("Opps", ex);
}
}
}
}
ApplicationServiceTimer
ApplicationServiceTimer
allows you to execute jobs in the background without having to deal with unhandled exceptions.
Do note that instances of this class are also treated as single instances.
public class DeleteOldMessages : ApplicationServiceTimer
{
public override void Execute()
{
using (var connection = new SqlConnection("..."))
{
using (var transaction = connection.BeginTransaction())
{
}
}
}
}
However, if you are using timers that require a container scope (lifetime scope), you might want to use IBackgroundJob
instead. The difference is that the instances of ApplicationServiceTimer
are single instances, while background jobs are created/disposed every time they are being executed.
Controlling Services
The classes are controlled by the ApplicationServiceManager
. It can both use your favorite IoC container (through the adapter contracts) or by using assembly scanning.
Without a container:
var serviceLocator = new AssemblyScanner();
serviceLocator.Scan(Assembly.GetExecutingAssembly());
_serviceManager = new ApplicationServiceManager(serviceLocator);
_serviceManager.Start();
Using an inversion of control container:
var builder = new ContainerBuilder();
builder.Register<YourServiceClass>().AsImplementedInterfaces().SingleInstance();
var autofac = builder.Build();
var container = new AutofacAdapter(autofac);
_serviceManager = new ApplicationServiceManager(container);
_serviceManager.Start();
_serviceManager.Stop();
By using a container, you can also use dependency injection in your services.
Detecting Failures
The ApplicationServiceManager
will restart services which have failed (as long as they implement IGuardedService
). But you probably want to be able to log all failures. This can be done with the help of the ServiceFailed
event:
public class Program
{
public static void Main(string[] argv)
{
_appServiceManager.ServiceFailed += OnServiceFailure;
}
public static void OnServiceFailure(object sender, ApplicationServiceFailedEventArgs e)
{
_logger.Error("Service " + e.ApplicationService.GetType().Name + " failed", e.Exception);
}
}
Starting/Stopping Services
The services can for instance be started/stopped using the application config. To activate a service, you just set the appSetting
<add key="YourClass.Enabled" value="true" />
and to disable it, you set the same key to false
.
This can also be done during run time if your services implement IGuardedService
.
Background Jobs
Do you have work which needs to be run occasionally? Do you use an inversion of control container?
Then the background jobs are for you. If not, look at ApplicationServiceTimer
instead.
The jobs are run at certain intervals within an isolated child scope in a container. That means that all other jobs will continue to work, even if one of your background jobs fail. It also means that you can use an own transaction for each job.
public class CleanUpOldFriendRequests : IBackgroundJob
{
private readonly IUnitOfWork _uow;
private static DateTime _lastExecutionTime;
public CleanUpOldFriendRequests(IUnitOfWork uow)
{
if (uow == null) throw new ArgumentNullException("uow");
_uow = uow;
}
public void Execute()
{
if (_lastExecutionTime.Date >= DateTime.Today)
return;
_lastExecutionTime = DateTime.Today;
using (var cmd = _uow.CreateCommand())
{
cmd.CommandText = "DELETE FROM FriendRequests WHERE CreatedAtUtc < @datum";
cmd.AddParameter("datum", DateTime.Today.AddDays(-10));
cmd.ExecuteNonQuery();
}
}
}
Running the Jobs
The jobs are managed by the BackgroundJobManager
class. It takes care of starting the jobs, running them at defined intervals and finally report any errors if they fail.
To get started, you need to create a new instance of the class and provide the IoC adapter that will be used to locate services.
public class Service1 : ServiceBase
{
BackgroundJobManager _jobInvoker;
IContainer _container;
public Service1()
{
_serviceLocator = CreateContainer();
_jobInvoker = new BackgroundJobManager(_container);
_jobInvoker.ScopeClosing += OnScopeClosing;
}
public override OnStart(string[] argv)
{
_jobInvoker.Start();
}
public override OnStop()
{
_jobInvoker.Stop();
}
public void CreateContainer()
{
var builder = new Containerbuilder();
builder.Register<DeleteOldMessages>().AsImplementedInterfaces().SingleInstance();
var autofac = builder.Build();
_container = new AutofacAdapter(autofac);
}
public void OnScopeClosing(object sender, ScopeCreatedEventArgs e)
{
e.Scope.Resolve<IUnitOfWork>().SaveChanges();
}
}
Controlling Intervals
In the BackgroundJobManager
, there are two properties which control how often the jobs are being executed.
Name | Description |
StartInterval | Amount of time before the jobs are executed for the first time (after Start() has been invoked). |
ExecuteInterval | Interval between every job execution (for a single job). The interval is reset when the job completes its execution. |
Logging Errors
Sometimes job fails. You can log all errors by subscribing to an event:
public class Service1 : ServiceBase
{
BackgroundJobManager _jobInvoker;
IContainer _container;
public Service1()
{
_serviceLocator = CreateContainer();
_jobInvoker = new BackgroundJobManager(_container);
_jobInvoker.JobFailed += OnBackgroundJobFailure;
}
private void OnBackgroundJobFailure(object sender, BackgroundJobFailedEventArgs e)
{
_logger.Error("Failed to execute " + e.Job, e.Exception);
}
}
Summary
The application service classes take care of bootstrapping, exception handling and add dependency injection support for timers and threads. Use them when you need a simple way of building background services in your application.
Install the nuget package griffin.framework to get started.
The documentation is also available at github (scroll down).
Sample project is also available at github.