I have code in ASP.net that sends an email. My application is older using ASP.net Webforms and is not converted over to async methods.
However the method for sending email via SendGrid is async awaitable.
Visual Studio doesn't complain if I use a discard:
_ = SendASendGridMessage()
Would this cause any crash or deadlock if I do this?
Here is the sample code:
public static void SendNew(string toEmail, string subject, string htmlContent, int domainId, int partyId, string category, int itemId)
{
EmailAddress from = new EmailAddress("example#example.com");
EmailAddress to = new EmailAddress(toEmail);
EmailAddress replyTo = new EmailAddress("example#example.com");
htmlContent = $"<html><body>{htmlContent}</body></html>";
var msg = MailHelper.CreateSingleEmail(from, to, subject, null, htmlContent);
_ = SendASendGridMessage(msg, domainId);
}
// Which will then connect with this method later:
// Summary:
// Make a request to send an email through Twilio SendGrid asynchronously.
//
// Parameters:
// msg:
// A SendGridMessage object with the details for the request.
//
// cancellationToken:
// Cancel the asynchronous call.
//
// Returns:
// A Response object.
[AsyncStateMachine(typeof(<SendEmailAsync>d__23))]
public Task<Response> SendEmailAsync(SendGridMessage msg, CancellationToken cancellationToken = default);
You can use "Fire and Forget" approach and just call an async method without await just like you did. In fact, it is a good practice to detach time-consuming operations, such as sending emails from the code handling http requests. One thing you need to keep in mind, is that an ASP.NET web application is treated as stateless and the host can decide to off-load your app at any moment, even before your async method completes.
There is a mechanism in ASP.NET Framework to schedule long lasting activities so that the Host will be able to gracefully terminate your application waiting for the scheduled activities
HostingEnvironment.QueueBackgroundWorkItem
using System.Web.Hosting;
...
// Schedule task in a background tread
Func<CancellationToken, Task> workItem = ct => SendASendGridMessage(...);
HostingEnvironment.QueueBackgroundWorkItem(workItem);
You could use something like this:
Task t = new Task(() =>
{
if (something == true)
{
DoSomething(e);
}
});
t.RunSynchronously();
For more details, on this topic you can look at the following:
Synchronously waiting for an async operation, and why does Wait() freeze the program here
Related
I have an OWIN-based ASP.NET Web API hosted in a Windows Service. Most of my ApiController actions are async, and accept CancellationToken parameters:
[Route("data/{id}")]
public async Task<IHttpActionResult> GetSomeDataAsync(int id, CancellationToken token)
{
try
{
using (var _dataSource = ...)
{
return Ok(await _dataSource.GetDataAsync(id, token));
}
}
catch (OperationCanceledException ex)
{
return StatusCode(HttpStatusCode.NoContent);
}
}
Using the built-in request-cancellation features of Web API, if the client cancels the request, token is signaled and _dataSource handles it appropriately and throws the OperationCanceledException.
So far, so great.
But when my host process terminates (that is, the Windows Service stops), token isn't signaled and the cancellation-and-bail-out process isn't graceful.
I'm aware of the OWIN environment dictionary's host.onAppDisposing property, and I've dug into the source for the Microsoft.Owin[.*] and Microsoft.AspNet.WebApi.* packages to try and figure out where GetSomeDataAsync's token argument is coming from, but I'm not sure how to connect the pieces together.
I'd like to do something like
class WebServiceInAWindowsService : ServiceBase
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
...
protected override void OnStop()
{
_cts.Cancel();
}
}
But I'm not sure how to get _cts to be the source of the CancellationTokens that get fed to my actions, while not breaking the request-cancellation feature that's working well.
I'm thinking that CancellationTokenSource.CreateLinkedTokenSource() might be useful, but I'm not seeing how to put the pieces together.
Can you help? Thanks!
host.onAppDisposing is triggered when you call Dispose on the value returned from WebApp.Start.
https://github.com/aspnet/AspNetKatana/blob/9f6e09af6bf203744feb5347121fe25f6eec06d8/src/Microsoft.Owin.Hosting/Engine/HostingEngine.cs#L302-L308
https://github.com/aspnet/AspNetKatana/blob/9f6e09af6bf203744feb5347121fe25f6eec06d8/src/Microsoft.Owin.Hosting/Engine/HostingEngine.cs#L112
GetSomeDataAsync's is only associated with the request disconnect token by default (e.g. owin.CallCancelled). Via middleware or otherwise you can replace it with a linked TCS that's also connected to host.onAppDisposing.
Something like:
app.Use(async (env, next) =>
{
var reqAbt = env.Get<CancellationToken>("owin.CallCancelled");
var appAbt = env.Get<CancellationToken>("host.onAppDisposing");
using (linked = CancellationTokenSource.CreateLinkedTokenSource(reqAbt, appAbt))
{
env["owin.CallCancelled"] = linked.Token;
await next();
env["owin.CallCancelled"] = reqAbt;
}
});
I'm a little new to ASP.Net and Asynchronous coding so bear with me. I have written an asynchronous wrapper in C# for a web API that I would like to use in a ASP.Net application.
Here is one of the functions in the C# API wrapper:
public async Task<string> getProducts()
{
Products products = new Products();
products.data = new List<Item>();
string URL = client.BaseAddress + "/catalog/products";
string additionalQuery = "include=images";
HttpResponseMessage response = await client.GetAsync(URL + "?" + additionalQuery);
if (response.IsSuccessStatusCode)
{
Products p = await response.Content.ReadAsAsync<Products>();
products.data.AddRange(p.data);
while (response.IsSuccessStatusCode && p.meta.pagination.links.next != null)
{
response = await client.GetAsync(URL + p.meta.pagination.links.next + "&" + additionalQuery);
if (response.IsSuccessStatusCode)
{
p = await response.Content.ReadAsAsync<Products>();
products.data.AddRange(p.data);
}
}
}
return JsonConvert.SerializeObject(products, Formatting.Indented);
}
I then have a WebMethod in my ASP.Net application (which will be called using Ajax from a Javascript file) which should call the getProducts() function.
[WebMethod]
public static string GetProducts()
{
BigCommerceAPI api = getAPI();
return await api.getProducts();
}
Now of course this will not work as the WebMethod is not an async method. I have tried to change it to an async method which looked like:
[WebMethod]
public static async Task<string> GetProducts()
{
BigCommerceAPI api = getAPI();
return await api.getProducts();
}
This code does run, but as soon as it gets to the HttpResponseMessage response = await client.GetAsync(URL + "?" + additionalQuery); line in the getProducts() function the debugger will stop without any errors or data being returned.
What am I missing? How can I get call this asynchronous API from my ASP application?
So I actually resolved an issue very similar to this last night. It's odd because the call worked in .net 4.5. But we moved to 4.5.2 and the method started deadlocking.
I found these enlightening articles (here, here, and here) on async and asp.net.
So I modified my code to this
public async Task<Member> GetMemberByOrganizationId(string organizationId)
{
var task =
await
// ReSharper disable once UseStringInterpolation
_httpClient.GetAsync(string.Format("mdm/rest/api/members/member?accountId={0}", organizationId)).ConfigureAwait(false);
task.EnsureSuccessStatusCode();
var payload = task.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Member>(await payload.ConfigureAwait(false),
new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
}
which resolved my deadlocking issue.
So TLDR: from the Stephen Cleary article
In the overview, I mentioned that when you await a built-in awaitable,
then the awaitable will capture the current “context” and later apply
it to the remainder of the async method. What exactly is that
“context”?
Simple answer:
If you’re on a UI thread, then it’s a UI context. If you’re responding
to an ASP.NET request, then it’s an ASP.NET request context.
Otherwise, it’s usually a thread pool context. Complex answer:
If SynchronizationContext.Current is not null, then it’s the current
SynchronizationContext. (UI and ASP.NET request contexts are
SynchronizationContext contexts). Otherwise, it’s the current
TaskScheduler (TaskScheduler.Default is the thread pool context).
and the solution
In this case, you want to tell the awaiter to not capture the current
context by calling ConfigureAwait and passing false
I am not sure what is [WebMethod] in ASP.NET. I remember it used to be SOAP web services but no one does it anymore as we have Web API with controllers where you can use async/await in action methods.
One way to test your code would be to execute async method synchronously using .Result:
[WebMethod]
public static string GetProducts()
{
BigCommerceAPI api = getAPI();
return api.getProducts().Result;
}
As maccettura pointed out in the comment, it's a synchronous call and it locks the thread. To make sure you don't have dead locks, follow Fran's advice and add .ConfigureAwait(false) at the end of each async call in getProducts() method.
First by convention GetProducts() should be named GetProductsAsync().
Second, async does not magically allocate a new thread for it's method invocation. async-await is mainly about taking advantage of naturally asynchronous APIs, such as a network call to a database or a remote web-service.
When you use Task.Run, you explicitly use a thread-pool thread to execute your delegate.
[WebMethod]
public static string GetProductsAsync()
{
BigCommerceAPI api = getAPI();
return Task.Run(() => api.getProductsAsync().Result);
}
Check this link It's a project sample about how to implement Asynchronous web services call in ASP.NET
I had a very similar issue:
Main webapp is a ASP.NET 4.5 Web forms, but many of its functions implemented as AJAX calls from UI to a [webMethod] decorated function in the aspx.cs code-behind:
The webmethod makes an async call to a proxy. This call was
originally implemented with Task.Run() and I tried to rewrite with
just await ...
[WebMethod]
public static async Task<OperationResponse<CandidatesContainer>> GetCandidates(string currentRoleName, string customerNameFilter, string countryFilter, string currentQuarter)
{
string htmlResult = String.Empty;
List<CandidateEntryDTO> entries = new List<CandidateEntryDTO>();
try
{
entries = await GetCandiatesFromProxy(currentUser, currentRoleName, customerNameFilter, countryFilter, currentQuarter)
.ConfigureAwait(false);
}
catch (Exception ex)
{
log.Error("Error .....", ex);
}
CandidatesContainer payloadContainer = new CandidatesContainer {
CountryMappedCandiates = ...,
GridsHtml = htmlResult };
return new OperationResponse<CandidatesContainer>(payloadContainer, true);
}
3) The call GetCandiatesFromProxy(...) is the top of a chain of several async methods and at the bottom there's finally a HttpClient.GetAsync(...) call:
private async Task<B2PSResponse<string>> GetResponseFromB2PService(string serviceURI)
{
string jsonResultString = String.Empty;
if (_httpClientHandler == null)
{
_httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true };
}
if (_client == null)
{
_client = new HttpClient(_httpClientHandler);
}
HttpResponseMessage response = await _client.GetAsync(serviceURI).ConfigureAwait(false);
HttpContent content = response.Content;
string json = String.Empty;
if (response.StatusCode == HttpStatusCode.OK)
{
json = await content.ReadAsStringAsync().ConfigureAwait(false);
}
B2PSResponse<string> b2psResponse = new B2PSResponse<string>(response.StatusCode, response.ReasonPhrase, json);
return b2psResponse;
}
The code was not working (was stuck on the lowest level await) until
I started to add .ConfigureAwait(false) to each await call.
Interesting, that I had to add these .ConfigureAwait(false) to all await calls on the chain - all the way to the top call in the webMethod. Removing any of them would break the code - it would hang after the await that does not have the .ConfigureAwait(false).
The last point: I had to modify the Ajax call's SUCCESS path. The default Jason serialization for webmethods makes the result sent to AJAX call as
{data.d.MyObject}
i.e. inserts the {d} field containing the actual payload. After the webmethod return value was changed from MyObject to Task - this no longer worked - my payload was not found in the {data.d}. The result now contains
{data.d.Result.MyObject}
This is simply the result of serializing the Task object - which has the .Result field.
With one small change to the AJAX call is now working.
I have seen other very similar questions, but I still haven't found a solution, I have the Postal nuget package installed to handle email, and I have a web method that sends email asynchronously (I suppose). Based on other examples, here is my code:
[ActionName("PostEnviarCorreoReserva")]
[HttpPost]
public async Task<IHttpActionResult> PostEnviarCorreoReserva(
[FromBody] ReservaEmail vermodel,
String ver_gkey)
{
var ReservaId = Convert.ToInt32(vermodel.Reserva);
CultureInfo es = new CultureInfo("es-ES");
Thread.CurrentThread.CurrentCulture = es;
DtContex = new DTPPublicDataContext();
var RSPD = DtContex.res_reservas_usuario_det.First(i => i.reserva_gkey == ReservaId);
dynamic emailReserva = new Email(TipoEmail);
emailReserva.To = RSPD.email_reserva;
emailReserva.CodReserva = RSPD.reserva_gkey.ToString();
...
await emailReserva.SendAsync();
return Ok();
}
So I'm still a newbie, but I understand that this code should execute asynchronously, so I can later perform other operations to the Web API, but until it return the Ok response, Web API is busy handling this threat, what exactly Im doing wrong? Sending email takes a really long time
If you don't want to wait, while sendout will be completed, execute code in new thread.
new Thread(async () =>
{
await emailReserva.SendAsync();
}).Start();
I'm running into an issue with the .NET HttpClient class (.NET 4.5.1, System.Net.Http v4.0.0.0). I'm calling HttpClient.GetAsync, passing in a CancellationToken (as part of a Nuget package that abstracts calls between webservices). If the token has been cancelled before the call is made, the request goes through without throwing an exception. This behavior doesn't seem correct.
My test (incomplete, not fully written - no exception check):
[TestMethod]
public async Task Should_Cancel_If_Cancellation_Token_Called()
{
var endpoint = "nonexistent";
var cancellationTokenSource = new CancellationTokenSource();
var _mockHttpMessageHandler = new MockHttpMessageHandler();
_mockHttpMessageHandler
.When("*")
.Respond(HttpStatusCode.OK);
var _apiClient = new ApiClientService(new HttpClient(_mockHttpMessageHandler));
cancellationTokenSource.Cancel();
var result = await _apiClient.Get<string>(endpoint, null, cancellationTokenSource.Token);
}
The method I'm testing:
public async Task<T> Get<T>(string endpoint, IEnumerable<KeyValuePair<string, string>> parameters = null, CancellationToken cancellationToken = default(CancellationToken))
{
var builder = new UriBuilder(Properties.Settings.Default.MyEndpointHost + endpoint);
builder.Query = buildQueryStringFromParameters(parameters);
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// After this, we really shouldn't continue.
var request = await _httpClient.GetAsync(builder.Uri, cancellationToken);
if (!request.IsSuccessStatusCode)
{
if (request.StatusCode >= HttpStatusCode.BadRequest && request.StatusCode < HttpStatusCode.InternalServerError)
{
throw new EndpointClientException("Service responded with an error message.", request.StatusCode, request.ReasonPhrase);
}
if (request.StatusCode >= HttpStatusCode.InternalServerError && (int)request.StatusCode < 600)
{
throw new EndpointServerException("An error occurred in the Service endpoint.", request.StatusCode, request.ReasonPhrase);
}
}
var json = await request.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(json);
}
catch (Exception ex)
{
throw;
}
}
I know that I can check the status of the cancellation token before calling HttpClient.GetAsync and throw if cancellation has been requested. I know that I can also register a delegate to cancel the HttpClient request. However, it seems as though passing the token to the HttpClient method should take care of this for me (or, else, what's the point?) so I'm wondering if I'm missing something. I don't have access to the HttpClient source code.
Why is HttpClient.GetAsync not checking my cancellation token and aborting its process when I pass it in?
HttpClient doesn't check the cancellation token itself, it passes it on to the message handler when it calls its SendAsync method. It then registers to the continuation on the task returned from SendAsync and will set its own task as cancelled if the task returned from the message handler was cancelled.
So the problem in your scenario is in your implementation of MockHttpMessageHandler which seems doesn't check the cancellation token.
Note, that if HttpClient is called via its empty constructor, it internally uses HttpClientHandler which registers a delegate on the cancellation token that aborts the request and cancels the task.
Could someone point me to a resource that would help explain how web api (specifically using Owin Self Host) handles request cancellation?
Here's the sequence of events i'm observing:
someone makes a GET from chrome
Api controller (through some layers) fires off an async SQL query
someone hits the X button in chrome (i don't know exactly what happens on socket for this)
What happens next in Web Api??
There was some code running in a controller, does the thread running it get aborted? If it was an async controller awaiting another Task, does that task still have an awaiter in case it returns with an exception?
For context: I do have an async controller awaiting a Task (this is the only call site) which looks to be throwing an unobserved exception in some edge cases. I haven't been able to isolate or re-produce yet :)
I did find something called HttpResponse.ClientDisconnectedToken, but don't know well that is supported in Owin Selfhost + is it even the good thing to use for all user cancels.
I've dealt with this by handing the System.OperationCanceledException in a custom middleware I've registered before WebApi.
public class ExceptionHanldingMiddleware : OwinMiddleware
{
public override async Task Invoke(IOwinContext context)
{
try
{
await Next.Invoke(context);
}
catch (OperationCanceledException) when (context.Request.CallCancelled.IsCancellationRequested)
{
//swallow user-agent cancelling request.
_log.Trace($"client disconnected on request for: {context.Request.Path}.");
}
catch (Exception ex)
{
_log.Error(ex);
context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
context.Response.ReasonPhrase = "Internal Server Error";
}
}
}
As you stated that your async controller is awaiting for a Task, which sometimes got some exception, I suggest you ContinueWith extension method for a task, which can be run only then your task is faulted, like this:
task.ContinueWith(
t =>
logger.Error(t.Exception.Message, t.Exception);
, TaskContinuationOptions.OnlyOnFaulted);
This is a default mechanism to handle the exceptions, and this will work in OWIN application.
Second, as for the cancellation: task can be started with a CancellationToken structure, which can be used for a cancelling the task during the execution. You can read more in the MSDN article.
HttpResponse.ClientDisconnectedToken is used for a situation when the client has been disconnected and the request should not be proceed in execution.
You can use this token, or create your own with CancellationTokenSource, like this:
var source = new CancellationTokenSource();
var token = source.Token;
var task = Task.Factory.StartNew(() =>
{
// Were we already canceled?
ct.ThrowIfCancellationRequested();
var moreToDo = true;
while (moreToDo)
{
// Poll on this property if you have to do
// other cleanup before throwing.
if (ct.IsCancellationRequested)
{
// Clean up here, then...
ct.ThrowIfCancellationRequested();
}
}
}, token);