Reb*_*cum 10 c# .net-core asp.net-core
我有一个带有 WebAPI 控制器的 ASP.NET Core Web 应用程序。我想要做的就是,在一些控制器中,能够启动一个将在后台运行的进程,但控制器应该在该进程完成之前继续并返回。我不希望服务的消费者不得不等待这项工作完成。
我看过所有关于 IHostedService 和 BackgroundService 的帖子,但似乎没有一个是我想要的。此外,所有这些示例都向您展示了如何设置,但并未实际调用它,或者我不了解其中的一些内容。
我尝试了这些,但是当您在 Startup 中注册 IHostedService 时,它会立即在该时间点运行。这不是我想要的。我不想在启动时运行任务,我希望能够在需要时从控制器调用它。另外,我可能有几个不同的,所以只注册 services.AddHostedService() 将不起作用,因为我可能有一个 MyServiceB 和 MyServiceC,那么我如何从控制器中获得正确的一个(我不能只注入 IHostedService) ?
最终,我所看到的一切都是一大堆复杂的代码,这些代码看起来应该是一件很简单的事情。我错过了什么?
Sab*_*viş 17
据我从您的问题中了解到,您想要创建一个即发即忘的任务,例如登录数据库。在这种情况下,您不必等待日志插入数据库。我还花了很多时间来发现一个易于实施的解决方案。这是我发现的:
在控制器参数中,添加IServiceScopeFactory。这不会影响请求正文或标头。之后创建一个范围并通过它调用您的服务。
[HttpPost]
public IActionResult MoveRecordingToStorage([FromBody] StreamingRequestModel req, [FromServices] IServiceScopeFactory serviceScopeFactory)
{
// Move record to Azure storage in the background
Task.Run(async () =>
{
try
{
using var scope = serviceScopeFactory.CreateScope();
var repository = scope.ServiceProvider.GetRequiredService<ICloudStorage>();
await repository.UploadFileToAzure(req.RecordedPath, key, req.Id, req.RecordCode);
}
catch(Exception e)
{
Console.WriteLine(e);
}
});
return Ok("In progress..");
}
Run Code Online (Sandbox Code Playgroud)
发布请求后,您将立即收到“正在进行中..”文本,但您的任务将在后台运行。
另一件事,如果您不以这种方式创建任务并尝试调用数据库操作,您将收到这样的错误,这意味着您的数据库对象已经死了,并且您正在尝试访问它;
无法访问已处置的对象。导致此错误的一个常见原因是处置从依赖项注入解析的上下文,然后尝试在应用程序的其他位置使用相同的上下文实例。如果您在上下文上调用 Dispose() 或将上下文包装在 using 语句中,则可能会发生这种情况。如果您使用依赖项注入,则应让依赖项注入容器负责处理上下文实例。\r\n对象名称:“DBContext”。
我的代码基于存储库模式。您不应该忘记在Startup.cs中注入服务类
services.AddScoped<ICloudStorage, AzureCloudStorage>();
Run Code Online (Sandbox Code Playgroud)
请在此处查找详细文档。
您有以下选择:
IHostedService类可以是长时间运行的方法,它们在应用程序的整个生命周期中都在后台运行。为了让它们处理某种后台任务,您需要在您的应用程序中实现某种“全局”队列系统,以便控制器存储数据/事件。这个队列系统可以很简单,就像一个Singleton带有ConcurrentQueue的类,你传递给你的控制器,或者像一个IDistributedCache或更复杂的外部发布/订阅系统。然后你可以轮询你的队列IHostedService并基于它运行某些操作。这是IHostedService处理队列的微软实施示例https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio#queued-background -任务
请注意,Singleton类方法可能会导致multi-server环境问题。单例方法的示例实现可以是这样的:// Needs to be registered as a Singleton in your Startup.cs
public class BackgroundJobs {
public ConcurrentQueue<string> BackgroundTasks {get; set;} = new ConcurrentQueue<string>();
}
public class MyController : ControllerBase{
private readonly BackgroundJobs _backgroundJobs;
public MyController(BackgroundJobs backgroundJobs) {
_backgroundJobs = backgroundJobs;
}
public async Task<ActionResult> FireAndForgetEndPoint(){
_backgroundJobs.BackgroundTasks.Enqueue("SomeJobIdentifier");
}
}
public class MyBackgroundService : IHostedService {
private readonly BackgroundJobs _backgroundJobs;
public MyBackgroundService(BackgroundJobs backgroundJobs)
{
_backgroundJobs = backgroundJobs;
}
public void StartAsync(CancellationToken ct)
{
while(!ct.IsCancellationRequested)
{
if(_backgroundJobs.BackgroundTasks.TryDequeue(out var jobId))
{
// Code to do long running operation
}
Task.Delay(TimeSpan.FromSeconds(1)); // You really don't want an infinite loop here without having any sort of delays.
}
}
}
Run Code Online (Sandbox Code Playgroud)
Task传递IServiceProvider给该方法并在其中创建一个新的 Scope 以确保 ASP.NET 在控制器 Action 完成时不会终止该任务。就像是IServiceProvider _serviceProvider;
public async Task<ActionResult> FireAndForgetEndPoint()
{
// Do stuff
_ = FireAndForgetOperation(_serviceProvider);
Return Ok();
}
public async Task FireAndForgetOperation(IServiceProvider serviceProvider)
{
using (var scope = _serviceProvider.CreateScope()){
await Task.Delay(1000);
//... Long running tasks
}
}
Run Code Online (Sandbox Code Playgroud)
更新:这是微软做类似事情的例子:https : //docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-3.1#do-not-capture-服务注入到后台线程中的控制器
从 .NET Core 中的控制器运行单个后台任务的最简单方法是什么?
我不希望服务的使用者必须等待这项工作完成。
最终,我所看到的一切都是一堆巨大而复杂的代码,而这些代码看起来应该是一件很简单的事情。我缺少什么?
问题在于 ASP.NET 是一个用于编写 Web 服务的框架,Web 服务是响应请求的应用程序。但是,一旦您的代码说“我不希望服务的使用者必须等待”,那么您就在谈论在请求之外运行代码(即请求外部代码)。这就是为什么所有解决方案都很复杂:您的代码必须绕过/扩展框架本身,以试图强制它执行其未设计的操作。
对于请求外部代码,唯一正确的 解决方案是拥有一个带有单独后台进程的持久队列。任何正在进行的事情(例如,带有)都会有可靠性问题;特别是,这些解决方案有时会失效。ConcurrentQueueIHostedService
| 归档时间: |
|
| 查看次数: |
5721 次 |
| 最近记录: |