我正在编写一些C#代码来发送电子邮件(通过Mailjet/Azure(。它确实发送电子邮件,但由于某种原因,在单步执行代码时,我从未跳过这行代码......
MailjetResponse response = await client.PostAsync(request);
它只是挂在那个点上。知道为什么吗?同样,电子邮件正在正常发送!
public static async Task<bool> SendEmailWithAttachment(string toAddress, string subject, string messageBody, bool sendBCCYesNo, bool sendFromInfoAddressYesNo, MemoryStream attachment = null, string attachmentFilename = null)
{
bool successYesNo = true;
try
{
MailjetClient client = new MailjetClient("xxxxxx", "xxxxx")
{
Version = ApiVersion.V3_1,
};
MailjetRequest request = new MailjetRequest
{
Resource = Send.Resource,
}
.Property(Send.Messages, new JArray {
new JObject {
{"From", new JObject {
{"Email", "xxxxx@xxxxx.com"},
{"Name", "xxxxx"}
}},
{"To", new JArray {
new JObject {
{"Email", toAddress},
{"Name", toAddress}
}
}},
{"Subject", subject},
{"TextPart", messageBody},
{"HTMLPart", messageBody}
}
});
MailjetResponse response = await client.PostAsync(request);
if (response.IsSuccessStatusCode) // I never get to this point
{
:
我正在使用这个调用代码....
if (Utility.SendEmailWithAttachment("xxxxx@xxxxx.com", "Test Email", "Test Body", false, false,
po, "AAA.pdf").Result == false)
{
lblStatus.Text = "Email send failure. Please contact support.";
return false;
}
有趣的是,当我运行 mailjet 提供的示例代码时,电子邮件发送正常,并且我确实到达了 PostAsync 之后的行。据我所知,唯一的主要区别是我使用的是返回布尔值的任务,而不仅仅是任务。这是 mailjet 提供的代码,可以正常工作....
static void Main(string[] args)
{
RunAsync().Wait();
}
static async Task RunAsync()
{
MailjetClient client = new MailjetClient("xxxx", "xxxx")
{
Version = ApiVersion.V3_1,
};
MailjetRequest request = new MailjetRequest
{
Resource = Send.Resource,
}
.Property(Send.Messages, new JArray {
new JObject {
{"From", new JObject {
{"Email", "xxxx@xxxx.com"},
{"Name", "xxxx"}
}},
{"To", new JArray {
new JObject {
{"Email", "xxxx@xxxx.com"},
{"Name", "xxxx"}
}
}},
{"Subject", "Your email flight plan!"},
{"TextPart", "Dear passenger 1, welcome to Mailjet! May the delivery force be with you!"},
{"HTMLPart", "<h3>Dear passenger 1, welcome to <a href='https://www.mailjet.com/'>Mailjet</a>!</h3><br />May the delivery force be with you!"}
}
});
MailjetResponse response = await client.PostAsync(request);
if (response.IsSuccessStatusCode) // this line is reached!
{
提前感谢!
试试下面的代码。一个好的经验法则是不要使用 *。结果,除非先等待。
if ((await Utility.SendEmailWithAttachment("xxxxx@xxxxx.com", "Test Email", "Test Body", false, false,
po, "AAA.pdf")) == false)
{
lblStatus.Text = "Email send failure. Please contact support.";
return false;
}
在方法中
public static async Task<bool> SendEmailWithAttachment(string toAddress, string subject, string messageBody, bool sendBCCYesNo, bool sendFromInfoAddressYesNo, MemoryStream attachment = null, string attachmentFilename = null)
将以下行从:
MailjetResponse response = await client.PostAsync(request);
自:
MailjetResponse response = await client.PostAsync(request).ConfigureAwait(false);
请阅读这篇关于异步方法中死锁的精彩文章
该问题是由对.Result
的调用引起的 - 这是一个阻塞调用。如果在 UI 线程中调用,它将阻止线程。这意味着应用程序通常无法响应并显得冻结,这相当丑陋。
这也意味着任何尝试在 UI 线程上恢复的await
调用,例如
MailjetResponse response = await client.PostAsync(request);
不会。我将重复一遍 - 如果await client.PostAsync(request);
似乎没有恢复,那是因为某些东西阻塞了 UI 线程。
await
不会使任何内容异步运行,也不会启动任何线程。它等待已经运行的异步操作而不会阻塞。完成这些操作后,它将在原始同步上下文中恢复 - 在桌面应用程序中,这意味着在 UI 线程上恢复。这就是允许await
后的任何代码修改 UI 的原因。
解决方案是 删除.Result
.没有它,无论如何使用异步调用都是没有意义的 - 整个应用程序都会挂起,那么等待的意义何在?
假设该方法是在事件处理程序中调用的,则事件处理程序本身应变为异步。代码应该清理一下。这不会影响异步行为,但使其更易于读取和调试:
private async void button1_Click(...)
{
var ok=await Utility.SendEmailWithAttachment("xxxxx@xxxxx.com", "Test Email", "Test Body",
false, false,po, "AAA.pdf");
if (!ok)
{
lblStatus.Text = "Email send failure. Please contact support.";
}
}
如果该方法不是事件处理程序,则应更改为异步方法。它的调用者也应该成为一个异步方法,一直到顶部 - 大多数时候,这是一个事件处理程序:
private async Task<bool> TrySend()
{
var ok=await Utility.SendEmailWithAttachment("xxxxx@xxxxx.com", "Test Email", "Test Body",
false, false,po, "AAA.pdf");
if (!ok)
{
lblStatus.Text = "Email send failure. Please contact support.";
return false;
}
else
{
.....
return true;
}
}
private async void button1_Click(...)
{
var ok=await TrySend();
...
}
SendEmailWithAttachment
本身不会尝试修改 UI,因此不需要在 UI 线程上恢复。添加ConfigureAwait(false)
将允许代码在线程池线程上恢复,并让调用方决定是否在 UI 上恢复。这主要是此时的优化,但它也消除了原始死锁中的辅助阻塞点。如果有人错误地重新添加.Result
,它将"仅"冻结 UI:
MailjetResponse response = await client.PostAsync(request).ConfigureAwait(false);