Call asynchronous method on a WCF service with Single InstanceContextMode
According to MSDN:
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.
WCF service call async function
With async/await you need to make it async all the way through.
Make the service a Task-based asynchronous service
[ServiceContract]
public interface IService1 {
[OperationContract]
Task<string> GetData();
}
With that, it is a simple matter of making the rest of the code async all the way through.
public class http {
public async Task<string> HttpRequestAsync() {
var request = GetHttpRequestMessage();
string str1 = await ExecuteRequest(request);
Console.WriteLine(str1);
return str1;
}
//...code removed for brevity as they are already Task-based
}
This should now allow the function to be used in the service implementation
public class Service1 : IService1 {
public Task<string> GetData() {
http test = new http();
return test.HttpRequestAsync();
}
}
In the original example provided the code was mixing async and blocking calls .Result
, which can lead to deadlocks
Reference Async/Await - Best Practices in Asynchronous Programming
I would also advise making the HttpClient
static and reuse it rather than creating multiple instances and disposing of them.
Reference keep an instance of HttpClient for the lifetime of your application for each distinct API that you connect to.
UPDATE:
Another possibility is that the URL being called is HTTPS.
Consider applying the following before making the request via the HttpClient
//Handle TLS protocols
System.Net.ServicePointManager.SecurityProtocol =
System.Net.SecurityProtocolType.Tls
| System.Net.SecurityProtocolType.Tls11
| System.Net.SecurityProtocolType.Tls12;
How to call wcf service Asynchronously
I think the best way is to convert the APM pattern into the Task pattern, using Task.Factory.FromAsync
:
public static class WcfExt
{
public static Task<int> AddAsync(this IAddTwoNumbers service, int a, int b)
{
return Task.Factory.FromAsync(
(asyncCallback, asyncState) =>
service.BeginAdd(a, b, asyncCallback, asyncState),
(asyncResult) =>
service.EndAdd(asyncResult), null);
}
}
Usage:
IAddTwoNumbers service = CreateWcfClientProxy();
int result = await service.AddAsync(a, b);
Pattern for calling WCF service using async/await
I think a feasible solution might be to use a custom awaiter to flow the new operation context via OperationContext.Current
. The implementation of OperationContext
itself doesn't appear to require thread affinity. Here is the pattern:
async Task TestAsync()
{
using(var client = new WcfAPM.ServiceClient())
using (var scope = new FlowingOperationContextScope(client.InnerChannel))
{
await client.SomeMethodAsync(1).ContinueOnScope(scope);
await client.AnotherMethodAsync(2).ContinueOnScope(scope);
}
}
Here is the implementation of FlowingOperationContextScope
and ContinueOnScope
(only slightly tested):
public sealed class FlowingOperationContextScope : IDisposable
{
bool _inflight = false;
bool _disposed;
OperationContext _thisContext = null;
OperationContext _originalContext = null;
public FlowingOperationContextScope(IContextChannel channel):
this(new OperationContext(channel))
{
}
public FlowingOperationContextScope(OperationContext context)
{
_originalContext = OperationContext.Current;
OperationContext.Current = _thisContext = context;
}
public void Dispose()
{
if (!_disposed)
{
if (_inflight || OperationContext.Current != _thisContext)
throw new InvalidOperationException();
_disposed = true;
OperationContext.Current = _originalContext;
_thisContext = null;
_originalContext = null;
}
}
internal void BeforeAwait()
{
if (_inflight)
return;
_inflight = true;
// leave _thisContext as the current context
}
internal void AfterAwait()
{
if (!_inflight)
throw new InvalidOperationException();
_inflight = false;
// ignore the current context, restore _thisContext
OperationContext.Current = _thisContext;
}
}
// ContinueOnScope extension
public static class TaskExt
{
public static SimpleAwaiter<TResult> ContinueOnScope<TResult>(this Task<TResult> @this, FlowingOperationContextScope scope)
{
return new SimpleAwaiter<TResult>(@this, scope.BeforeAwait, scope.AfterAwait);
}
// awaiter
public class SimpleAwaiter<TResult> :
System.Runtime.CompilerServices.INotifyCompletion
{
readonly Task<TResult> _task;
readonly Action _beforeAwait;
readonly Action _afterAwait;
public SimpleAwaiter(Task<TResult> task, Action beforeAwait, Action afterAwait)
{
_task = task;
_beforeAwait = beforeAwait;
_afterAwait = afterAwait;
}
public SimpleAwaiter<TResult> GetAwaiter()
{
return this;
}
public bool IsCompleted
{
get
{
// don't do anything if the task completed synchronously
// (we're on the same thread)
if (_task.IsCompleted)
return true;
_beforeAwait();
return false;
}
}
public TResult GetResult()
{
return _task.Result;
}
// INotifyCompletion
public void OnCompleted(Action continuation)
{
_task.ContinueWith(task =>
{
_afterAwait();
continuation();
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
SynchronizationContext.Current != null ?
TaskScheduler.FromCurrentSynchronizationContext() :
TaskScheduler.Current);
}
}
}
How to filter out WCF Async method from Services .NET
There is no async in the wire protocol. Client and server cannot tell whether the remote side is implemented in an async or sync way. You only need one at each side and they can be different.
Implement the server only once. Don't have two methods or two interfaces.
On the client you can freely pick between sync and async methods. You can disable async methods in the service reference settings.
I have explained this here.
calling WCF method asynchronously
CallMethodAsync
is correct.
If you don't await (or Wait) resultFromMethodCall
execution will continue while that task is still running. Whether you should allow that depends on what you want to happen.
How to make a call to my WCF service asynchronous?
All your needs will be satisfied in the following articles from MSDN:
Implementing an Async Service Operation
Calling WCF Service Async
Designing Service Contracts
Related Topics
How Enumerate All Classes with Custom Class Attribute
How to Include External Font in Wpf Application Without Installing It
C# Short/Long/Int Literal Format
How to Run a Test Method with Multiple Parameters in Mstest
Getting the Thread Id from a Thread
How to Inject a Dbcontext Instance into an Ihostedservice
The Type or Namespace Name Does Not Exist in the Namespace 'System.Web.Mvc'
Pass Array to MVC Action via Ajax
Xml Serialize Generic List of Serializable Objects
How to Resolve Ioptions Instance Inside Configureservices
What Does Missingmanifestresourceexception Mean and How to Fix It
What Does {0} Mean When Found in a String in C#
How to Determine a Mapped Drive's Actual Path
Detect Antivirus on Windows Using C#
Escape Invalid Xml Characters in C#