WCF Asynchronous Call with ChannelFactory and CreateChannel

I am working on a project where a web application hosted on a web server calls WCF services hosted on an application server. A proxy for WCF calls is created by ChannelFactory, and calls are made by channel, for example:

(excluding block use)

var factory = new ChannelFactory<IUserService>(endpointConfigurationName); var channel = factory.CreateChannel(); var users = channel.GetAllUsers(); 

If I understand that the call through the channel is asynchronous, and the thread on the web server is inactive during the request and just waiting for a response.

I would like to make an async call like this:

 var users = await channel.GetAllUsersAsync(); 

Is there a way to make a call with ChannelFactory and an asynchronous channel? I have not found. I know that I can generate asynchronous methods using the svcutil / Add service link, but I don't want to do this. Also, I do not want to change the service interface on the application server (IUserService) by adding async methods.

Is there a way to call async methods with ChannelFactory? Thanks.

+6
source share
3 answers

Unfortunately no, no.

Asynchronous methods that you get from svcutil are generated in the proxy server based on your interface. There is nothing in the original WCF channel.

The only way is to change the service link to have your own aync calls that you don't want, or create your own wrapper around the channel and implement them yourself, like a generated proxy.

+3
source

You can automatically generate a new interface containing asynchronous versions of methods from the source interface using T4 and use it in ChannelFactory without changing the server-side interface .

I used NRefactory to parse the original and generate new C # source code and AssemblyReferences.tt to use the nuget packages in the T4 template:

 <#@ template debug="false" hostspecific="true" language="C#" #> <#@ include file="AssemblyReferences.tt" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="ICSharpCode.NRefactory.CSharp" #> <#@ output extension=".cs"#> <# var file = System.IO.File.ReadAllText(this.Host.ResolvePath("IUserService.cs")); if(!file.Contains("using System.Threading.Tasks;")) { #> using System.Threading.Tasks; <# } #> <# CSharpParser parser = new CSharpParser(); var syntaxTree = parser.Parse(file); foreach (var namespaceDeclaration in syntaxTree.Descendants.OfType<NamespaceDeclaration>()) { namespaceDeclaration.Name += ".Client"; } foreach (var methodDeclaration in syntaxTree.Descendants.OfType<MethodDeclaration>()) { if (methodDeclaration.Name.Contains("Async")) continue; MethodDeclaration asyncMethod = methodDeclaration.Clone() as MethodDeclaration; asyncMethod.Name += "Async"; if (asyncMethod.ReturnType.ToString() == "void") asyncMethod.ReturnType = new SimpleType("Task"); else asyncMethod.ReturnType = new SimpleType("Task", typeArguments: asyncMethod.ReturnType.Clone()); methodDeclaration.Parent.AddChild(asyncMethod, Roles.TypeMemberRole); } #> <#=syntaxTree.ToString()#>โ€‹ 

You pass the interface file name to the template:

 using System.Collections.Generic; using System.ServiceModel; namespace MyProject { [ServiceContract] interface IUserService { [OperationContract] List<User> GetAllUsers(); } } 

To get a new one:

 using System.Threading.Tasks; using System.Collections.Generic; using System.ServiceModel; namespace MyProject.Client { [ServiceContract] interface IUserService { [OperationContract] List<User> GetAllUsers (); [OperationContract] Task<List<User>> GetAllUsersAsync (); } } 

Now you can put it in a factory to use the channel asynchronously:

 var factory = new ChannelFactory<MyProject.Client.IUserService>("*"); var channel = factory.CreateChannel(); var users = await channel.GetAllUsersAsync(); 
+6
source

Unfortunately, this is not possible, and there is a good reason for this. CreateChannel returns an object that implements the provided interface ( IUserService in your example). This interface is not asynchronous, so there is no way to return an object with the correct methods.

There are two possible solutions:

  • Create your own proxy capable of invoking the WCF service. This means that you need to write your own proxy (or let svcutil do it for you).
  • Make sure IUserService is an asynchronous interface that returns tasks. This is supported in WCF 4.5 and later. This is what I often use. The main drawback is that it makes your service a bit complicated, and you need to call async methods (which can also be considered an advantage).
+4
source

All Articles