Вы можете создать фоновую службу в очереди на основе IHostedService
. Затем вы добавляете элемент в очередь, когда пользователь регистрируется и обрабатывает эту очередь через фоновую службу. Когда вы вытаскиваете элементы из очереди, вы проверяете, готовы ли они к отправке, в зависимости от времени. Если это так, вы попали в конечную точку send-greeting
, в противном случае вы потребуете предмет. документы предоставляют образец такой услуги.
Очередь:
public interface IBackgroundTaskQueue
{
void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem);
Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken);
}
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private ConcurrentQueue<Func<CancellationToken, Task>> _workItems =
new ConcurrentQueue<Func<CancellationToken, Task>>();
private SemaphoreSlim _signal = new SemaphoreSlim(0);
public void QueueBackgroundWorkItem(
Func<CancellationToken, Task> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
_workItems.Enqueue(workItem);
_signal.Release();
}
public async Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken)
{
await _signal.WaitAsync(cancellationToken);
_workItems.TryDequeue(out var workItem);
return workItem;
}
}
И размещенный сервис:
public class QueuedHostedService : BackgroundService
{
private readonly ILogger _logger;
public QueuedHostedService(IBackgroundTaskQueue taskQueue,
ILoggerFactory loggerFactory)
{
TaskQueue = taskQueue;
_logger = loggerFactory.CreateLogger<QueuedHostedService>();
}
public IBackgroundTaskQueue TaskQueue { get; }
protected async override Task ExecuteAsync(
CancellationToken cancellationToken)
{
_logger.LogInformation("Queued Hosted Service is starting.");
while (!cancellationToken.IsCancellationRequested)
{
var workItem = await TaskQueue.DequeueAsync(cancellationToken);
try
{
await workItem(cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex,
$"Error occurred executing {nameof(workItem)}.");
}
}
_logger.LogInformation("Queued Hosted Service is stopping.");
}
}
Этот код прямо из документации. Он в основном поддерживает ваш сценарий использования, но требует нескольких настроек, чтобы вы могли пройти весь путь туда. Во-первых, поскольку есть компонент времени (т. Е. Вы хотите обрабатывать элемент в очереди только, если ему 15 минут), вам нужно сделать параметр типа ConcurrentQueue<T>
тем, что вы можете кодировать как в datetime, так и в func , Это может быть ValueTuple
или реальный объект, созданный специально для этой цели: решать только вам. Например:
ConcurrentQueue<(DateTimeOffset added, Func<CancellationToken, Task> task)>
Затем вам нужно изменить логику удаления, чтобы переместить ее в очередь, если не прошло достаточно времени:
public async Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken)
{
await _signal.WaitAsync(cancellationToken);
_workItems.TryDequeue(out var workItem);
if (DateTimeOffset.UtcNow.AddMinutes(-15) < workItem.added)
{
_workItems.Enqueue(workItem);
return ct => ct.IsCancellationRequested ? Task.FromCanceled(ct) : Task.CompletedTask;;
}
return workItem;
}
Возвращение туда, когда еще не время, это просто фиктивная лямбда для удовлетворения ограничения. Вы можете вместо этого вернуть что-то вроде null, но тогда вам также потребуется изменить метод ExecuteAsync
фоновой службы, чтобы добавить проверку на ноль перед обработкой функции.
Стоит также отметить, что пример кода разработан как универсальный и позволяет ставить в очередь что угодно для обработки. В результате, из-за требуемых изменений времени, вы должны использовать более конкретные имена: ITimedBackgroundTaskQueue
, TimedBackgroundTaskQueue
и TimedQueuedHostedService
, например. Это особенно верно в свете того факта, что примеры интерфейсов / классов из документов будут фактически интегрированы в ASP.NET Core 3.0.