gpt4 book ai didi

c# - 无法访问 Task.Run 中已处置的对象

转载 作者:行者123 更新时间:2023-12-02 16:43:20 24 4
gpt4 key购买 nike

我正在使用 .NET Core 3.1。我想运行一些后台处理而无需用户等待它完成(大约需要 1 分钟)。因此,我使用了 Task.Run像这样:

public class MyController : Controller
{
private readonly IMyService _myService;

public MyController(IMyService myService)
{
_myService = myService;
}

public async Task<IActionResult> Create(...)
{
await _myService.CreatePostAsync(...);
return View();
}
}

public class MyService : IMyService
{
private readonly MyDbContext _dbContext;
private readonly IServiceScopeFactory _scopeFactory;

public MyService(MyDbContext dbContext, IServiceScopeFactory scopeFactory)
{
_dbContext = dbContext;
_scopeFactory = scopeFactory;
}

public async Task CreatePostAsync(Post post)
{
...
string username = GetUsername();
DbContextOptions<MyDbContext> dbOptions = GetDbOptions();
Task.Run(() => SaveFiles(username, dbOptions, _scopeFactory));
}

private void SaveFiles(string username, DbContextOptions<MyDbContext> dbOptions, IServiceScopeFactory scopeFactory)
{
using (var scope = scopeFactory.CreateScope())
{
var otherService = scope.ServiceProvider.GetRequiredService<IOtherService>();
var cntxt = new MyDbContext(dbOptions, username);

Post post = new Post("abc", username);

cntxt.Post.Add(post); <----- EXCEPTION

cntxt.SaveChanges();
}
}
}

我在标记行中收到以下异常:

System.ObjectDisposedException: 'Cannot access a disposed object. Object name: 'IServiceProvider'.'

为什么会这样?我为 MyDbContext 使用了自定义构造函数(而不是 scope.ServiceProvider.GetRequiredService<MyDbContext>() ),因为我需要保存一个额外的属性(用户名)以供以后在覆盖方法中使用。

public partial class MyDbContext
{
private string _username;
private readonly DbContextOptions<MyDbContext> _options;

public DbContextOptions<MyDbContext> DbOptions { get { return _options; } }

public MyDbContext(DbContextOptions<MyDbContext> options, string username) : base(options)
{
_username = username;
_options = options;
}

... other overriden methods
}

我做错了什么?

最佳答案

首先,不要在您的服务中隐藏线程池操作;让调用代码决定是否在线程池上运行操作:

当您使用依赖注入(inject)时,框架会在 HTTP 请求结束时处理您的 DbContext

您需要将服务范围工厂注入(inject) Controller ,并从那里请求服务:

public class MyController : Controller
{
private readonly IMyService _myService;
private readonly IServiceScopeFactory _scopeFactory;

public MyController(IMyService myService, IServiceScopeFactory scopeFactory)
{
_myService = myService;
_scopeFactory = scopeFactory;
}

public async Task<IActionResult> Create(...)
{
HostingEnvironment.QueueBackgroundWorkItem(SaveInBackground);
return View();
}

private async Task SaveInBackground(CancellationToken ct)
{
using (var scope = scopeFactory.CreateScope())
{
var scopedService = scope.ServiceProvider.GetRequiredService<IMyService>();
await scopedService.CreatePostAsync(...);
}
}
}

HostingEnvironment.QueueBackgroundWorkItem 的工作方式与 Task.Run 类似,只是它确保应用程序在所有后台工作项目完成之前不会关闭。

您的服务需要像这样:

public class MyService : IMyService
{
private readonly MyDbContext _dbContext;

public MyService(MyDbContext dbContext)
{
_dbContext = dbContext;
}

public async Task CreatePostAsync(Post post)
{
_dbContext.Post.Add(post);

await _dbContext.SaveChangesAsync();
}
}

更新

将附加参数传递给 SaveInBackground:

private async Task SaveInBackground(YourParam param)

然后这样调用:

HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => SaveInBackground(yourParam));

关于c# - 无法访问 Task.Run 中已处置的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61115769/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com