Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing async/await pattern for manually generated WCF (client-side) proxies

Given this interface

[ServiceContract]
public interface IProductService
{
    [OperationContract]
    Product Get(int id);
}

I would like to manually (that is, without using scvutil or Add Service Reference in VS) create a client-side proxy.

I do it in the following way

public class ProductService: IProductService
{
    readonly ChannelFactory<IProductService> factory;

    public ProductService()
    {
        factory = new ChannelFactory<IProductService>("*");
    }

    public Product Get(int id)
    {
        var channel = factory.CreateChannel();
        return channel.Get(id);
    }
}

My problem is that I also want async/await version of this method, only on client-side, server side is still synchronous.

I want this to be a generic solution because I have many methods and services of this sort.

like image 499
Elad Avatar asked Mar 29 '16 07:03

Elad


2 Answers

If you're using ChannelFactory to allow for async-await your interface needs to return a Task or Task<T>.

It will force your server side to also return a task but you can do that synchronously with Task.CompletedTask and Task.FromResult if you insist on keeping it synchronous (though why would you if you have the option).

For example:

[ServiceContract]
interface IProductService
{
    [OperationContract]
    Task<Product> GetAsync(int id);
}

class ProductService : IProductService
{
    ChannelFactory<IProductService> factory;

    public ProductService()
    {
        factory = new ChannelFactory<IProductService>("*");
    }

    public Task<Product> GetAsync(int id)
    {
        var channel = factory.CreateChannel();
        return channel.GetAsync(id);
    }
}

class ProductAPI : IProductService
{
    public Task<Product> GetAsync(int id) => Task.FromResult(Get(id))
}
like image 122
i3arnon Avatar answered Oct 18 '22 11:10

i3arnon


You can actually do that without changing the service itself. You can simply define a second interface which contains async and Task returning versions of the methods and is marked with [ServiceContract(Name = "NameOfTheIterfaceWhichIsActuallyExposedOnTheServer")]

In the example you mentioned you would define a second interface with GetAsync() operation:

[ServiceContract(Name = "IProductService")]
public interface IProductServiceAsync
{
    [OperationContract]
    Task<Product> GetAsync(int id);
}

and even though your service still implements and exposes IProductService you can use ChannelFactory<IProductServiceAsync> to call into it. As long as the method names match the GetFoo/GetFooAsync pattern everything will just work. That's how Add Service Reference in Visual Studio can generate you an async service reference to a synchronous service.

See Calling a synchronous WCF method asynchronously using ChannelFactory for a more detailed explanation on how this work.

like image 39
MarcinJuraszek Avatar answered Oct 18 '22 12:10

MarcinJuraszek