24

I have a web app serving a WCF REST API for JSON and an ASMX web service. The application has been around for a few years. It's based on ASP.NET 2.0, but upgraded to .NET 4.0 a couple of years ago, and I just upgraded to .NET 4.5 to be able to use the new async framework.

Behind the application are some legacy services, and I realized that there is a big potential for increasing the performance by going async. I have implemented async all the way through the application, and everything is working perfectly through the WCF REST API.

Too late I discovered that the ASMX API fails, I wanted methods like this:

[WebMethod(Description = "Takes an internal trip ID as parameter.")]
async public Task<Trip> GetTrip(int tripid)
{
    var t = await Trip.GetTrip(tripid);
    return t;
}

I then learned that async/await isn't supported in ASMX at all, and everybody advises to migrate to WCF. I am not too joyful about this. The ASMX (actually three of them) are stuffed with different methods, and there are loads of API consumers that we want to keep serving from the old API.

But we need the increased performance! Does anybody know about a workaround so I can keep using async/await behind the ASMX, but expose the ASMX as before?

Barslett
  • 286
  • 1
  • 2
  • 5
  • 1
    You understand that async is only faster under special circumstances? Might well be slower. Research this first. – usr Sep 04 '13 at 20:03
  • 1
    possible duplicate of [Calling Task-based methods from ASMX](http://stackoverflow.com/questions/24078621/calling-task-based-methods-from-asmx) – Paddy Aug 14 '15 at 14:23

3 Answers3

25

It may be possible to do this, but it would be a bit awkward. ASMX supports APM-style asynchronous methods, and you can convert TAP to APM (however, note that the MSDN example on that page does not propagate exceptions correctly).

I have an example on my blog that shows how to wrap TAP implementations in APM (with exception propagation that keeps the correct exception type but loses the stack; see ExceptionDispatchInfo for fully correct exception propagation). I used this for a while when WCF only supported APM. A very similar approach should work for ASMX.

However, note that you will have to target 4.5 (i.e., httpRuntime.targetFramework) for async/await to work as expected.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Hi Stephen, Will wrapping an EAP into Task will be a fake asynchrony? e.g. as you suggested [here](https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/interop-with-other-asynchronous-patterns-and-types?redirectedfrom=MSDN#EAP) – Haris Munawar Oct 14 '19 at 07:24
  • @HarisMunawar: No. "Fake asynchrony" is blocking a thread pool thread in order to provide an asynchronous API. EAP is asynchronous, so there's no thread pool thread blocked. – Stephen Cleary Oct 14 '19 at 11:22
0

You might not be able to make the ASMX service itself behave in an async way, but if you are calling from a WebApp using javascript you can wrap a promise around the service when calling in order to take advantage of async within the client-side.

As stated, it does not make your service async, but is an example of using this service in an async way within the client (ie, a javascript promise is used in this instance to halt further operation until the service has resolved. Similarly it can be adjusted to continue operation and perform other actions once resolved or failed).

Optimising the service itself is only half of the solution, the other half is within the client, so I thought this would be a helpful expansion to some :)

Javascript Code:

var tripObj;

async function getTripObject(tripId) {


  // Ensures Trip is fetched before proceeding 
  var promise = new Promise(function (resolve, reject) {
    ASMXService.getTrip(tripId, async function (ret) {
      tripObj = ret;

      resolve("Success");
    }, function () {
      reject("Error");
    });
  });

  await promise;

}
Radderz
  • 2,770
  • 4
  • 28
  • 40
-2

If your problem is simply integrating the top of your async logic in your asmx, you can do like the following snippet.

[WebMethod(Description = "Takes an internal trip ID as parameter.")] 
public Trip GetTrip(int tripid) {
   var trip = Trip.GetTrip(tripid).Wait();
   return trip; 
}

Be aware that if Trip.GetTrip() throws an exception, you will receive an AggregateException, and not the exception you would receive if it was await throwing the exception. You can do

[WebMethod(Description = "Takes an internal trip ID as parameter.")] 
public Trip GetTrip(int tripid) {
  try
  {
    var trip = Trip.GetTrip(tripid).Wait();
    return trip; 
  }
  catch(AggregateException ex)
  {
    throw ex.InnerException.First();
  }       
}
guiomie
  • 4,988
  • 6
  • 37
  • 67