6

I want to call asynchronous methods from a WCF service, something like:

[ServiceContract]
interface IService
{
    [OperationContract]
    int SomeMethod(int data);
}

int SomeMethod(int data)
{
    var query = ... build LINQ query;
    var response = await query.ToListAsync();
    return response.Length;
}

I don't want to add async to the IService interface or SomeMethod method. Using asynchronous methods is an internal issue that shouldn't be reflected in the interface.

How can I do that?

CLARIFICATION:

My problem here is using await in a non-async method. I don't want the service contract to change (the client doesn't necessarily know what async is), and I don't want to split the method into BeginSomeMethod and EndSomeMethod. I want one method that uses await internally.

zmbq
  • 38,013
  • 14
  • 101
  • 171

1 Answers1

19

Whether the server is using sync or async code does not matter for the client. Client and server are separated by a well-specified wire-protocol (often SOAP). SOAP has no notion of asynchronous completion.

You can have a sync server and an async client, or vice versa. The client cannot even detect whether the server is sync or async. This is an implementation detail. The server could be a wrist watch running Linux and you still couldn't tell.

The style of IO you use is an implementation detail and does not influence the bytes that go over the network.

So pick what you like. The client can still use async IO to access the server.

I'm not sure why this is such a surprise to people. In other contexts this seems very intuitive: You can have a asynchronous TCP server and a synchronous client. I can say new WebClient().DownloadString(url) and download a string synchronously from a web-server that is implemented in an asynchronous way. I cannot even tell what server software is running.

Use Fiddler to look at what goes over the wire when you make a WCF call. There is no notion of synchronous or asynchronous calls.

Under the hood, when you invoke a service asynchronously, the WCF client library using TCP sockets in an asynchronous way. When you invoke synchronously, TCP sockets are being used with blocking calls. That's the entire difference.

WCF generated clients can be made to have asynchronous methods in addition to the synchronous methods. Select the "Generate asynchronous operations" option in the UI. Now you have both versions. Both fully functional.

Here's how you can convince yourself of this with an experiment: Write a sync server, and call it both sync and async from the same .NET client. Now write a 2nd server asynchronously (in any style you like) and use the exact same client code to call it.

Task and IAsyncResult are not serializable over SOAP anyway so it cannot possibly be the case that a Task is transmitted to the client.

usr
  • 168,620
  • 35
  • 240
  • 369
  • 1
    I don't think this answers @zmbq's goal pointed in the [comment](http://stackoverflow.com/a/22590478/1768303): *"The method is a long executing method by definition. Clients who do not wish to block will need to implement their own asynchronous mechanism. I was hoping to use async in the server, regardless of the client (the client may be running .NET 4, for example, with no async/await support at all)."* I think my answer does :) – noseratio Mar 23 '14 at 13:45
  • He *can* use async on the server, regardless what the client does. The client can sync call an async server. No Task.Wait or similar required. Every generated WCF client both has method for sync and for async calls. Both work fine. – usr Mar 23 '14 at 13:49
  • Let me put it that way, if the API starts an async operation on the server (call it `SomeMethodStart`), how does it notify the client about its completion? There will be a client proxy `BeginSomeMethodStart` and `EndSomeMethodStart` methods for it, but calling `EndSomeMethodStart` on the client will return as soon as `SomeMethodStart` returns on the server, not by the end of the server-side async op. What he needs is to *correctly* expose the server-side *async* op to the client, and be able to use `async/await` on the server without blocking. The service needs to expose APM or TAP API. – noseratio Mar 23 '14 at 14:49
  • By analogy with your `WebClient` example, I may have an asynchronous ASP.NET Web API service (`ApiController` with an `async` method), which I want to call using `DownloadStringAsync`. – noseratio Mar 23 '14 at 15:18
  • 3
    When an (async) server completes processing, it sends the response. No response is sent after the BeginXXX method completes. Not a single byte. The response comes after the EndXXX method completes on the server. `calling EndSomeMethodStart on the client will return as soon as SomeMethodStart returns on the server` No - it returns when the response has arrived. The response arrives after the server has completed *all* processing. Try it. Observe with Fiddler. – usr Mar 23 '14 at 15:24
  • Regarding your Web-API example: What do you think happens when you call it with curl.exe? It will work just as correctly as with any other client. The response is sent and received when the server is done completely. It is *not possible* to separately call BeginXXX and not call EndXXX. If you tried it you'd see... – usr Mar 23 '14 at 15:26
  • Sorry if I'm too persistent, that's because I feel like I'm missing something fundamental here. I'll try it as soon as get to my laptop. So far, my point is simple: if the WCF service exposes APM or TAP API, I can have non-blocking code on *both* server and client sides, while the async op is *"in-flight"* on the server. At least, one blocked thread less on *each* side. Yet, this is not possible if the WCF service doesn't exposes either APM or TAP API. Is this point not correct, and why, if so? – noseratio Mar 23 '14 at 15:45
  • That is not correct and you'll why as soon as you try it. I'm running out of ways to explain this... I have made a few statements that would contradict your mental model of how it works. I suggest you try to disprove my statements. One of us will learn something new that way. – usr Mar 23 '14 at 15:50
  • @usr, I'll clarify in the question itself. – zmbq Mar 23 '14 at 22:00
  • @zmbq I do not know how to explain further. You want the client to be able to use a synchronous interface, yet you want the server implemented asynchronously, correct? Did you read anything I said? What did you not understand about it? Just write the service asynchronously (by changing the interface to use APM or TAP), and you don't have to change the client. *Please* respond specifically to this suggestion. What do you not understand about it? I get that it is counter-intuitive but I have explained why this is so. – usr Mar 23 '14 at 22:47
  • 3
    @usr, I've played with it now. I did learn something new and I stand corrected, thanks a lot. – noseratio Mar 24 '14 at 07:32