Is There Some Way to Handle Async/Await Behind an Asmx Service

How to call an async function inside web-service method in c#

This code works...... ####

 [WebMethod]
public static void DisplayWord(int id)
{

SpellingBee_spellbyte Sg = new SpellingBee_spellbyte();//pageclassname
Task.Run(async () => await Sg.getstartWord(id));

}

protected async Task getstartWord(int tagId)
{
}

Async/await call to asmx service

What you have is a Event-Based Asynchronous Pattern (EAP) and what you want to do is wrap that up in a task so you can await it. You do this via a TaskCompletionSource.

public async Task<List<ObjectToReturn>> GetDataFromAsmxServiceAsync(GetReportDataRequest somedata)
{
var tcs = new TaskCompletionSource<GetReportDataResponse>();
_asmxService.GetReportDataCompleted += GetReportDataCallBack;
_asmxService.GetReportDataAsync(somedata, tcs); //we pass tcs in so it can be used from the callback.
GetReportDataResponse res;
try
{
res = await tcs.Task;
}
finally
{
//unsubscribe from the handler when done so we don't get a leak.
_asmxService.GetReportDataCompleted -= GetReportDataCallBack;
}

var result = process(res);
return result;
}

private void GetReportDataCallBack(object sender, GetReportDataCompletedEventArgs e)
{
var tcs = (TaskCompletionSource<GetReportDataResponse>)e.UserState;
if (e.Cancelled)
{
tcs.TrySetCanceled();
}
else if (e.Error != null)
{
tcs.TrySetException(e.Error);
}
else
{
tcs.TrySetResult(e.Result);
}
}

You will also need to change your controller to be async too

public Task<ActionResult> IndexAsync()
{
var data = await _repository.GetDataFromAsmxServiceAsync(somedata);
}

Here is a good article explaining how to convert from synchronous MVC code to async MVC code and explains why even though Index is now called IndexAsync it will still work as your controller for Index.

Calling Task-based methods from ASMX

I've recently been updating an ASP.NET 2.0 project that includes a legacy ASMX web service to ASP.NET 4.5.

The first thing to do is ensure that httpRuntime@targetFramework is set to 4.5 in your web.config.

the parent task (i.e. the method call in the ASMX that returned a Task) was never detected as completing.

This is actually a classic deadlock situation. I describe it in full on my blog, but the gist of it is that await will (by default) capture a "context" and use that to resume the async method. In this case, that "context" is an ASP.NET request context, which only allows one thread at a time. So, when the asmx code further up the stack blocks on the task (via WaitAll), it is blocking a thread in that request context, and the async method cannot complete.

Pushing the blocking wait to a background thread would "work", but as you note it is a bit brute-force. A minor improvement would be to just use var result = Task.Run(() => MethodAsync()).Result;, which queues the background work to the thread pool and then blocks the request thread waiting for it to complete. Alternatively, you may have the option of using ConfigureAwait(false) for every await, which overrides the default "context" behavior and allows the async method to continue on a thread pool thread outside the request context.


But a much better improvement would be to use asynchronous calls "all the way". (Side note: I describe this in more detail in an MSDN article on async best practices).

ASMX does allow asynchronous implementations of the APM variety. I recommend that you first make your asmx implementation code as asynchronous as possible (i.e., using await WhenAll rather than WaitAll). You'll end up with a "core" method that you then need to wrap in an APM API.

The wrapper would look something like this:

// Core async method containing all logic.
private Task<string> FooAsync(int arg);

// Original (synchronous) method looked like this:
// [WebMethod]
// public string Foo(int arg);

[WebMethod]
public IAsyncResult BeginFoo(int arg, AsyncCallback callback, object state)
{
var tcs = new TaskCompletionSource<string>(state);
var task = FooAsync(arg);
task.ContinueWith(t =>
{
if (t.IsFaulted)
tcs.TrySetException(t.Exception.InnerExceptions);
else if (t.IsCanceled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(t.Result);

if (callback != null)
callback(tcs.Task);
});

return tcs.Task;
}

[WebMethod]
public string EndFoo(IAsyncResult result)
{
return ((Task<string>)result).GetAwaiter().GetResult();
}

This gets a bit tedious if you have a lot of methods to wrap, so I wrote some ToBegin and ToEnd methods as part of my AsyncEx library. Using these methods (or your own copy of them if you don't want the library dependency), the wrappers simplify nicely:

[WebMethod]
public IAsyncResult BeginFoo(int arg, AsyncCallback callback, object state)
{
return AsyncFactory<string>.ToBegin(FooAsync(arg), callback, state);
}

[WebMethod]
public string EndFoo(IAsyncResult result)
{
return AsyncFactory<string>.ToEnd(result);
}

Does async calling of web service make sense on server?

You are right in that using async IO does "save" one thread while it is running. This is the main advantage of async IO on the server. I have written about the pros and cons of async IO before. (Also: https://stackoverflow.com/a/12796711/122718).

What should you do? If that web-service tends to respond slowly or has a risk of sometimes responding slowly async is quite attractive. Image 10 requests coming in per second and the web service having 10s latency (maybe due to a performance problem). Then, you need 100 threads just to serve that load.

On the other hand, if you don't fear any of this, async will give you nothing. You save a few MB of stack memory which you likely don't need. You also burn more CPU with async. You become less productive (especially because you now need to make all callers of this method async as well. Async is viral.).

Call async .NET 4.5 web service from jquery

According to comments on Is it possible to use async/await in webmethod asmx service, ASMX simply doesn't support async. That's why your code doesn't work. And it has nothing to do with jQuery being asynchronous too.



Related Topics



Leave a reply



Submit