Flo*_*gex 5 c# asp.net-core asp.net-core-hosted-services
用户可以通过向 ASP.NET Core 控制器发送请求来触发长时间运行的作业。当前,控制器执行作业,然后发送 200 OK 响应。问题是客户端必须等待相当长的时间才能得到响应。
这就是我目前尝试在后台任务中处理作业的原因。我正在使用一个IBackgroundTaskQueue存储所有作业的地方,并且IHostedService在有新作业入队时处理作业。它类似于Microsoft 文档中的代码。
但该作业确实需要访问数据库,因此用户必须使用 Active Directory 进行身份验证。因此,我需要访问HttpContext.User后台任务中的属性。不幸的是,在HttpContext发送响应时和作业处理开始之前,它会被处理掉。
public class Job
{
public Job(string message)
{
Message = message;
}
public string Message { get; }
}
Run Code Online (Sandbox Code Playgroud)
控制器将新作业排入任务队列。
[HttpGet]
public IActionResult EnqueueJob()
{
var job = new Job("Hello World");
this.taskQueue.QueueBackgroundWorkItem(job);
return Accepted();
}
Run Code Online (Sandbox Code Playgroud)
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private ConcurrentQueue<Job> jobs = new ConcurrentQueue<Job>();
private SemaphoreSlim signal = new SemaphoreSlim(0);
public void QueueBackgroundWorkItem(Job job)
{
jobs.Enqueue(job);
signal.Release();
}
public async Task<Job> DequeueAsync(CancellationToken cancellationToken)
{
await signal.WaitAsync(cancellationToken);
jobs.TryDequeue(out var job);
return job;
}
}
Run Code Online (Sandbox Code Playgroud)
将IHostedService创建一个新JobRunner的它会转移每个作业。我正在使用IServiceScopeFactoryhere 来提供依赖注入。JobRunner在实际代码中也有更多的依赖。
public class JobRunnerService : BackgroundService
{
private readonly IServiceScopeFactory serviceScopeFactory;
private readonly IBackgroundTaskQueue taskQueue;
public JobRunnerService(IServiceScopeFactory serviceScopeFactory, IBackgroundTaskQueue taskQueue)
{
this.serviceScopeFactory = serviceScopeFactory;
this.taskQueue = taskQueue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (stoppingToken.IsCancellationRequested == false)
{
var job = await taskQueue.DequeueAsync(stoppingToken);
using (var scope = serviceScopeFactory.CreateScope())
{
var serviceProvider = scope.ServiceProvider;
var runner = serviceProvider.GetRequiredService<JobRunner>();
runner.Run(job);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
public class JobRunner
{
private readonly ILogger<JobRunner> logger;
private readonly IIdentityProvider identityProvider;
public JobRunner(ILogger<JobRunner> logger, IIdentityProvider identityProvider)
{
this.logger = logger;
this.identityProvider= identityProvider;
}
public void Run(Job job)
{
var principal = identityProvider.GetUserName();
logger.LogInformation($"{principal} started a new job. Message: {job.Message}");
}
}
Run Code Online (Sandbox Code Playgroud)
public class IdentityProvider : IIdentityProvider
{
private readonly IHttpContextAccessor httpContextAccessor;
public IdentityProvider(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public string GetUserName()
=> httpContextAccessor.HttpContext.User.Identity.Name; // throws NullReferenceException
}
Run Code Online (Sandbox Code Playgroud)
现在,当发送请求时, aNullReferenceException被抛出,JobRunner.Run()因为它httpContextAccessor.HttpContext是空的。
我还没有一个好主意如何解决这个问题。我知道可以从 复制必要的信息HttpContext,但不知道如何将它们提供给依赖注入服务。
我想也许我可以创建一个IServiceProvider使用旧服务的新服务,但替换 的实现IHttpContextAccesor,但似乎不可能。
HttpContext尽管响应已完成,我如何在后台任务中使用?
| 归档时间: |
|
| 查看次数: |
883 次 |
| 最近记录: |