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.