【发布时间】:2019-08-17 08:30:51
【问题描述】:
我正在运行定时后台任务来发送电子邮件,并且我想在电子邮件中包含生成的链接。
当我通过控制器中的用户交互发送其他电子邮件时,我正在使用这个小方法来生成链接:
public string BuildUrl(string controller, string action, int id)
{
Uri domain = new Uri(Request.GetDisplayUrl());
return domain.Host + (domain.IsDefaultPort ? "" : ":" + domain.Port) +
$@"/{controller}/{action}/{id}";
}
当然,后台任务对 Http 上下文一无所知,所以我需要替换链接的域部分,如下所示:
public string BuildUrl(string controller, string action, int id)
{
return aStringPassedInFromSomewhere + $@"/{controller}/{action}/{id}";
}
我在 startup.cs ConfigureServices 中像这样启动后台任务:
services.AddHostedService<ProjectTaskNotifications>();
我在想也许可以从资源文件中获取域名,但我不妨将它硬编码到任务方法中。
有没有办法将这些信息动态传递给后台任务?
更多信息
这是整个后台任务:
internal class ProjectTaskNotifications : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
private readonly IServiceScopeFactory scopeFactory;
private readonly IMapper auto;
public ProjectTaskNotifications(
ILogger<ProjectTaskNotifications> logger,
IServiceScopeFactory scopeFactory,
IMapper mapper)
{
_logger = logger;
this.scopeFactory = scopeFactory;
auto = mapper;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(30));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Timed Background Service is working.");
// Connect to the database and cycle through all unsent
// notifications, checking if some of them are due to be sent:
using (var scope = scopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<MyDbContext>();
List<ProjectTaskNotification> notifications = db.ProjectTaskNotifications
.Include(t => t.Task)
.ThenInclude(o => o.TaskOwner)
.Include(t => t.Task)
.ThenInclude(p => p.Project)
.ThenInclude(o => o.ProjectOwner)
.Where(s => !s.IsSent).ToList();
foreach (var notification in notifications)
{
if (DateTime.UtcNow > notification.Task.DueDate
.AddMinutes(-notification.TimeBefore.TotalMinutes))
{
SendEmail(notification);
notification.Sent = DateTime.UtcNow;
notification.IsSent = true;
}
}
db.UpdateRange(notifications);
db.SaveChanges();
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
public void SendEmail(ProjectTaskNotification notification)
{ // Trimmed down for brevity
// Key parts
string toAddr = notification.Task.TaskOwner.Email1;
BodyBuilder bodyBuilder = new BodyBuilder
{
HtmlBody = TaskInfo(auto.Map<ProjectTaskViewModel>(notification.Task))
};
return;
}
public string TaskInfo(ProjectTaskViewModel task)
{ // Trimmed down for brevity
return $@"<p>{BuildUrl("ProjectTasks", "Edit", task.Id)}</p>";
}
public string BuildUrl(string controller, string action, int id)
{
// This is where I need the domain name sent in from somewhere:
return "domain:port" + $@"/{controller}/{action}/{id}";
}
}
【问题讨论】:
-
这个答案可以帮助你:stackoverflow.com/questions/49813628/…
-
您的后台任务是如何工作的,它从哪里获取应该发送的电子邮件?
-
@Fabio 查看更新的问题。我添加了后台任务类以显示我如何确定要发送哪些电子邮件。
-
为什么不能在数据库的通知中保存域 URL。那么您将能够使用任何域 url 发送电子邮件。
-
将“通知”记录视为包含要发送的电子邮件信息的事件。后台任务(或事件处理程序)不应该关心如何构建所需的数据,所有数据都准备好并保存在事件中。事件处理程序(后台任务)只会处理它。
标签: c# asp.net-core asp.net-core-mvc