Код: Выделить всё
IBackgroundTaskQueue
Код: Выделить всё
namespace My.Namespace.BackgroundTaskQueue
{
public interface IBackgroundTaskQueue
{
ValueTask QueueBackgroundWorkItemAsync(Func workItem);
ValueTask DequeueAsync(CancellationToken cancellationToken);
}
}
< /code>
BackgroundTaskQueue
Код: Выделить всё
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;
}
}
}
< /code>
BackgroundTaskQueueHostedService
Код: Выделить всё
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>
The plan is to then instantiate this class / queue in startup.cs
Код: Выделить всё
CustomControllerService
Код: Выделить всё
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 synchronous 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>
The controller service (simplified for brevity) will do some things to the incoming request, then proceed with processing the awaited tasks, followed by enqueueing an async function that contains some unawaited tasks into the BackgroundTaskQueue
Подробнее здесь: https://stackoverflow.com/questions/795 ... nd-service