1

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?

danludwig
  • 45,241
  • 21
  • 150
  • 230
  • Main is the unique process entry, so it's more a "process-wide" constructor. A service program/process can host multiple services instances, not just one. Each service instance constructor should be called only once per process lifetime, but OnStart/OnStop can be called repeatedly. Now, I don't really understand what you mean by application root or what you want to compose, but I suppose you should tie per-service initialization with instance initialization. So if you use ServiceBase, it should be tied somehow to it's constructor. – Simon Mourier Mar 15 '16 at 07:03
  • Sound to me you are over-complicating it needlessly. The entrypoint in both cases is Main(). The only *extra* thing that happens is the OnStart() call by the service manager, you typically start a thread to do something useful. There is no point in delaying anything, it doesn't optimize anything, that OnStart() call is going to happen pretty quickly after Main() started running. – Hans Passant Mar 16 '16 at 12:30
  • @HansPassant so when a windows service is restarted, the Main() method is invoked again? If so, then I misunderstood something and you are right that this question is irrelevant. – danludwig Mar 16 '16 at 12:32

1 Answers1

2

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. :)

Luca Ghersi
  • 2,956
  • 18
  • 28