Search code examples
wcfvisual-studio-2010.net-4.0nettcpbindingwebhttpbinding

Having WCF callback only for binding that supports it


I am trying to create a WCF service, that has a webHttpBinding endpoint (for Java clients) and a netTcpBinding endpoint (for .NET clients).

With the netTcpBinding endpoint I would like to be able to use callbacks in order to be alerted to events, but when I try to configure this, WCF complains because the service also has the webHttpBinding endpoint, which doesn't support callbacks.

Is there a way of having the callback utilised by one endpoint but not another?


Solution

  • No, the binding will validate that it can honor the contract; if the contract is a duplex contract (i.e., it specifies a CallbackContract) but the binding can't do duplex, then it will throw during validation.

    What you can do is to have a base contract which is used by the webHttpBinding endpoint, and another contract (this time a duplex one), derived from the first, which is used by the netTcpBinding endpoint.

    The code below shows an example of such contract arrangement.

    public class StackOverflow_7341463
    {
        [ServiceContract]
        public interface ICalc
        {
            [OperationContract, WebGet]
            int Add(int x, int y);
            [OperationContract, WebGet]
            int Subtract(int x, int y);
            [OperationContract, WebGet]
            int Multiply(int x, int y);
            [OperationContract, WebGet]
            int Divide(int x, int y);
        }
        [ServiceContract(CallbackContract = typeof(ICalcNotifications))]
        public interface INotifyingCalc : ICalc
        {
            [OperationContract]
            void Connect();
            [OperationContract]
            void Disconnect();
        }
        [ServiceContract]
        public interface ICalcNotifications
        {
            [OperationContract(IsOneWay = true)]
            void OperationPerformed(string text);
        }
        public class Service : INotifyingCalc
        {
            static List<ICalcNotifications> clients = new List<ICalcNotifications>();
    
            #region ICalc Members
    
            public int Add(int x, int y)
            {
                this.NotifyOperation("Add", x, y);
                return x + y;
            }
    
            public int Subtract(int x, int y)
            {
                this.NotifyOperation("Subtract", x, y);
                return x - y;
            }
    
            public int Multiply(int x, int y)
            {
                this.NotifyOperation("Multiply", x, y);
                return x * y;
            }
    
            public int Divide(int x, int y)
            {
                this.NotifyOperation("Divide", x, y);
                return x / y;
            }
    
            #endregion
    
            #region INotifyingCalc Members
    
            public void Connect()
            {
                var callback = OperationContext.Current.GetCallbackChannel<ICalcNotifications>();
                clients.Add(callback);
            }
    
            public void Disconnect()
            {
                var callback = OperationContext.Current.GetCallbackChannel<ICalcNotifications>();
                clients.Remove(callback);
            }
    
            #endregion
    
            private void NotifyOperation(string operationName, int x, int y)
            {
                foreach (var client in clients)
                {
                    client.OperationPerformed(string.Format("{0}({1}, {2})", operationName, x, y));
                }
            }
        }
        class MyCallback : ICalcNotifications
        {
            public void OperationPerformed(string text)
            {
                Console.WriteLine("Operation performed: {0}", text);
            }
        }
        public static void Test()
        {
            string baseAddressTcp = "net.tcp://" + Environment.MachineName + ":8008/Service";
            string baseAddressHttp = "http://" + Environment.MachineName + ":8000/Service";
            ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddressHttp), new Uri(baseAddressTcp));
            host.AddServiceEndpoint(typeof(ICalc), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
            host.AddServiceEndpoint(typeof(INotifyingCalc), new NetTcpBinding(SecurityMode.None), "");
            host.Open();
            Console.WriteLine("Host opened");
    
            var factory = new DuplexChannelFactory<INotifyingCalc>(
                new InstanceContext(new MyCallback()),
                new NetTcpBinding(SecurityMode.None),
                new EndpointAddress(baseAddressTcp));
            var proxy = factory.CreateChannel();
            proxy.Connect();
            Console.WriteLine("Proxy connected");
    
            Console.WriteLine(new WebClient().DownloadString(baseAddressHttp + "/Add?x=4&y=7"));
            Console.WriteLine(new WebClient().DownloadString(baseAddressHttp + "/Multiply?x=44&y=57"));
            Console.WriteLine(new WebClient().DownloadString(baseAddressHttp + "/Divide?x=432&y=16"));
    
            proxy.Disconnect();
    
            Console.Write("Press ENTER to close the host");
            Console.ReadLine();
    
            ((IClientChannel)proxy).Close();
            factory.Close();
            host.Close();
        }
    }