首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在ASP.NET核心中请求结束后使用(注入DI) DbContext

在ASP.NET核心中请求结束后使用(注入DI) DbContext
EN

Stack Overflow用户
提问于 2020-12-22 08:46:05
回答 2查看 74关注 0票数 1

对于我的ASP.NET核心MVC应用程序,我需要一个处理数据的控制器操作。这个过程需要一些时间,所以我不想阻塞请求。在控制器操作中,我宁愿启动一个后台工作进程,并立即结束请求,告诉用户处理正在进行中。然后使用第二个控制器动作来访问处理后的数据。

在后台工作程序中,我需要访问DbContext以将处理后的数据存储在我的数据库中。(或通过依赖注入注入的任何其他服务。)我发现通过IServiceScopeFactory创建一个新的独立于请求的作用域是可行的,这反过来又给了我一个ServiceProvider

代码语言:javascript
复制
public class ProcessingController : Controller
{
    private readonly IServiceScopeFactory mServiceProvider;

    public HomeController(IServiceScopeFactory serviceProvider)
    {
        mServiceProvider = serviceProvider;
    }
    public IActionResult BeginProcessing(int id)
    {
        var longRunningScope = mServiceProvider.CreateScope();
        var _ = Task.Run(() => {
            try {
                var context = longRunningScope.ServiceProvider.GetRequiredService<DbContext>();
                var workItem = context.Items.First(i => i.Id == id) 
                ...
            }
            finally {
                longRunningScope.Dispose();
            }
        });
        return Ok();
    }
}

有没有更好的(更像ASP.NET-Core风格的)方法来做到这一点?请注意,我的“长时间运行”操作只需要2-5秒,并且需要同时处理多个用户。不需要按顺序处理请求的后台线程。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-12-23 14:57:50

我认为hosted services不能很好地满足我的需求,因为它需要显式地实现和注入,这使得向它传递数据变得很困难。

这是一个更灵活、更易于使用的解决方案ScopedBackgroundTaskRunner,它在侦听shutdown事件的同时,在自己的任务和作用域中运行操作。该操作接收相应的取消令牌以及作用域ServiceProvider,以获得任何所需的服务。

代码语言:javascript
复制
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace WebApplication2.Support
{
    /// <summary>
    /// Service class that executes tasks which run in their own thread with their own scope
    /// and can thus continue executing after the web request ended.
    /// </summary>
    /// <remarks>
    /// Register via:
    /// services.AddTransient<ScopedBackgroundTaskRunner>();
    /// </remarks>
    public class ScopedBackgroundTaskRunner
    {
        private readonly ILogger<ScopedBackgroundTaskRunner> mLogger;
        private CancellationTokenSource mStoppingCts;
        private IServiceProvider mServiceProvider;

        public ScopedBackgroundTaskRunner(IServiceProvider services,
            ILogger<ScopedBackgroundTaskRunner> logger,
            IHostApplicationLifetime lifetime)
        {
            mServiceProvider = services;
            mLogger = logger;
            lifetime.ApplicationStopping.Register(OnAppStopping);
        }

        private void OnAppStopping()
        {
            if (mStoppingCts != null)
            {
                mLogger.LogDebug($"Cancel due to app shutdown");
                mStoppingCts.Cancel();
            }
        }

        public void Execute(Action<IServiceProvider, CancellationToken> action, CancellationToken stoppingToken) {
            Execute(action, "<unnamed>", stoppingToken);
        }
        public void Execute(Action<IServiceProvider, CancellationToken> action, string actionName, CancellationToken stoppingToken)
        {
            mStoppingCts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
            var scope = mServiceProvider.CreateScope();
            var _ = Task.Run(() => {
                mLogger.LogTrace($"Executing action '{actionName}' on thread {Thread.CurrentThread.ManagedThreadId}...");
                try
                {
                    action.Invoke(scope.ServiceProvider, mStoppingCts.Token);
                }
                finally {
                    mLogger.LogTrace($"Action '{actionName}' {(mStoppingCts.IsCancellationRequested ? "canceled" : "finished")}" +
                        $" on thread {Thread.CurrentThread.ManagedThreadId}");
                    scope.Dispose();
                    var mStoppingCtsCopy = mStoppingCts;
                    mStoppingCts = null;
                    mStoppingCtsCopy.Dispose();
                }
            }, mStoppingCts.Token);
        }
    }
}
票数 0
EN

Stack Overflow用户

发布于 2020-12-22 09:18:05

考虑将您的后台任务实现为hosted service

使用这种方法,后台服务的生命周期是受管理的(如果宿主环境正在关闭,运行时将通过CancellationToken请求取消,而在现有代码中,您的任务不会被礼貌地停止)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65401889

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档