I have method that execute Email sender like this:
public void SendIt()
{
if (_Error && !_Override) return;
var html = new StringBuilder();
html.Append(HTMLHeader());
html.Append(Body);
html.Append(HTMLClose());
IsHTML = true;
SendAsync = true;
Body = html.ToString();
Send();
}
I want to change this method to asynchronous method so I do:
public async void SendIt()
{
await Send();
}
But I get
'bool' does not contain a definition for 'GetAwaiter' and no
extension method 'GetAwaiter' accepting a first argument of type
'bool' could be found (are you missing a using directive or an
assembly reference?)
Send method:
public bool Send()
{
var MailFrom = new MailAddress(_From, _From);
_Email.From = MailFrom;
if (_To.IndexOf(';') > -1)
{
string[] tolist = _To.Split(';');
foreach (string toAddress in tolist)
_Email.To.Add(new MailAddress(toAddress.Trim()));
}
else _Email.To.Add(new MailAddress(_To.Trim()));
if (_CC != null && _CC.Length > 5)
{
if (_CC.IndexOf(';') > -1)
{
string[] Cclist = _CC.Split(';');
foreach (string ccAddress in Cclist)
_Email.CC.Add(new MailAddress(ccAddress.Trim()));
}
else _Email.CC.Add(new MailAddress(_CC.Trim()));
}
if (_BCC != null && _BCC.Length > 5)
{
if (_BCC.IndexOf(';') > -1)
{
string[] Bcclist = _BCC.Split(';');
foreach (string bccAddress in Bcclist)
_Email.Bcc.Add(new MailAddress(bccAddress.Trim()));
}
else _Email.Bcc.Add(new MailAddress(_BCC.Trim()));
}
_Email.Subject = _Subject;
_Email.IsBodyHtml = _isHTML;
_Email.Body = _Body;
_Email.BodyEncoding = Encoding.UTF8;
// Smtp Client
var Connection = new SmtpClient(_Server, _Port)
{
DeliveryMethod = SmtpDeliveryMethod.Network,
Timeout = 60000
};
if (_UserName.Length > 1 && _Password.Length > 1 && _Domain.Length > 1)
Connection.Credentials = new NetworkCredential(_UserName, _Password, _Domain);
else if (_UserName.Length > 1 && _Password.Length > 1)
Connection.Credentials = new NetworkCredential(_UserName, _Password);
bool bFlag = true;
Connection.EnableSsl = _SSL;
try
{
Connection.Send(_Email);
}
catch (SmtpFailedRecipientsException ex)
{
var msg = new StringBuilder();
for (int k = 0; k < ex.InnerExceptions.Length; k++)
{
var StatusCode = ex.InnerExceptions[k].StatusCode;
if (StatusCode == SmtpStatusCode.MailboxUnavailable || StatusCode == SmtpStatusCode.MailboxBusy)
{
ErrMsg = "Failed to deliver message to " + ex.FailedRecipient[k].ToString();
try
{
if (_TryAgainOnFailure)
{
System.Threading.Thread.Sleep(_TryAgainDelayTime);
// Send the message
string sTemp = "";
if (_SendAsync)
Connection.SendAsync(_Email, sTemp);
else Connection.Send(_Email);
}
_ErrMsg = string.Empty;
}
catch (SmtpException sex)
{
_ErrMsg = sex.Message;
_Error = true;
}
}
}
}
catch (SmtpException sex)
{
string dat = sex.Data.ToString();
_ErrMsg = sex.Message;
_Error = true;
}
return bFlag;
}
How can I convert this boolean method to async method?
I try something like:
public async bool Send() {
But I get:
The return type of an async method must be void, Task or Task
What should I do to solve this? Regards
The return type of an async method must be void, Task or Task<T>
Like the error states you have to change the return type to Task<bool>. You will then have to change the method itself so it awaits a Task or returns a Task.
public async Task<Boolean> SendAsync() { // renamed your method with suffix Async
// ... code unchanged
try {
await Connection.SendMailAsync(this._Email);
}
// ... code unchanged
}
As for your other methods that call this they should also return type Task unless they are event driven like a windows form or wpf click event.
See also SendMailAsync
You must return Task<T> if you use async method.
Correct definition for your method is:
public async Task<Boolean> Send()
Also you need to use await keyword inside your method.
You can write Send method using SendMailAsync.
Example is here:
public async Task Send(string to, string subject, string body)
{
var message = new MailMessage
{
Subject = subject,
Body = body
};
message.To.Add(to);
using (var smtpClient = new SmtpClient())
{
await smtpClient.SendMailAsync(message);
}
}
More details you can review here.
Also please review details about async/await programming here.
Related
I have a .net core API service which is called from a angular client project.
When a user request a status of his payment, we will make call to this service api and this service will then call a payment gateway service to fetch the status of payment and the output result will return to the user.
When i try to integrate this i am facing this below error.
net::ERR_CONNECTION_RESET 200 (OK)
core.js:5967 ERROR Unknown Error
This above issue is not showing when i try to hit the service after putting one breakpoint. Its also returning the result.
This is how entire flow works
Client side call performs by user
this.dataservice.postFeed(method, JSON.stringify(this.initsearch)).subscribe(result => {
var response = result.body["data"];
console.log(response);
});
Server side code looks like
[HttpPost]
public async Task<IActionResult> Post([FromBody] ObjectModel searchValue)
{
ApiResponse<string> response = new ApiResponse<string>();
IBaseResult<string> result = await _adlerBo.GetPaymentStatus(searchValue);
response.Success = result.success;
response.Data = result.Data;
return Ok(response);
}
In BusinessObject.cs
public async Task<IBaseResult<string>> GetPaymentStatus(PaymentSearchModel requestModel){
string apiResponse = await PaymentStatusCheckUsingAPI(requestModel.orderid);
return apiResponse ;
}
private async Task<string> PaymentStatusCheckUsingAPI(string orderNumber)
{
string message = await PostPaymentRequestToGateway(statusApiUrl, authQueryUrlParam);
NameValueCollection param = await GetResponseMap(message);
string status = "";
string encResJson = "";
if (param != null && param.Count == 2)
{
for (int i = 0; i < param.Count; i++)
{
if ("status".Equals(param.Keys[i]))
{
status = param[i];
}
if ("enc_response".Equals(param.Keys[i]))
{
encResJson = param[i];
}
}
if (!"".Equals(status) && status.Equals("0"))
{
resJson = crypto.Decrypt(encResJson, workingKey);
}
else if (!"".Equals(status) && status.Equals("1"))
{
Console.WriteLine("failure response: " + encResJson);
}
}
return resJson;
}
private async Task<string> PostPaymentRequestToGateway(string queryUrl, string urlParam)
{
string message = "";
try
{
StreamWriter myWriter = null;// it will open a http connection with provided url
WebRequest objRequest = WebRequest.Create(queryUrl);//send data using objxmlhttp object
objRequest.Method = "POST";
//objRequest.ContentLength = TranRequest.Length;
objRequest.ContentType = "application/x-www-form-urlencoded";//to set content type
myWriter = new System.IO.StreamWriter(objRequest.GetRequestStream());
myWriter.Write(urlParam);//send data
myWriter.Close();//closed the myWriter object
// Getting Response
System.Net.HttpWebResponse objResponse = (System.Net.HttpWebResponse)objRequest.GetResponse();//receive the responce from objxmlhttp object
using (System.IO.StreamReader sr = new System.IO.StreamReader(objResponse.GetResponseStream()))
{
message = await sr.ReadToEndAsync();
//Response.Write(message);
}
}
catch (Exception exception)
{
Console.Write("Exception occured while connection." + exception);
}
return message;
}
private async Task<NameValueCollection> GetResponseMap(string message)
{
//await Task.Delay(2000); I did this with no Luck
NameValueCollection Params = new NameValueCollection();
if (message != null || !"".Equals(message))
{
string[] segments = message.Split('&');
foreach (string seg in segments)
{
string[] parts = seg.Split('=');
if (parts.Length > 0)
{
string Key = parts[0].Trim();
string Value = parts[1].Trim();
Params.Add(Key, Value);
}
}
}
return await Task.FromResult(Params);
}
Any idea how to fix this? Why its working when i put breakpoint and not otherwise.
Am i doing correct asynchronous implimentsion in my api?
I don't know the async/await mechanism very well and I searched on Google, but without finding an acceptable answer.
I have a question about the code below:
When using .Result, the switch() block is executed.
// Here the flow starts - the callee is a VueJS axios ajax call
[Route("ChangeStatusOrders/ValidateStatus")]
[HttpPost]
public async Task<ActionResult> ValidateStatus(OrdersToBeProcessed model)
{
bool success = false;
string message = "";
try
{
model.ReturnData = await _statusBusiness.Validate(model);
success = true;
return Json(new { Success = true, ReturnData = model.ReturnData }, JsonRequestBehavior.AllowGet);
}
catch (ArgumentException ex)
{
message = $"Bad Request {ex.Message}";
_log.Error(message);
Response.StatusCode = 400;
return Json(new { Success = false, ErrorMessage = message }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
message = $"Spomething wrong message here - {ex.Message}";
_log.Error(message);
Response.StatusCode = 500;
return Json(new { Success = false, ErrorMessage = message }, JsonRequestBehavior.AllowGet);
}
finally
{
var param = new
{
model.Orders,
model.NotesText,
model.CompanyID,
model.CompanyName,
model.NewStatus,
model.ReturnData,
model.CodeType,
model.UserID,
model.isInternalUser,
Message = message
};
LogUserAction(ActionType.ChangeStatusOrders, success, param);
}
}
private readonly IDictionary<bool, Func<List<string>, int, List<Order>>> _dic;
constructor()
{
// below methods are not async
_dic = new Dictionary<bool, Func<List<string>, int, List<Pedido>>>
{
{ true, (p) => _orderRepository.GetByID(p) },
{ false, (p) => _orderRepository.GetByCompanyID(p) }
};
}
public async Task<ProcessResults> Validate(OrdersToBeProcessed model)
{
ProcessResults result = new ProcessResults();
bool useOurID = (model.CodeType == 1);
return await Task.Run(() =>
{
try
{
List<string> ordersList = SplitOrders(model.Orders);
List<string> buffer = ordersList.Distinct().ToList();
List<Order> ordersList = new List<Order>(buffer.Count());
buffer.ForEach(item =>
{
var tmp = new Order();
if (useOurID)
tmp.ID = Convert.ToInt64(item);
else
tmp.CompanyId = item;
orderList.Add(tmp);
});
List<Order> foundOrders = _dic[useOurId](buffer);
result.SuccessfullOrders = new List<ProcessedOrder>();
result.NotFoundOrders = new List<ProcessedOrder>();
result.NotAllowedOrders = new List<ProcessedOrder>();
// merge the list of order's id with those one's found in database
List<Orders> union = foundOrders.MergeAndReplace(ordersList).ToList();
foreach (var item in union)
{
ProcessedOrder ret = ValidateAsync(item).Result; // <-- here is an Async call
switch (ret.ProcessedResult)
{
case ProcessedResultEnum.CanBeProcessed:
result.SuccessfullOrders.Add(ret);
break;
case ProcessedResultEnum.OrderNotFound:
result.NotFoundOrders.Add(ret);
break;
case ProcessedResultEnum.CannotBeProcessed:
result.NotAllowedOrders.Add(ret);
break;
}
}
return result;
});
private async Task<Order> ValidateAsync(Order item)
{
ProcessedOrder ret = new ProcessedOrder();
ret.CompanyID = item.CompanyID;
ret.Name = item.Name;
ret.ID = item.ID;
ret.Status = (item.Status?.ID).ToString();
var queueOrderItems = await _queueOrderService.SearchByOrderIdAsync(item.ID);
if (item.ID == 0 || String.IsNullOrEmpty(iitem.CodigoEntidade))
{
ret.Message = "Order not found in database.";
ret.Result = ProcessedResultEnum.OrderNotFound;
}
else if (item.Status == null || queueOrderItems == null)
{
ret.Message = "Order Cannot Be Processed";
ret.Result = ProcessedResultEnum.CannotBeProcessed;
}
else
{
ret.Message = "Order Can Be Processed";
ret.Result = ProcessedResultEnum.CanBeProcessed;
}
return ret;
}
public async Task<SearchBiIdResult> SearchByOrderIdAsync(long orderID )
{
SearchByIDResult ret;
SearchByIDRequest request = new SearchByIDRequest() { OrderID = orderID };
// calls a WCF async method descibed below
SearchByIDResponse response = await _queueOrderItemsClient.SearchByIDAsync(request);
ret = _mapper.Map<SearchByIDResponse>(response);
return ret;
}
// WCF async method definition (in reference.cs)
public System.Threading.Tasks.Task<UI.Services.Business.QueuesService.SearchByIDResponse> SearchByIDAsync(UI.Services.Business.QueuesService.SearchByIDRequest request) {
return base.Channel.SearchByIDAsync(request);
}
However, if I replace .Result with
ProcessedOrder ret = await ValidateAsync(item);
the loop immediately returns back up to the foreach statement, without executing the switch() block.
Can someone explain to me why this behavior?
I changed
return await Task.Run(() =>
to
return await Task.Run(async () =>
and the foreach block behaved as expected.
Had this problem yesterday, changed some code, and it's happening again... Why don't both mails send? The promise is not awaited for. Sometimes 1, sometimes 2 mails send. Bool "messagesSent" also returns false sometimes, when it should return true.
The function:
private async Task<bool> SendMails(string email, string name, string pdfPath, string imgPath)
{
var client = new SendGridClient(_config["SendGrid:Key"]);
bool messagesSent = false;
try
{
var messageClient = new SendGridMessage
{
From = new EmailAddress(_config["SendGrid:Recipient"]),
Subject = "Subject1",
HtmlContent = _textManager.Get("email-b")
};
var MessageServer = new SendGridMessage
{
From = new EmailAddress(_config["SendGrid:Recipient"]),
Subject = "Subject2",
HtmlContent = _textManager.Get("email-s")
};
messageClient.AddTo(email, name);
MessageServer.AddTo(email, name);
string[] fileListClient = new string[] { pdfPath };
string[] FileListServer = new string[] { pdfPath, imgPath };
foreach (var file in fileListClient)
{
var fileInfo = new FileInfo(file);
if (fileInfo.Exists)
await messageClient.AddAttachmentAsync(fileInfo.Name, fileInfo.OpenRead());
}
foreach (var file in FileListServer)
{
var fileInfo = new FileInfo(file);
if (fileInfo.Exists)
await MessageServer.AddAttachmentAsync(fileInfo.Name, fileInfo.OpenRead());
}
var responseClient = await client.SendEmailAsync(messageClient);
var responseServer = await client.SendEmailAsync(MessageServer);
if (responseClient.StatusCode.ToString() == "202" && responseServer.StatusCode.ToString() == "202")
{
messagesSent = true;
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
return messagesSent;
}
Called by:
bool sentMails = await SendMails(formCollection["email"], formCollection["name"], pdfPath, imgPath);
if (!sentMails)
{
errorMessage = "Error sending mails.";
succes = false;
}
EDIT:
Request was valid, the call was being blocked by ISP/MailServer. This had nothing to do with faulty async.
Ok, the whole point of an asynchroneous call, is not to "wait" or delay performance. The method will just 'run'. Unless there is an exception raised in that method, there is nothing that would prevent the method from not get executed successfully.
Try adding a break-point and see why the messagesSent returns false sometimes, I am suspecting this line:
if (responseClient.StatusCode.ToString() == "202" && responseServer.StatusCode.ToString() == "202")
Is there any chance that the StatusCode might be returning a "200" as well?
I have three different databases that I need to check that I am connected to. This is what I originally have, which works perfectly fine.
public async Task<ServiceAvailabilityDTO> ServiceAvailabilityStatus()
{
return new ServiceAvailabilityDTO
{
IsDb1Online = await IsDb1Available(),
IsDb2Online = IsDb2Available(),
IsDb3Online = await IsDb3Available()
};
}
private async Task<bool> IsDb1Available()
{
var count = await _db1Service.GetDbCount();
if (count > 0)
return true;
return false;
}
private bool IsDb2Available()
{
if (_db2Service.GetDbCount() > 0)
return true;
return false;
}
private async Task<bool> IsDb3Available()
{
var pong = await _db3Provider.PingDb();
if(pong.Success == true && pong.Version != null)
return true;
return false;
}
Now however, I need to log exception messages in my DTO for each DB check.
public async Task<ServiceAvailabilityDTO> ServiceAvailabilityStatus()
{
return new ServiceAvailabilityDTO
{
IsDb1Online = await IsDb1Available(),
IsDb2Online = IsDb2Available(),
IsDb3Online = await IsDb3Available(this) // This is an example. I want to pass the reference of **ServiceAvailabilityDTO** to **IsDb3Available**
};
}
private async Task<bool> IsDb3Available(ServiceAvailabilityDTO availability)
{
try
{
var pong = await _db3Provider.PingDb();
if(pong.Success == true && pong.Version != null)
return true;
return false;
}
catch (Exception e)
{
var exceptionMessage = e.Message;
if (e.InnerException != null)
{
// This is what I hope to put into the object reference
exceptionMessage = String.Join("\n", exceptionMessage, e.InnerException.Message);
availability.db3Exception = exceptionMessage ;
}
return false;
}
}
My question is;
Can I keep my return method the same as in the first example, and pass the object reference to each method to store the exception and still return my bool value.
Or does the object not get created until all of the method calls have happened, and then create the object with the returned values?
I know I could just create the object normally and pass it in each
method call, but it is specifically this way of doing it that has
inspired me to ask this question, purely to be informed and learn
from.
Thanks.
No, you cannot do it like this because in the context of what you're doing this does not refer to the object you're populating, it refers to the object containing the method you're calling.
public async Task<ServiceAvailabilityDTO> ServiceAvailabilityStatus()
{
return new ServiceAvailabilityDTO
{
IsDb1Online = await IsDb1Available(),
IsDb2Online = IsDb2Available(),
IsDb3Online = await IsDb3Available(this) // here 'this' does NOT ref to ServiceAvailabilityDTO
};
}
There is no keyword which does refer to ServiceAvailabilityDTO either, so you're left with creating the object, and passing it to each method. At this point, I dont think there is much point you returning the boolean either - you may as well set the boolean property in line
public async Task<ServiceAvailabilityDTO> ServiceAvailabilityStatus()
{
var sa = new ServiceAvailabilityDTO();
await CheckDb1Available(sa);
CheckDb2Available(sa);
await CheckDb3Available(sa);
return sa;
}
(Note I've renamed the methods from Is* to Check* as the former implies a return boolean, the latter implies something going on inline.)
I have a the following controller method
public async Task<ActionResult> SendToAllUsers(SentMailToAllUsersModel model)
{
if (ModelState.IsValid)
{
var mail = MailService.SendMailToAllUsers(model.Body, model.Title);
await mail;
}
return View(model);
}
Which is calling this method on the mail service
public Task SendMailToAllUsers(string content, string title)
{
var users = UserService.GetAllUsers();
var mailTemplates = users.Result.AsParallel().Select(user =>
{
var mailTemplate = new MastersMailTemplate(user);
mailTemplate.HtmlEmailTemplate = content;
mailTemplate.Subject = title;
mailTemplate.From = _fromEmail;
return Task.Factory.StartNew(() => MailProvider.SendEmailAsync(mailTemplate.CreateMailMessage(), new ResultDescription()).ConfigureAwait(false));
}).ToArray();
return Task.WhenAll(mailTemplates);
}
This method is triggering the mail provider that executes this method:
public Task<IResultDescription> SendEmailAsync(MailMessage message, IResultDescription rd)
{
// Create our SMTP Client
SmtpClient client = new SmtpClient();
client.Host = SmtpServer;
client.Port = SmtpServerPort;
client.Credentials = new NetworkCredential(SmtpServerUsername, SmtpServerPassword);
client.EnableSsl = true;
if (AppSettings.IsInTestMode)
{
Log.Info("Test mode check: Removing all emails and replace to test");
message.To.Clear();
foreach (var email in AppSettings.DefaultTestEmail)
{
message.To.Add(new MailAddress(email));
}
}
client.Timeout = 10;
Log.Info("Sending Email to" + message.To.FirstOrDefault());
var task = Task.Run(async () =>
{
try{
client.SendCompleted += (s, e) =>
{
client.Dispose();
message.Dispose();
};
await client.SendAsync(message);
rd.Success = true;
return rd;
}
catch (Exception e)
{
Log.Error("Email not send");
rd.Success = false;
if (rd.Errors == null)
{
IList<string> errors = new List<string>();
errors.Add(e.Message);
rd.Errors = errors;
}
else
{
rd.Errors.Add(e.Message);
}
return rd;
}
});
return task;
}
The problem is that the result view is returned before any mails where sent.
The controller is not waiting untill all mails are sent.
How can I make sure that the controller only continues execution when all tasks in the mail service are completed?
As a general rule, do not use Task.Run, Task.Factory.StartNew, Parallel, or PLINQ on ASP.NET. There is always a better way. In this case, just use async and await:
public async Task SendMailToAllUsersAsync(string content, string title)
{
var users = await UserService.GetAllUsersAsync();
var mailTemplates = users.AsParallel().Select(user =>
{
var mailTemplate = new MastersMailTemplate(user);
mailTemplate.HtmlEmailTemplate = content;
mailTemplate.Subject = title;
mailTemplate.From = _fromEmail;
return MailProvider.SendEmailAsync(mailTemplate.CreateMailMessage());
}).ToArray();
return await Task.WhenAll(mailTemplates);
}
Similarly for your inner method:
public Task<IResultDescription> SendEmailAsync(MailMessage message, IResultDescription rd)
{
using (SmtpClient client = new SmtpClient())
using (message)
{
client.Host = SmtpServer;
client.Port = SmtpServerPort;
client.Credentials = new NetworkCredential(SmtpServerUsername, SmtpServerPassword);
client.EnableSsl = true;
if (AppSettings.IsInTestMode)
{
Log.Info("Test mode check: Removing all emails and replace to test");
message.To.Clear();
foreach (var email in AppSettings.DefaultTestEmail)
{
message.To.Add(new MailAddress(email));
}
}
client.Timeout = 10;
Log.Info("Sending Email to" + message.To.FirstOrDefault());
try
{
await client.SendAsync(message);
rd.Success = true;
}
catch (Exception e)
{
Log.Error("Email not send");
rd.Success = false;
if (rd.Errors == null)
{
IList<string> errors = new List<string>();
errors.Add(e.Message);
rd.Errors = errors;
}
else
{
rd.Errors.Add(e.Message);
}
}
return rd;
}
}
Remember, async makes things easy. If the async code is excessively complicated, check for A Better Way. I have an async intro on my blog that you may find useful.
I think the problem is in your SendMailToAllUsers method. I think you need to await the MailProvider.SendEmailAsync call. If you don't do this, the task started by Task.Factory.StartNew will be considered complete as soon as that method executes. Because the method is actually asynchronous it only kicks off the operation, it doesn't wait for its completion. If you await the result that should fix the problem.
Change your code to:
public Task SendMailToAllUsers(string content, string title)
{
var users = UserService.GetAllUsers();
var mailTemplates = users.Result.AsParallel().Select(user =>
{
var mailTemplate = new MastersMailTemplate(user);
mailTemplate.HtmlEmailTemplate = content;
mailTemplate.Subject = title;
mailTemplate.From = _fromEmail;
// Await the result of the lambda expression
return Task.Factory.StartNew(() => await MailProvider.SendEmailAsync(mailTemplate.CreateMailMessage(), new ResultDescription()).ConfigureAwait(false));
}).ToArray();
return Task.WhenAll(mailTemplates);
}