I have a Web API controller. It calls a method that returns an IAsyncResult. When I call the controller, I get the error
An asynchronous module or handler completed while an asynchronous operation was still pending.
How do I get the controller to wait for the asyncresult?
I was planning to use await, but I may just not have figured out the syntax for this use case.
I haven't found an existing answer on SO.
I'm using c# 4.5
[HttpGet]
[Route("GetGridDataAsync")]
public string GetGridDataAsync()
{
var proxy = new Proxy();
return proxy.BeginGetDataAsync("test", ar => proxy.EndGetDataAsync(ar));
}
public IAsyncResult BeginGetDataAsync(string r, AsyncCallback callback){}
public DataResponse[] EndGetDataAsync(IAsyncResult asyncResult){}
You can make your method an async Task<string>, create a Task based on the Async methods in the Proxy class and await that
Example:
public async Task<string> GetGridDataAsync()
{
var proxy = new Proxy();
return await Task.Factory.FromAsync(proxy.BeginGetDataAsync, proxy.EndGetDataAsync, "test", null);
}
Related
I want to use CancellationToken in one of async tasks in my WCF service. the implementation is as follows:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Reentrant)]
public partial class MyService: IMyService
{
private void Initialize()
{
_serviceHost = new ServiceHost(this, new Uri[] { new Uri("net.pipe://localhost") });
var binding = new NetNamedPipeBinding();
_serviceHost.AddServiceEndpoint(typeof(IMyService), binding, "MyService");
var behavior = _serviceHost.Description.Behaviors.Find<ServiceBehaviorAttribute>();
behavior.InstanceContextMode = InstanceContextMode.Single;
_serviceHost.Open(); // I get error on this line
}
public async Task<List<object>> DoSomeWorkAsync(CancellationToken ct = default(CancellationToken))
{
//do some work
...
var list = await SomeAwaitableTask(ct);
return list;
}
}
the WCF interface:
[ServiceContract(CallbackContract = typeof(IClientCallback))]
public interface IMyService
{
[OperationContract]
[FaultContract(typeof(ServiceExceptionDTO))]
Task<List<object>> DoSomeWorkAsync(CancellationToken ct = default(CancellationToken));
}
I get this error :
System.NotSupportedException: 'The use of 'System.Threading.CancellationToken' on the task-based asynchronous method is not supported.'
what should I do and how can I use cancellation token inside my async task?
There is no way to cross service boundaries and cancel on the server side.You can do this using WCF client-side and task-based async patterns by registering an Abort action with a cancellation token
Or use the extension method WithCancellation of Microsoft.VisualStudio.Threading.ThreadingTools.
For details, see the link below:
How to cancel an async WCF call?
What's the best way to cancel an asynchronous WCF request?
How to use the CancellationToken property?
I am having web api, which is non async.
From that web api, i am calling method
SmtpClient.SendMailAsync(email);
This email get sent to respective person, but the next web api request get fails.
public class TestController : ApiController
{
public TestController(TestService testService)
{
_testService = testService;
}
public IHttpActionResult Post(data)
{
_testService.SendEmail(data);
}
}
public class TestService
{
public async Task SendEmail(MailMessage email)
{
SmtpClient client = new SmtpClient();
client.SendMailAsync(email)
}
}
From the days before the async/await pattern was introduced, there are still many non-asychronous functions around in Framework classes.
SmtpClient client is one of the classes old enough for this. The SendFunction are the droids you are look for:
https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient.send
While the naming is a bit off and they return void, those seems to be the pre-async functions. Failure should be communicated via Exceptions in both cases.
Given your comment that you want to have SendEmail behave like fire & forget, I would propose using
Task.Run(() => _testService.SendEmail(data));
This will give the unit of work to the threadpool and free your request from the duty of waiting for this task. Generelly this is advised for fire & forget.
As a rule of thumb otherwise, it's generally a bad idea to call asynchronous things from a synchronous context. Do async all the way, or be prepared for deadlocking. For example, you could simply make your controller actions asynchronous as well.
If you will need use some result of sending:
public async IHttpActionResult Post(data)
{
var t = Task.Run(() =>_testService.SendEmail(data));
var result = await t;
// do something with result
}
- but of course your SendEmail function should return Task<> instead of Task ...
I have a syncronized method and class which is static. I have to call Mandrill and use the result.
My method, looks like this
public static void MyMethod(Association association , Person p)
{
MandrillApi mandrill = new MandrillApi(mandrillAPIKey);
var mail = GetMandrillMessage(fromEmail, clubAdmin.ProfileInfo.Email);
mail.AddGlobalVariable("Key", value);
mail.AddGlobalVariable("Key", value);
mail.AddGlobalVariable("AssociationBaseUrl", SettingsHelper.GetSetting("AssociationBaseUrl"));
mail.AddGlobalVariable("Key", value);
mail.AddGlobalVariable("UserFirstname", clubAdmin.Firstname);
mail.Subject = "Subject goes here";
var messageRequest = new SendMessageTemplateRequest(mail, "template");
var result = mandrill.SendMessageTemplate(messageRequest);
}
I need result.Result. I can't make my method to async.
So is there any way to get result.Result and use it in a new method?
i was thinking to do something like
var messageRequest = new SendMessageTemplateRequest(mail, "template");
var result = mandrill.SendMessageTemplate(messageRequest);
CallMyMethod(result.Result.First().Id)
but this wont work, it will just stop the program. Really need help. Ask for more information. I am really bad at explaining myself.
You are experiencing a deadlock. The correct wait to call asynchronous code from synchronous code is to make your whole call stack (starting at the controller's action method) asynchronous. IMO not doing this is an unreasonable constraint.
Changes:
Make MyMethod return Task instead of void so it can be awaited
Add async to MyMethod
Add await to wait for the result from IMandrilApi.SendMessageTemplate
Rename MyMethod to MyMethodAsync, not required but it follows a commonly accepted naming convention
Make controller method async and return a Task. If the controller is returning something then return Task<T> where T is the current return type.
Controller method awaits the result of MyMethodAsync
public static async Task MyMethodAsync(Association association , Person p)
{
/* your existing code has been removed for brevity as there are no changes in that code */
var result = await mandrill.SendMessageTemplate(messageRequest);
}
Controller method
I had to make an assumption this code but you did state in the comments the call stack is controller → MyMethod. Also note that only the relevant part to the question is shown and I had no other info to go on.
[HttpPost]
public async Task SendMessage()
{
await MyMethodAsync(association, person);
}
You can put your method to a Task and await it
public static object MyMethod(Association association , Person p)
{
MandrillApi mandrill = new MandrillApi(mandrillAPIKey);
var mail = GetMandrillMessage(fromEmail, clubAdmin.ProfileInfo.Email);
mail.AddGlobalVariable("Key", value);
mail.AddGlobalVariable("Key", value);
mail.AddGlobalVariable("AssociationBaseUrl", SettingsHelper.GetSetting("AssociationBaseUrl"));
mail.AddGlobalVariable("Key", value);
mail.AddGlobalVariable("UserFirstname", clubAdmin.Firstname);
mail.Subject = "Subject goes here";
var messageRequest = new SendMessageTemplateRequest(mail, "template");
var result = mandrill.SendMessageTemplate(messageRequest);
return result;
}
[HttpPost]
public async Task<HttpResponseMessage> Post([FromBody]MyObject obj)
{
var task = Task.Run(() => MyMethod(a,p));
var result = await task;
return Request.CreateResponse(...);
}
But async in the controller does not make the user experience async. User will have to wait as long as task finishes. So why do we do async ?
I am trying to call a webapi method from my quartz.net schedule job. I am not sure whether the way I am doing is right? Can anyone help if this is the right way or is there any better approach available?
MethodRepository.cs
public async Task<IEnumerable<ResultClass>> GetResult(string queryCriteria)
{
return await _httpClient.Get(queryCriteria);
}
Quartz job:
public async void Execute(IJobExecutionContext context)
{
var results= await _repo.GetResult();
}
generic Httpclient :
public async Task<IEnumerable<T>> Get(string queryCriteria)
{
_addressSuffix = _addressSuffix + queryCriteria;
var responseMessage = await _httpClient.GetAsync(_addressSuffix);
responseMessage.EnsureSuccessStatusCode();
return await responseMessage.Content.ReadAsAsync<IEnumerable<T>>();
}
But the quartz documentation says I can't use async method in a quartz job. How can one the Web API method then?
Can I change the quartz job execute method as:
public void Execute(IJobExecutionContext context)
{
var result = _repo.GetResult().Result;
}
Quartz.NET 3.0 supports async/await out of the box. So you can (and must) now declare Execute method as Task returning and you can use async/await.
public async Task Execute(IJobExecutionContext context)
{
var result = await _repo.GetResult();
}
If you have to do it - then yes you can do that, but it will block the calling thread until the asynchronous operation is complete.
Task.Result will wrap any exception into an AggregateException.
So you should probably put your httpclient call in a try catch.
try
{
var result = _repo.GetResult().Result;
}
catch (AggregateException ae)
{
// handle exception
}
Also, it seems they are working on an AsyncJob.
While i call the web api, asyn method start process. After completed API process then asyn process also stopped. Here is my sample code snippet. Please give any solution for this.
private readonly ITestService testservice;
public TestController(ITestService test)
{
this.testservice=test;
}
[Route("Sample/SaveMyData")]
[HttpPost]
public IHttpActionResult SaveMyData(MyInfo info)
{
MyInfo inf = new MyInfo ();
inf = testservice.SaveMyInformation(info);
SendMailProcess(inf)
return Ok<MyInfo>(inf);
}
private async Task<bool> SendMailProcess(MyInfo emailInfo)
{
await Task.Run(()=> this.testservice.SendMail(emailInfo));
return true;
}
#Eraiarasu, you are creating a separate Thread in your SendMailProcess and thats why your API call completes before the SendMailProcess completes.
Also, If you are trying to create async methods then please try to put await on SendMailProcess method invocation in SaveMyData as well. Also make SaveMyData as async too. Note that, you have SendAsync method in smtp api in .NET. So you don't have to use Task.Run as you did in SendMailProcess