2

I am trying to implement asynchronous feedback from an existing slow synchronous WCF service function. So I followed the MSDN example to get familiar: https://msdn.microsoft.com/en-us/library/ms731177(v=vs.90).aspx

I reused CompletedAsyncResult from the example. The host WCF service:

[ServiceContract]
    public interface IService
    {
        [OperationContract]
        string SlowHelloWorld(string msg);

        [OperationContract(AsyncPattern = true)]
        IAsyncResult BeginFeedback(string msg, AsyncCallback callback, object asynState);

        string EndFeedback(IAsyncResult result);
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class Service : IService
    {
        public string SlowHelloWorld(string msg)
        {
            Console.WriteLine("SlowHelloWorld called with: \"{0}\"", msg);
            Thread.Sleep(5000);

            return "Hello world " + msg;
        }

        public IAsyncResult BeginFeedback(string msg, AsyncCallback callback, object asynState)
        {
            Console.WriteLine("BeginServiceAsyncMethod called with: \"{0}\"", msg);
            return new CompletedAsyncResult<string>(msg);
        }

        public string EndFeedback(IAsyncResult r)
        {
            CompletedAsyncResult<string> result = r as CompletedAsyncResult<string>;
            Console.WriteLine("EndServiceAsyncMethod called with: \"{0}\"", result.Data);
            Thread.Sleep(1000);

            return result.Data;
        }
    }

Create the host:

 internal class Program
    {
        private static void Main(string[] args)
        {
            Uri addr = new Uri("net.tcp://localhost:4588");

            NetTcpBinding binding = new NetTcpBinding();
            binding.Security.Mode = SecurityMode.None;

            Service service = new Service();
            var host = new ServiceHost(service, addr);
            host.AddServiceEndpoint(typeof(IService), binding, "Message");
            host.Open();

            Console.ReadKey();
        }
    }

The client:

private static void Main(string[] args)
        {
            var binding = new NetTcpBinding();
            binding.Security.Mode = SecurityMode.None;
            var endpoint = new EndpointAddress("net.tcp://localhost:4588/Message");
            var channelFactory = new ChannelFactory<IService>(binding, endpoint);

            IService client = null;

            try
            {
                Mutex mutex = new Mutex();
                var numberOfThreads = 10;

                var message = "I am Client";
                client = channelFactory.CreateChannel();

                int globalValue = 0;
                for (int i = 0; i < numberOfThreads; i++)
                {
                    var thread = new Thread(() =>
                    {
                        // increment counter safely
                        mutex.WaitOne();
                        var threadValue = globalValue;
                        globalValue++;
                        mutex.ReleaseMutex();

                        // wait for slow hello world to start before calling async method
                        Thread.Sleep(500);

                        var res = client.BeginFeedback(
                            threadValue.ToString(),
                            delegate(IAsyncResult r)
                            {
                                var result = client.EndFeedback(r);
                                Console.WriteLine(result);
                            },
                            null);
                    });

                    thread.Start();
                }

                Console.WriteLine(client.SlowHelloWorld(message));

                Console.ReadKey();
                ((ICommunicationObject)client).Close();
            }
            catch
            {
                if (client != null)
                {
                    ((ICommunicationObject)client).Abort();
                }
            }
        }

Ignore that fact that the return order is not guaranteed.

This solution gives me the wrong result as the slow method finishes and then the threads can finish their work ala:

Hello world I am Client
1
0
3
6
2
9
8
4
7
5

Next I found a few hints that I may be calling the client function wrongly: https://stackoverflow.com/questions/22590239/calling-async-methods-from-a-wcf-service/22591516#22591516 http://geekswithblogs.net/MarkPearl/archive/2011/10/12/async-using-the-old-async-begin--end-pattern.aspx https://stackoverflow.com/questions/448487/is-there-a-way-to-get-error-feedback-on-asynchronous-wcf-calls

So I went ahead tried to call the host method asynchronously, hopefully.

  public delegate void MethodInvoker();

and changed the Thread method to do:

client.BeginFeedback(
                            threadValue.ToString(),
                            delegate(IAsyncResult r)
                            {
                                var invoker = new MethodInvoker(delegate()
                                {
                                    var result = client.EndFeedback(r);
                                    Console.WriteLine(result);
                                });

                                invoker.Invoke();
                            },
                            null);

Same result.

Trying to call the slow method also asynchronously(hopefully):

public delegate string AsyncMethodCaller(string message);

        var caller = new AsyncMethodCaller(client.SlowHelloWorld);
        caller.BeginInvoke(
            message,
            new AsyncCallback(delegate(IAsyncResult result)
            {
                AsyncResult beginResult = (AsyncResult)result;

                AsyncMethodCaller endCaller = (AsyncMethodCaller)beginResult.AsyncDelegate;

                var endResult = endCaller.EndInvoke(beginResult);

                Console.WriteLine(endResult);
            }),
            null);

Still no luck. These seem to hint that ConcurrencyMode must be Multiple on the host side. But no difference. https://stackoverflow.com/questions/17080037/async-wcf-self-hosted-service https://stackoverflow.com/questions/11264825/accessing-persession-service-simultaneously-in-wcf-using-c-sharp

This one seems to be what I want, add an asynchronous function to an existing synchronous interface but I cant use await, async and Task as .Net 3.5 https://stackoverflow.com/questions/27882501/how-to-add-async-support-to-a-net-4-5-wcf-service-so-it-doesnt-disrupt-existin

So the questions: What am I missing/did I not understand about async WCF communication?

Must all the service methods I call follow the async pattern also? Including the long running method.

Johannes Rabauer
  • 330
  • 3
  • 15
kristian mo
  • 1,476
  • 2
  • 11
  • 19
  • What are you trying to accomplish by going async? – usr Jun 12 '15 at 14:19
  • We have an existing application which performs an ExportData() function to external systems which may take minutes/hours. Because of customer requirements sometimes we have to start the application in a WCF service host mode on a remote server. Then we run the same application locally to prepare the data, send it to the remote host and it performs the job. I want to trigger this as normal and then start a thread monitoring the progress. Above is an attempt to recreate the situation. – kristian mo Jun 12 '15 at 14:31
  • Why don't you use synchronous operations for everything and run the call in a thread or task? – usr Jun 12 '15 at 14:42
  • @kristianmo: Just an observation that you're using the term "asynchronous" to mean three different things: "report progress for a long-running operation", "run on a background thread", and "implement a (presumably-)asynchronous operation". Each of these is really quite different; you should consider each independently and decide what you really need to do. – Stephen Cleary Jun 12 '15 at 16:37

0 Answers0