【问题标题】:C# SmtpClient SendAsync from Static Helper Method?来自静态助手方法的 C# SmtpClient SendAsync?
【发布时间】:2015-02-26 19:18:37
【问题描述】:

我正在尝试编写一个帮助类来在我的 C# 应用程序中发送电子邮件。我想使用SmtpClient.SendAsync,但显然我不明白异步的工作原理,或者我的设置有误:

public class EmailService
{
    public static void SendMessage(MailMessage message)
    {
        var client = new SmtpClient("127.0.0.1", 25);
        client.SendCompleted += (s, e) =>
        {
            if (e.Error != null)
            {
                // TODO: Log the SMTP error somewhere
            }
            var callbackClient = s as SmtpClient;
            var callbackMessage = e.UserState as MailMessage;
            callbackClient.Dispose();
            callbackMessage.Dispose();
        };
        client.SendAsync(message, message);
    }
}

这会导致以下异常:

此时无法启动异步操作。异步 操作只能在异步处理程序中启动或 模块或页面生命周期中的某些事件期间。如果这 执行页面时发生异常,请确保该页面是 标记为 。此异常也可能表明 尝试调用通常不受支持的“async void”方法 在 ASP.NET 请求处理中。相反,异步方法 应该返回一个任务,调用者应该等待它。

基于这个异常,听起来我的SendMessage 方法可能需要返回一个Task。但是,client.SendAsync 返回 void,因此似乎没有任何 Task 要返回或 await

【问题讨论】:

    标签: c# asp.net smtpclient


    【解决方案1】:

    一般异步用法

    要使用async 方法,您的方法必须标记为async(一直到调用堆栈)。当你有异步方法时,你真的想避免返回类型为 void。大多数情况下,您应该返回 Task(如果您的方法具有现有的返回类型,则返回 Task<T>)。您还应该使用SendMailAsync,它使用新的基于任务的异步方法。最后,您需要 await 调用异步方法。

    public class EmailService
    {
        public static async Task SendMessage(MailMessage message)
        {
            using (var client = new SmtpClient("127.0.0.1", 25))
            {
                await client.SendMailAsync(message, message);
            }
        }
    }
    

    仅适用于 Web 表单

    在 Web 窗体中使用异步有点棘手。您必须将页面标记为异步。

    <%@ Page Async="true" %>
    

    您会注意到 Web 窗体的一件事是,您无法更改方法以返回 ASP.NET 生命周期事件的任务。所以你需要注册任务。

    public void Page_Load(object sender, EventArgs e)
    {
        RegisterAsyncTask(new PageAsyncTask(SendMessage));
    }
    

    但是,我们需要能够将消息传递给函数。所以我们将使用 lambda。

    public void Page_Load(object sender, EventArgs e)
    {
        RegisterAsyncTask(new PageAsyncTask(() => EmailService.SendMessage(message)));
    }
    

    【讨论】:

    • 我正在测试一些东西,但只是一个简短的说明......我使用的是 MVC,而不是 WebForms。刚刚提到它是因为我看到你随着时间的推移改进你的答案:)
    • @Sam 老实说,随着时间的推移,我正在研究如何做到这一点,因为我还没有在 Web 表单中做太多异步操作。由于您在 MVC 中(我有一点异步经验),您可以将您的操作方法返回值包装在 Task&lt;T&gt; 中,例如:public Task&lt;ActionResult&gt; Index()。此外,约定是用后缀-Async 结束async 方法,例如:public static async Task SendMessageAsync(MailMessage message)。这有助于您在非异步的同时拥有异步方法,并让消费者知道他们需要等待结果。
    • 所以,为了使它工作,我基本上必须将async/Task/await 的东西一直链接到我的方法链上?从文档中,看起来 SendAsync 应该可以工作:将指定的电子邮件消息发送到 SMTP 服务器以进行传递。此方法不会阻塞调用线程,并允许调用者将对象传递给操作完成时调用的方法。
    • 由于您的回答非常彻底,请务必处理掉您的客户。
    • @Sam SendAsync 不是您想要使用的。那是为了支持旧形式的异步编程。基于任务的方法更胜一筹,可通过SendMailAsync 访问。是的,您需要将其一直链接到调用堆栈。
    【解决方案2】:

    你应该使用SendMailAsync(),它返回一个Task

    【讨论】:

    • 我自己充实了你的答案,但如果你看看我的 cmets,我们不确定如何在异步上下文中正确处理 SmtpClient,即使它是有必要这样做。你能解释一下吗?
    • @mason:在awaiting SendMailAsync() 之后处理它。
    • 在邮件发出之前客户被处理掉的危险吗?
    • @mason:如果你 await 发送,则不会。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-10
    • 1970-01-01
    • 2014-11-30
    • 2012-03-30
    • 2014-09-12
    • 1970-01-01
    相关资源
    最近更新 更多