Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WCF Wait for operations to finish when closing ServiceHost

Tags:

c#

.net

soap

wcf

I create a WCF SOAP server with an operation that takes some time to perform:

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    string LongRunningOperation();
}

[ServiceBehavior(
    ConcurrencyMode = ConcurrencyMode.Multiple,
    UseSynchronizationContext = false,
    InstanceContextMode = InstanceContextMode.Single)]
class MyService : IMyService
{
    public string LongRunningOperation()
    {
        Thread.Sleep(20000);
        return "Hey!";
    }
}

class Program
{
    static void Main(string[] args)
    {
        MyService instance = new MyService();
        ServiceHost serviceHost = new ServiceHost(instance);
        BasicHttpBinding binding = new BasicHttpBinding();
        serviceHost.AddServiceEndpoint(typeof(IMyService), binding, "http://localhost:9080/MyService");
        serviceHost.Open();
        Console.WriteLine("Service running");
        Thread.Sleep(10000);
        serviceHost.Close();
        Console.WriteLine("Service closed");
        Thread.Sleep(30000);
        Console.WriteLine("Exiting");
    }
}

The ServiceHost is opened, and after 10 seconds I close it.

When calling serviceHost.Close(), all clients currently connected, waiting for LongRunningOperation to finish, are inmediately disconnected.

Is there a wait of closing the ServiceHost in a cleaner way? That is, I want to disable the service listeners, but also wait for all currently connected clients to finish (or specify a maximum timeout).

like image 450
Álvaro Iradier Avatar asked May 16 '11 11:05

Álvaro Iradier


3 Answers

Im surprised calling ServiceHost.Close is not letting LongRunningOperation complete.

The whole architecture is setup to allow things time to gracefully shut down (e.g. the difference between Close and Abort transitions.). According to MSDN docs:

This method causes a CommunicationObject to gracefully transition from any state, other than the Closed state, into the Closed state. The Close method allows any unfinished work to be completed before returning.

Also there is a CloseTimeout on the ServiceHost for precisely this. Have you tried setting the CloseTimeout to be greater than 20 seconds? (According to Reflector the default CloseTimeout for ServiceHost is 10 seconds...)

like image 118
Jack Ukleja Avatar answered Nov 06 '22 16:11

Jack Ukleja


In principle, I think something like the following should be possible, though I haven't implemented it to confirm all the details:

  • Implement a custom IOperationInvoker wrapping the Dispatcher's normal OperationInvoker (you'll want an IServiceBehavior to install the wrapped invoker when the service dispatcher runtime is built)
  • the custom invoker would mostly delegate to the real one, but would also provide "gate-keeper" functionality to turn away new requests (e.g. raise a some kind of exception) when the service host is about to be shut down.
  • it would also keep track of operation invocations still in progress and set an event when the last operation invocation finishes or times out.
  • the main hosting thread would then wait on the invoker's "all finished" event before calling serviceHost.Close().
like image 22
Chris Dickson Avatar answered Nov 06 '22 15:11

Chris Dickson


What you are doing seems all wrong to me. The ServiceHost should never close abruptly. It is a service and should remain available. There is no real way to close gracefully without some participation from the client. When I say close gracefully, this also subjective from a clients perspective.

So I dont think I understand your requirements at all, however one way would be to implement a publish/subscribe pattern and when the host is ready to close, notify all subscribers of this event so that all connections could be closed by each respective client. You can read more about this here http://msdn.microsoft.com/en-us/magazine/cc163537.aspx

Again, this approach to hosting a service is not standard and thats why you are finding it hard to find a solution to this particular problem of yours. If you could elaborate on your use case/usage scenario, it would probably help to find a real solution.

like image 2
Glav Avatar answered Nov 06 '22 17:11

Glav