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
Put Wpf Control into a Windows Forms Form
Serialize Property, But Do Not Deserialize Property in JSON.Net
How to Select Xml Nodes with Xml Namespaces from an Xmldocument
Force JSON.Net to Include Milliseconds When Serializing Datetime (Even If Ms Component Is Zero)
The Calling Thread Must Be Sta, Because Many UI Components Require This in Wpf
Shortcut for "Null If Object Is Null, or Object.Member If Object Is Not Null"
Does Distinct() Method Keep Original Ordering of Sequence Intact
How to Make a Property Protected and Internal in C#
Additional Text Encountered After Finished Reading JSON Content:
Meanings of Declaring, Instantiating, Initializing and Assigning an Object
Possible to Iterate Backwards Through a Foreach
Struct Constructor: "Fields Must Be Fully Assigned Before Control Is Returned to the Caller."
How to Generate a Cryptographically Secure Pseudorandom Number in C#