Freigeben über


Client proxy close method call does not finish immediately in one-way WCF calls

Problem: Clients calling a one-way method in WCF Service and then close method on proxy does not return until the call is actually finished or call times out. Ever wonder why this happens?

Cause: When you specify “One-Way” on your interface, the underlying channel operation is still two-way since the one way binding element is not in the channel stack. Thus, the close operation gets blocked until the one way operation completes.

This is by design and the development team is working to change it in future versions of .Net framework.

Scenarios: The problem is more visible in windows workflow applications calling WCF service. Workflow follows a sequence, if the activity calling a WCF service does not return then the activities following it will not execute and the workflow is stuck.

You'll not realize this in regular WCF clients unless you are doing something in sequence after closing the proxy.

Steps to reproduce:

With the code below, the close method call on proxy will return only after the server is done processing the call.

Service Code:

namespace WCFServiceLibrary{

    [ServiceContract]
    public interface IService1
    {
        [OperationContract(IsOneWay = true)]
        void SetData(int value);
    }

    public class Service1 : IService1
    {
        public void SetData(int value)
        {
             //Application specific code
        }

    }
}

Service Host code:

 Form1ServiceHost = new ServiceHost(this, new Uri ("net.tcp://localhost:8092/WindowsFormApp/Form1/"), new Uri("https://localhost:8094/WindowsFormApp/Form1/"));
            System.ServiceModel.Channels.Binding binding = new NetTcpBinding();
                      
            Form1ServiceHost.AddServiceEndpoint("WCFServiceLibrary.IService1", binding,

"");

            Form1ServiceHost.Open();

Client Code:

    Binding binding = new NetTcpBinding();
    Service1Client client = new Service1Client(binding, new EndpointAddress("net.tcp://localhost:8091/WindowsFormApp/Form1/"));
    client.SetData(10);
    Console.WriteLine("set data");
    Console.WriteLine("Now closing the channel,Before close, current time is {0}", DateTime.Now.ToString() + " " + DateTime.Now.Millisecond.ToString());
    client.Close();
    Console.WriteLine("Now closing the channel,After close, current time is {0}", DateTime.Now.ToString() + " " + DateTime.Now.Millisecond.ToString());

Solution (Work around):

Layer the OneWayBindingElement on top of netTcpBinding as shown in the below code. This way, close call on proxy will return immediately and eventually the one-way call will return in fire and forget fashion.

Service Code:

namespace WCFServiceLibrary{

    [ServiceContract]
    public interface IService1
    {
        [OperationContract(IsOneWay = true)]
        void SetData(int value);
    }

    public class Service1 : IService1
    {
        public void SetData(int value)
        {
             //Application specific code
        }
    }
}

Service Host code:

            Form1ServiceHost = new ServiceHost(this, new Uri("net.tcp://localhost:8091/WindowsFormApp/Form1/"), new Uri("https://localhost:8090/WindowsFormApp/Form1/"));

            Binding binding = new NetTcpBinding();
            BindingElementCollection oldBindingElements = binding.CreateBindingElements();
            BindingElementCollection bindingElements = new BindingElementCollection();
            bindingElements.Add(new OneWayBindingElement());
            foreach (BindingElement bindingElement in oldBindingElements)
            {
                bindingElements.Add(bindingElement);
            }

            binding = new CustomBinding(bindingElements);

            Form1ServiceHost.AddServiceEndpoint("WCFServiceLibrary.IService1", binding, "");
            Form1ServiceHost.Open();

Client Code:

     Binding binding = new NetTcpBinding();
            BindingElementCollection oldBindingElements = binding.CreateBindingElements();
            BindingElementCollection bindingElements = new BindingElementCollection();
            bindingElements.Add(new OneWayBindingElement());
            foreach (BindingElement bindingElement in oldBindingElements)
            {
                bindingElements.Add(bindingElement);
            }

            binding = new CustomBinding(bindingElements);

            Service1Client client = new Service1Client(binding, new EndpointAddress("net.tcp://localhost:8091/WindowsFormApp/Form1/"));
            client.SetData(10);
            Console.WriteLine("set data");
            Console.WriteLine("Now closing the channel,Before close, current time is {0}", DateTime.Now.ToString() + " " + DateTime.Now.Millisecond.ToString());
            client.Close();
            Console.WriteLine("Now closing the channel,After close, current time is {0}", DateTime.Now.ToString() + " " + DateTime.Now.Millisecond.ToString());

Comments

  • Anonymous
    April 22, 2009
    Thank you this is very useful.

  • Anonymous
    December 28, 2011
    Had to also add CompositeDuplexBindingElement in order to get this working with a contract that had one-way and two-way calls.