I am running a timed background task to send out emails, and in the email I want to include a generated link.
When I send out other emails via user interactions in the controller, I'm using this little method to generate the link:
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}";
}
Of course, a background task does not know anything about the Http context, so I would need to replace the domain-part of the link, like this:
public string BuildUrl(string controller, string action, int id)
{
return aStringPassedInFromSomewhere + $@"/{controller}/{action}/{id}";
}
I'm starting the background task in startup.cs ConfigureServices like this:
services.AddHostedService<ProjectTaskNotifications>();
I was thinking to maybe get the domainname from a resource file, but then I might as well just hard code it into the task method.
Is there some way to pass this information dynamically to the background task?
MORE INFO
Here is the entire background task:
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}";
}
}