我正在使用ASP.NET上的REST API。我想知道哪个是独立于将响应发送到客户端的后台工作的最佳方式。基本上,我有一个PUT方法,我必须发送几封电子邮件。假设他们是10封电子邮件。我将恢复所有方法,使一切更清晰:
我尝试了以下方法,并且在发送电子邮件之后发送了响应的两种方式,并且它们花了相同的时间:
方式1:
[EnableCors(origins: "*", headers: "*", methods: "*", SupportsCredentials = true)]
public async Task<HttpResponseMessage> Put(string id, [FromBody]InformeModel informe)
{
if (CookieManager.ValidarCookie(Request.Headers.GetCookies("mb-session").FirstOrDefault()) == EstadoCookie.VALIDA)
{
await SendMails()
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
private async Task SendMails()
{
await Task.Run(() => {
foreach (string m in mails)
{
Mail mail = new Mail("test@test.com", 25, "test@test.com.ar", "myPass");
mail.SendMailHTML(m, "Email Title", "This is the Email");
}
});
}
方式2:
// PUT: api/Informes/5
[EnableCors(origins: "*", headers: "*", methods: "*", SupportsCredentials = true)]
public HttpResponseMessage Put(string id, [FromBody]InformeModel informe)
{
if (CookieManager.ValidarCookie(Request.Headers.GetCookies("mb-session").FirstOrDefault()) == EstadoCookie.VALIDA)
{
SendMails()
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
private async void SendMails()
{
await Task.Run(() => {
foreach (string m in mails)
{
Mail mail = new Mail("test@test.com", 25, "test@test.com.ar", "myPass");
mail.SendMailHTML(m, "Email Title", "This is the Email");
}
});
}
将响应发送给客户端的最佳方法是什么,而不必等待所有电子邮件都已发送?我必须使用ThreadPool吗?
答案 0 :(得分:4)
我会更进一步解耦并使用类似于域事件的通用事件。
如果控制器可以完成其工作并返回响应,无论电子邮件是否已发送,强烈建议发送电子邮件超出控制器的责任范围。 (特别是通过电子邮件,控制器无法确定是否已发送电子邮件。)
在那种情况下,我会使用某种事件总线。一些示例使用静态实例,或者它可能是您注入控制器的内容。
然后,控制器不是发送电子邮件,而是提出一个事件,比如
_eventBus.Raise(new SomethingHappenedEvent(argsThatIncludeRelevantInfo args));
然后你有一个事件处理程序(你甚至可以有多个),一个事件处理程序发送电子邮件,包括使它成为async
并处理任何异常。
这使得单元测试变得更加容易。您无需测试控制器是否已发送电子邮件。您可以模拟事件总线并测试事件是否已引发。然后可以单独测试事件处理程序。
Here is an article on domain events在很长一段时间内引用了很多。我确信那里有更多的最新信息。
有关这些事件的大量信息是在域驱动开发的背景下,但无论您是否在进行DDD,原则和机制都适用。您的类与其方法产生的其他东西分离,但它本身不应该知道或关心 - 比如返回响应的控制器,副作用是发送电子邮件。
Here's an event bus implementation我做了一会儿。我认为我不包括异步处理,但是当你想要发射并忘记时,这可能是有意义的。这是我应该重新审视的事情。
正如评论中所提到的那样,“即发即忘”并不意味着事件的结果是微不足道的。为了确保发生某些事情,事件处理程序将请求放入队列而不是自己完成工作可能是有意义的。这使得它更耐用,因此您可以确保处理队列中的任何内容都得到处理。
答案 1 :(得分:1)
如果您不想等待发送电子邮件(或任何相关错误),您应该进行“开火”并忘记&#34;。解决方案的稳健性:
使用HangFire,强大的即发即弃lib
HostingEnvironment.QueueBackgroundWorkItem - 至少尝试以阻止应用池在发布回收时杀死您的电子邮件
Unawaited Task.Run,继续进行错误管理:
例如:
Task.Run(async () => await ..send emails.)
.ContinueWith(t => logError(t.Exception.GetBaseException()),
TaskContinuationOptions.OnlyOnFaulted);