WCF asynchronous callback

17,498

You need to change the CallbackContract property of the service contract IMessage to that type (IAsyncMessageCallback). The example below runs with the async callback.

    public class StackOverflow_5979252
{
    [ServiceContract(Name = "IMessageCallback")]
    public interface IAsyncMessageCallback
    {
        [OperationContract(AsyncPattern = true)]
        IAsyncResult BeginOnMessageAdded(string msg, DateTime timestamp, AsyncCallback callback, object asyncState);
        void EndOnMessageAdded(IAsyncResult result);
    }
    [ServiceContract(CallbackContract = typeof(IAsyncMessageCallback))]
    public interface IMessage
    {
        [OperationContract]
        void AddMessage(string message);
    }
    [ServiceBehavior(IncludeExceptionDetailInFaults = true, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class Service : IMessage
    {
        public void AddMessage(string message)
        {
            IAsyncMessageCallback callback = OperationContext.Current.GetCallbackChannel<IAsyncMessageCallback>();
            callback.BeginOnMessageAdded(message, DateTime.Now, delegate(IAsyncResult ar)
            {
                callback.EndOnMessageAdded(ar);
            }, null);
        }
    }
    class MyClientCallback : IAsyncMessageCallback
    {
        public IAsyncResult BeginOnMessageAdded(string msg, DateTime timestamp, AsyncCallback callback, object asyncState)
        {
            Action<string, DateTime> act = (txt, time) => { Console.WriteLine("[{0}] {1}", time, txt); };
            return act.BeginInvoke(msg, timestamp, callback, asyncState);
        }

        public void EndOnMessageAdded(IAsyncResult result)
        {
            Action<string,DateTime> act = (Action<string,DateTime>)((System.Runtime.Remoting.Messaging.AsyncResult)result).AsyncDelegate;
            act.EndInvoke(result);
        }
    }
    static Binding GetBinding()
    {
        return new NetTcpBinding(SecurityMode.None);
    }
    public static void Test()
    {
        string baseAddress = "net.tcp://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        host.AddServiceEndpoint(typeof(IMessage), GetBinding(), "");
        host.Open();
        Console.WriteLine("Host opened");

        InstanceContext instanceContext = new InstanceContext(new MyClientCallback());
        DuplexChannelFactory<IMessage> factory = new DuplexChannelFactory<IMessage>(instanceContext, GetBinding(), new EndpointAddress(baseAddress));
        IMessage proxy = factory.CreateChannel();
        proxy.AddMessage("Hello world");

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        ((IClientChannel)proxy).Close();
        factory.Close();
        host.Close();
    }
}
Share:
17,498
user481779
Author by

user481779

Updated on June 15, 2022

Comments

  • user481779
    user481779 almost 2 years

    I have successfully implemented the WCF callback pattern in my code and now I want to implement an asynchronous callback. Here is my interface code:

    [ServiceContract(Name = "IMessageCallback")]
    public interface IMessageCallback
    {
      [OperationContract(IsOneWay = true)]
      void OnMessageAdded(string message, DateTime timestamp);
    }
    
    [ServiceContract(Name="IMessageCallback")]
    public interface IAsyncMessageCallback
    {
      [OperationContract(AsyncPattern = true)]
      IAsyncResult BeginOnMessageAdded(string msg, DateTime timestamp, AsyncCallback callback, object asyncState);
      void EndOnMessageAdded(IAsyncResult result);
    }
    
    [ServiceContract(CallbackContract = typeof(IMessageCallback))]
    public interface IMessage
    {
      [OperationContract]
      void AddMessage(string message);
    }
    

    To use the synchronous callback I declared my channel and endpoint like so:

    DuplexChannelFactory<IMessage> dcf = new DuplexChannelFactory<IMessage>(new InstanceContext(this), "WSDualHttpBinding_IMessage");
    <endpoint address="net.tcp://localhost:8731/Message/"
                binding="netTcpBinding"
                contract="WCFCallbacks.IMessage" name="WSDualHttpBinding_IMessage">
    

    I am having trouble getting the right combination of endpoint and channel to utilize the asynchronous callback. Can someone point me in the right direction?

    In addition when the following line of code is executed:

    OperationContext.Current.GetCallbackChannel<IAsyncMessageCallback>();
    

    I get the following error:

    Unable to cast transparent proxy to type 'WCFCallbacks.IAsyncMessageCallback'
    
  • user481779
    user481779 almost 13 years
    Figueria - Thanks for the response but when I put my clients on a separate thread and call AddMessage it blocks when BeginInvoke is called.
  • carlosfigueira
    carlosfigueira almost 13 years
    Are you setting the concurrency mode to Multiple (or reentrant) on your service? You can also add the [CallbackBehavior] to the callback class (MyClientCallback in the example I posted) to set the concurrency mode on the client. Try setting it to Multiple to see if what you're seeing is a deadlock.
  • user481779
    user481779 almost 13 years
    I tried it every which way with no success. Let me be more clear that in my architecture I have a service serving multiple clients. The clients call the service side function AddMessage and the service can callback to the clients (hopefully asynchronously) on the function OnMessageAdded. So AddMessage is implemented o the service-side and OnMessageAdded on the client side.
  • carlosfigueira
    carlosfigueira almost 13 years
    I changed the code I added to make the call in a separate thread thread, and even created multiple clients on separate threads, I still don't have the issue. One thing you can try to do is to start with a working code (does my sample work for you?) and then start modifying it slowly until you hit the point where it breaks, that will be a good information to have.
  • user481779
    user481779 almost 13 years
    Thanks carlos. I will do as you suggest. I have been trying to wedge your code into mine.