3

Suppose we are writing a client for a Calculator web service using WCF's ChannelFactory. The service contract is shared by way of a third assembly referenced by both the implementation service and the client. Below is the service contract (which cannot be changed!)

public interface ICalculator 
{
    int Add(int x, int y);
}

The ChannelFactory creates a transparent proxy object that "mocks" the ICalculator service contract, passing method calls to a RealProxy object which then sends the message down the WCF channel stack. Is there a way to manipulate WCF (on the client side ONLY!) to auto-expose Task-friendly service operations, similar to VS-auto-generated service proxies?

To be clear, I'm not looking to modify my service to be async-friendly in anyway. I want my client to proceed processing while waiting for ordinarily blocking service calls to complete.

Martin Bliss
  • 1,043
  • 7
  • 24

2 Answers2

2
public interface ICalculator 
{
    Task<int> Add(int x, int y);
}

Use this as the contract. However you need to make sure you are running .net 4.5 for this to work.

Remember on the server side to use this pattern

public class Calculator : ICalulator
{
    public Task<int> Add(int x, int y)
    {
        int result = x + y;
        return Task.FromResult(result);
    }
}
Aron
  • 15,464
  • 3
  • 31
  • 64
  • Please re-read my post where I mention that I CANNOT change the contract, nor would I want to. I don't want to expose Task when it's unnecessary. I should be able to await ANY service operation regardless of its method signature as it is an I/O operation much like streaming. – Martin Bliss Apr 19 '14 at 03:21
  • For proof that a client can always await any service call, see VS' auto-gen service proxy implementation. I basically want that, but using ChannelFactory. – Martin Bliss Apr 19 '14 at 03:22
  • You need to "expose" a Task on the client side because you need to have async at the point of calling, to ensure threadless async (otherwise you only get concurrency async). I demonstrated how you achieve synchronous invocation on the server side. If you want to achieve this without changing the service contract I would advise you to use Service References to create your interface, to reduce the manual steps. – Aron Apr 19 '14 at 05:37
  • See my comment response to your similar comment on the "answer" to this question/thread, as it applies to your latest comment here as well. – Martin Bliss Apr 19 '14 at 18:09
2

I did some exploring using ILSpy and found that both ChannelFactory and VS' service proxy code both leverage the same code underneath the covers to generate a transparent proxy for ICalculator. The only difference is that the auto-gen code created a custom interface that mirrored the service's contract, plus Task async methods, with OperationContract attributes that pointed back to the "real" service operation URIs.

[ServiceContract]
public interface ICalculatorClient
{
    [ServiceOperation(Action="http://tempuri.org/ICalculator/Add")]
    Task<int> AddAsync(int x, int y);
}

So, instead of using an assembly containing the original service's service contract, I created my own ICalculatorClient service contract in the client code (the name was just to eliminate ambiguity for this test) which featured the desired Task async method calls, only with the requisite ServiceOperation attributes with an Action property pointing to the URI for the "real" service operation on the server contract.

At runtime, the desired behavior was observed: My client code proceeded while waiting on a response from the server, which it eventually received as expected. The service is none the wiser, not having been modified to address client needs. The client is all the smarter, gaining control over the details of its interaction with a web service.

Martin Bliss
  • 1,043
  • 7
  • 24
  • 2
    In short you lose all the advantages of using a shared contract assembly rather than using Add Service Reference. – Aron Apr 19 '14 at 05:33
  • I disagree. There are a lot of disadvantages to service reference. There are other ways to regain the convenience of auto-gen code like T4 templates, without the restrictions imposed by proxies: See this MSDN article: http://blogs.msdn.com/b/juveriak/archive/2008/02/03/using-channels-vs-proxies-in-wcf.aspx – Martin Bliss Apr 19 '14 at 18:08
  • T4 does not work well with interfaces in cs files (try get CodeDom to understand partial), just broken with dll reflection (dll read lock stops you compiling). T4 with Roslyn, might work, but i have never used it. I never advised you to use proxies. I prefer channel factory. I only suggested you gen the contract with service reference. – Aron Apr 19 '14 at 18:19
  • Interesting suggestion, Aron. I never thought about running the code gen JUST to extract an interface, then using ChannelFactory. That might be the best of both worlds for a common practice. If you edit your answer to reflect this, I'd be more than happy to give you the "answer" mark. – Martin Bliss Apr 19 '14 at 18:43