Search code examples
c#windows-servicestopshelfcompositionroot

When to compose application root in a windows service


Given a C# console application that will run as a windows service, there are sort of 2 application entry points. The first obvious one is the static void Main method. Part of the job of this method is to install something that extends ServiceBase and then run it to get its OnStart method to be invoked. There are also higher level tools like Topshelf that help you avoid lower level ServiceBase implementations, but still, you end up with 2 potential application entry points: static void Main, and some kind of OnStart method.

Should the application root for such a service be composed within the service's OnStart method, or earlier than that as part of static void Main?

It seems that there could be an advantage to composing within the service's OnStart method (and destroying / disposing it in the OnStop method) since restarting a service would then compose a new application root. The only real disadvantage I can see here is that if I am using a tool like Topshelf, I can't use the DI container to obtain an instance of my Service class. Then again, that's probably not a real disadvantage. Still, most of the applications I read compose the root during Main, not during OnStart, and I'm not sure why.

Is one approach really better than the other, or does it depend, and my question is really an opinion-based one?


Solution

  • I think it's more an opinion than a fact, but I prefer to compose during service building and then use OnStart() to activate the service I composed before. This is the way I usually work (with Topshelf). For example:

    program.cs

    public class Program
    {
        private static ILifetimeScope _scope;
        private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
    
        public static void Main(string[] args)
        {
            try
            {
                XmlConfigurator.Configure();
    
                // configure composition
                _scope = CompositionRoot.CreateScope();
    
                HostFactory.Run(x =>
                {
                    x.UseLog4Net();
                    x.UseAutofacContainer(_scope);
    
                    x.Service<IMyService>(svc =>
                    {
                        svc.ConstructUsingAutofacContainer();
                        svc.WhenStarted(tc => tc.Start());
                        svc.WhenStopped(tc =>
                        {
                            tc.Stop();
                            _scope.Dispose();
                        });
                    });
    
                    x.RunAsNetworkService();
                    x.StartManually();
                });
            }
            catch (Exception e)
            {
                Log.Error("An error occurred during service construction.", e);
                throw;
            }
        }
    }
    

    composition.cs

    internal class CompositionRoot
    {
        public static ILifetimeScope CreateScope()
        {
            var builder = new ContainerBuilder();
    
            builder.RegisterType<MyService>().As<IMyService>().SingleInstance();
            // other things you want to register
    
            return builder.Build();
        }
    }
    

    imyservice.cs

    public interface IMyService
    {
        void Start();
        void Stop();
    }
    

    The only real disadvantage I can see here is that if I am using a tool like Topshelf, I can't use the DI container to obtain an instance of my Service class

    This is true, but you don't need to access the program.cs code, just the MyService code, which will represent the actual "core" code of your service.

    Also when you stop the service you will actually kill it (unless you pause it), so the composition will be executed again, no matter if you put it inside the "onStart()" or not.

    As usual, IMHO. :)