Running windows service in separate thread and use autofac for DI
autofac, multithreading, windows-services
Solution
The answer is actually simple: use scoping! You should do the following:
- Register all services (such as `DbContext`) that should live for the duration of a request or action with the LifetimeScope lifestyle. You'll usually have a timer in your windows service. Each 'pulse' can be considered a request.
- On the beginning of each request begin a lifetime scope.
- Within that scope, resolve the root object from the object graph and call its method.
- Dispose the scope.
In your case that means you need to change your design, since `NotificationService` is resolved once and its dependencies are reused on another thread. This is a no-no in dependency injection land.
Here's an alternative design:
// This method is called on a background thread
// (possibly in a timely manner)
public void Run()
{
try
{
using (var scope = container.BeginLifetimeScope())
{
var service = scope.Resolve<NotificationService>();
service.Execute();
}
}
catch (Exception ex)
{
// IMPORTANT: log exception.
// Not logging an exception will leave us in the dark.
// Not catching the exception will kill our service
// because we run in a background thread.
}
}
Using a lifetime scope allows you to get a fresh `DbContext` for every request and it would even allow you to run requests in parallel (with each request its own `DbContext`).
Problem
I'm trying to create a long running windows service, so I need to run the actual worker class on a separate thread, to avoid the "service did not respond in a timely fashion" error when I right click and select start in Windows Service Manager. The worker class ("NotificationProcess") has a whole raft of dependencies and I'm using Autofac to satisfy these. I'm really not sure how to set up Autofac for the worker class. At the moment I'm getting errors telling me that the DbContext has been disposed when I go to use it in the "Execute" method of the worker class. I guess I'm looking for how to write a windows service and use a new thread for the worker class with dependencies satisfied by autofac. I've googled and can't find any examples of this. Any suggestions would be awesome. Here's what I've got so far... Program.cs: ``` static class Program { static void Main() { using (var container = ServiceStarter.CreateAutoFacContainer()) { var service = container.Resolve<NotificationService>(); if (Environment.UserInteractive) { service.Debug(); } else { ServiceBase.Run(container.Resolve<NotificationService>()); } } ``` The Service class: ``` public partial class NotificationService : ServiceBase { private NotificationProcess _app; readonly ILifetimeScope _lifetimeScope; public NotificationService(ILifetimeScope lifetimeScope) { _lifetimeScope = lifetimeScope; InitializeComponent(); } protected override void OnStart(string[] args) { _app = _lifetimeScope.Resolve<NotificationProcess>(); _app.Start(); } ``` The worker class: ``` public class NotificationProcess { private Thread _thread; private readonly IBankService _bankService; private readonly IRateService _rateService; private readonly IEmailService _emailService; private readonly IRateChangeSubscriberService _rateChangeSubscriberService; private readonly IRateChangeNotificationService _rateChangeNotificationService; private readonly ILogManager _logManager; public NotificationProcess(IBankService bankService, ILogManager logManager, IRateService rateService, IEmailService emailService, IRateChangeSubscriberService rateChangeSubscriberService, IRateChangeNotificationService rateChangeNotificationService) { _bankService = bankService; _rateService = rateService; _emailService = emailService; _rateChangeSubscriberService = rateChangeSubscriberService; _rateChangeNotificationService = rateChangeNotificationService; _logManager = logManager; } public void Start() { _thread = new Thread(new ThreadStart(Execute)); _thread.Start(); } public void Execute() { try { var rateChangeToNotify = _rateService.GetRateChangesForNotification(); foreach (var rateChange in rateChangeToNotify) { //do whatever business logic..... } } } ```