Код: Выделить всё
namespace My.Namespace.BackgroundTaskQueue
{
public interface IBackgroundTaskQueue
{
ValueTask QueueBackgroundWorkItemAsync(Func workItem);
ValueTask DequeueAsync(CancellationToken cancellationToken);
}
}
Код: Выделить всё
namespace My.Namespace.BackgroundTaskQueue
{
using System.Threading.Channels;
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private readonly Channel _queue;
public BackgroundTaskQueue(int capacity) {
var options = new BoundedChannelOptions(capacity) {
FullMode = BoundedChannelFullMode.Wait
};
_queue = Channel.CreateBounded(options);
}
public async ValueTask QueueBackgroundWorkItemAsync(Func workItem) {
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
await _queue.Writer.WriteAsync(workItem);
}
public async ValueTask DequeueAsync(CancellationToken cancellationToken) {
var workItem = await _queue.Reader.ReadAsync(cancellationToken);
return workItem;
}
}
}
namespace My.Namespace.BackgroundTaskQueue
{
using Microsoft.Extensions.Hosting;
public class BackgroundTaskQueueHostedService : BackgroundService
{
public IBackgroundTaskQueue TaskQueue { get; }
public BackgroundTaskQueueHostedService(IBackgroundTaskQueue taskQueue)
{
TaskQueue = taskQueue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await BackgroundProcessing(stoppingToken);
}
private async Task BackgroundProcessing(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem =
await TaskQueue.DequeueAsync(stoppingToken);
try
{
await workItem(stoppingToken);
}
catch (Exception ex)
{
// Error Handling
}
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
await base.StopAsync(stoppingToken);
}
}
}
< /code>
План состоит в том, чтобы затем создать экземпляр этого класса /очередь в startup.cs, и мои контроллеры включают асинхронную функцию, которая содержит все задачи, которые я хочу обрабатывать в фоновом режиме. Проблема заключается в том, что эти фоновые задачи полагаются на различные зависимости от обслуживания в одиночном возрасте, которые вводят в контроллеры. Но я не хочу вводить все синглтонские зависимости в фоновый сервис, поскольку цель состоит в том, чтобы иметь его достаточно общего для использования с любым из 40-50 репо, без создания специализированных реализаций класса фоновых услуг для каждого.namespace My.Repo.Namespace.BL.ControllerServices.Custom
{
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using My.Namespace.BackgroundTaskQueue;
using My.Repo.Other.Namespace.Services;
public CustomControllerService : ICustomControllerService
{
private readonly IBackgroundTaskQueue _backgroundTaskQueue;
private readonly CancellationToken _ct;
private readonly IService1 _service1;
private readonly IService2 _service2;
public CustomControllerService(IBackgroundTaskQueue backgroundTaskQueue,
IHostApplicationLifetime applicationLifetime,
IService1 service1,
IService2 service2) {
_backgroundTaskQueue = backgroundTaskQueue;
_ct = applicationLifetime.ApplicationStopping;
_service1 = service1;
_service2 = service2;
}
#region [Http Request Execution]
public async Task ProcessRequest(HttpRequest httpRequest) {
ApiRequestContext apiRequestContext = new ApiRequestContext();
CustomRequestBody customRequestBody;
try {
// Ingest the event and deserialize it to a data object dedicated to this endpoint
customRequestBody = await DeserializeRequestBody(httpRequest, apiRequestContext);
if (apiRequestContext?.ApiResponse?.HttpStatusCode == 200) {
// Deserialization successful, process data
await ProcessRequestData(customRequestBody, apiRequestContext);
apiRequestContext.ApiResponse.Content = CreateResponseBody(apiRequestContext);
}
} catch (Exception ex){
} finally {
}
// Return the current ApiResponse object contained in the ApiRequestContext
return apiRequestContext.ApiResponse;
}
#endregion
#region [Private Methods]
private async Task DeserializeRequestBody(HttpRequest httpRequest, ApiRequestContext apiRequestContext) {
// Logic to deserialize request body into my custom object
return new CustomRequestBody;
}
private CustomResponseBody CreateResponseBody(ApiRequestContext apiRequestContext) {
// Some logic to build API response
return new CustomResponseBody;
}
private async Task ProcessRequestData(CustomRequestBody customRequestBody, ApiRequestContext apiRequestContext) {
try {
await ProcessAwaitedTasks(customRequestBody, apiRequestContext);
await _backgroundTaskQueue.QueueBackgroundWorkItemAsync(
_ct => ProcessUnawaitedTasks(customRequestBody, _ct)
);
} catch (Exception ex) {
} finally {
}
}
private async Task ProcessAwaitedTasks(CustomRequestBody customRequestBody, ApiRequestContext apiRequestContext) {
try {
// Some synchronoous sequence of async / await Tasks
} catch (Exception e) {
} finally {
}
}
private async ValueTask ProcessUnawaitedTasks(CustomRequestBody customRequestBody, CancellationToken ct) {
try {
// Some background async Tasks that I need executed, but not awaited for the api response
_ = _service1.Function1(customRequestBody);
_ = _service2.Function2(customRequestBody);
} catch (Exception e) {
} finally {
}
}
#endregion
}
}
< /code>
Служба контроллера (упрощенная для краткости) сделает некоторые вещи с входящим запросом, а затем выполняет обработку ожидаемых задач, а затем внедрение асинхронной функции, которая содержит некоторые неотъемлемые задачи в фоновую каркасную кожу (канал), прежде чем немедленно возвращать ответ API. Но эти неприемлемые задачи полагаются на зависимости в службе контроллера, таких как iService1 и iService2. Я не хочу напрямую вводить эти зависимости в фоновый класс службы. В этой ситуации я видел обширную документацию о службах обселений, но я ничего не могу найти в управлении синглтонами. Спасибо!
Подробнее здесь: https://stackoverflow.com/questions/795 ... nd-service