I'm trying to call asynchronously a method on a WCF which has a single instanceContextMode.
Is there a way reuse an instance of a service while it is waiting for an asynchronous method ? I use the Task way for generating async operations on my WCF service reference.
I made a testing project because I had some problems on my application. My TestService expose 2 methods :
For some other reasons, my service should be in Single instanceContextMode:
[ServiceContract]
public interface ITestService
{
[OperationContract]
string FastMethod(string name);
[OperationContract]
Task<string> LongMethodAsync(string name);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class TestService : ITestService
{
public TestService() { }
public string FastMethod(string name)
{
Console.WriteLine($"{DateTime.Now.ToLongTimeString()} - FastMethod call - {name}");
return $"FastMethod - {name}";
}
public async Task<string> LongMethodAsync(string name)
{
for (int i = 5; i > 0; i--)
{
await Task.Delay(1000);
Console.WriteLine($"LongMethod pending {i}");
}
Console.WriteLine($"{DateTime.Now.ToLongTimeString()} - LongMethod call - {name}");
return $"LongMethod - {name}";
}
}
My host is a simple console application which allow me to see WS calls through Console.WriteLine() methods:
class Program
{
static void Main(string[] args)
{
using (ServiceHost hostTest = new ServiceHost(typeof(TestService)))
{
Console.WriteLine($"{DateTime.Now.ToLongTimeString()} - Service starting...");
hostTest.Open();
Console.WriteLine($"{DateTime.Now.ToLongTimeString()} - Service started");
Console.ReadKey();
hostTest.Close();
}
}
}
On my client side, I only have a simple form which show result calls:
private async void button1_Click(object sender, EventArgs e)
{
string result;
result = srvClient.FastMethod("test1");
resultTextBox.Text = $"{DateTime.Now.ToLongTimeString()} - {result}";
Task<string> t1 = srvClient.LongMethodAsync("test2");
result = srvClient.FastMethod("test3");
resultTextBox.Text += $"\r\n{DateTime.Now.ToLongTimeString()} - {result}";
System.Threading.Thread.Sleep(1000);
result = srvClient.FastMethod("test4");
resultTextBox.Text += $"\r\n{DateTime.Now.ToLongTimeString()} - {result}";
result = await t1;
resultTextBox.Text += $"\r\n{DateTime.Now.ToLongTimeString()} - {result}";
}
When I do that I can see in my resultTestBox
and in the host Console that "test3" and "test4" are called only after the end of "test2".
If I do the same test locally (not through a WCF service) the behavoir is like expected, the "test3" and "test4" are called while "test2" is waiting.
If the InstanceContextMode value is set to Single the result is that your service can only process one message at a time unless you also set the ConcurrencyMode value to ConcurrencyMode.
(looks like they forgot to tell what ConcurrencyMode)
So just set the right ConcurrencyMode
on your service:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
Make sure your code is stateless and thread-safe though. This combination is very error prone.