Calling Async Methods from a Wcf Service

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



Leave a reply



Submit