Search code examples
c#signalrusing

Explain why "using" won't work in service?


So I was stuck on this problem for about a week. I was trying to run a project to recieve a TCP connection and start a SignalR Hub as a Service. Both worked perfectly running the project as a .exe file. The TCP part would work perfectly, however I was having problems with the SignalR side.

The reason ended up being the using statement.

Before

using (WebApp.Start<SignalrStartup>(url))
{
    Console.ForegroundColor = ConsoleColor.Green;
    Console.WriteLine("Server running on {0}", url); // was url
    Console.WriteLine("ID\tMessage");
    Console.ReadLine();
}

After

WebApp.Start<SignalrStartup>(url);

I had tried running the the code with the Console.WriteLine() commented out, as I thought it might be throwing an exception as the is no console to output to once run as a service. This also didn't work, but also wouldn't work as a .exe file either as it needed the Console.ReadLine() to keep the console open, sort of how you need it to keep HelloWorld.cs open. Once the using wrapper was removed along with the console, it would then work in both the .exe and the service.

I have read that the using statement kills objects in it once you leave the wrapper. But I don't understand how the After bit of code keeps the .exe code open once running. Is there any point in using using or have I been using it wrong?

Edit

protected override void OnStart(string[] args)
{
    Task.Factory
        .StartNew(() => StartTCP())
        .ContinueWith(t => StartSignalR());
}

The call is being made from the StartSignalR() method.


Solution

  • What's Going Wrong With Your Service?

    The problem you're having is that your Console.ReadLine does a blocking wait for standard input. That will block forever on a windows service and cause the service control manager to time out the service start after 30 seconds. This question has more information about what's going on.

    If you remove the entire contents of the using statement, and the statement itself as you have done the server that was started by WebApp.Start will continue on in the background after your service Start method completes. This is the correct behaviour of a service.

    What you're effectively doing here is to leak the asynchronous workers that WebApp.Start created. This means that they're still running after the service start completes to listen for requests.

    You should probably keep track of the IDisposable that WebApp.Start returns though, and dispose it in the Stop method.

    What About using?

    A using statement makes sure a resource is always disposed when control leaves the using statement's block. This can be either due to an exception being thrown or because the block completes successfully and control moves on to the next part of the program.

    using statements are used when you know that nothing wants to access the resource after the block has completed. This is usually the case when you're dealing with methods that return an IDisposable to you. In your case however you don't want to call dispose because you want the threads that WebApp.Start created to continue after the service has started.