gpt4 book ai didi

c# - .NET Entity Framework Core、依赖注入(inject)和线程的 DBContext System.ObjectDisposed 异常

转载 作者:行者123 更新时间:2023-11-30 20:15:26 25 4
gpt4 key购买 nike

我不确定我是否完全正确地处理了这件事。

背景:我有一个 Controller 操作 GET foo() 作为示例。这个 foo() 需要去调用 bar(),而 bar() 可能需要很长时间。因此,我需要 foo() 在 bar() 完成之前(或无论何时)以“OK”响应

稍微有点复杂的是 bar() 需要访问 DBContext 并从数据库中获取一些数据。在我当前的实现中,当我尝试通过 bar 访问数据库时,出现“DBContext System.ObjectDisposed”异常。请知道为什么以及如何解决这个问题?我对线程和任务真的很陌生,所以我可能完全错了!

我使用依赖注入(inject)在启动时提供数据库上下文

     services.AddEntityFrameworkNpgsql()
.AddDbContext<MyDBContext>()
.BuildServiceProvider();

然后我调用 foo(),foo() 又使用新线程调用 bar()(也许我做错了?):

    public async Task<string> foo(string msg)
{

Thread x = new Thread(async () =>
{
await bar(msg);
});

x.IsBackground = true;
x.Start();

return "OK.";
}

所以 bar 立即尝试访问 DBContext 以获取一些实体,但它抛出了异常!

未处理的异常:System.ObjectDisposedException:无法访问已处置的对象。此错误的一个常见原因是处理从依赖项注入(inject)解析的上下文,然后尝试在应用程序的其他地方使用相同的上下文实例。如果您在上下文中调用 Dispose() 或将上下文包装在 using 语句中,则可能会发生这种情况。如果您正在使用依赖注入(inject),则应该让依赖注入(inject)容器负责处理上下文实例。对象名称:“MyDBContext”。

如果我将 bar() 从线程中取出,这很好,但当然“OK”不会返回,直到 bar 完成其非常长的过程,这是我需要解决的问题。

非常感谢您的一些指导。

使用运行代码进行编辑,但它仍在等待 Task.Run 完成,然后返回“确定”。 (快到了?)

public async Task<string> SendBigFile(byte[] fileBytes)
{
ServerLogger.Info("SendBigFile called.");

var task = Task.Run(async () =>
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var someProvider = scope.ServiceProvider.GetService<ISomeProvider>();
var fileProvider = scope.ServiceProvider.GetService<IFileProvider>();

await GoOffAndSend(someProvider, fileProvider, fileBytes);
}
});

ServerLogger.Info("Hello World, this should print and not wait for Task.Run."); //Unfortunately this is waiting even though GoOffAndSend takes at least 1 minute.

return "Ok"; //This is not returned until Task.Run finishes unfortunately... how do I "skip" to this immediately?
}

private async Task GoOffAndSend(ISomeProvider s, IFileProvider f, byte[] bytes)
{
// Some really long task that can take up to 1 minute that involves finding the file, doing weird stuff to it and then
using (var client = new HttpClient())
{
var response = await client.PostAsync("http://abcdef/somewhere", someContent);
}
}

最佳答案

AddDbContext<>()注册MyDBContext作为 ServiceLifetime.Scoped 的服务这意味着你的 DbContext是根据网络请求创建的。它在请求完成时被释放。

避免该异常的最简单方法是注入(inject) IServiceScopeFactory进入你的 Controller ,然后使用 CreateScope() 创建一个新的范围并请求 MyDBContext该范围内的服务(您无需担心 DbContextOptions )。工作完成后,处理随后处理的范围 DbContext .而不是 Thread最好使用 Task . Task API 更强大,通常具有更好的性能。它看起来像这样:

public class ValuesController : ControllerBase
{
IServiceScopeFactory _serviceScopeFactory
public ValuesController(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public async Task<string> foo(string msg)
{
var task = Task.Run(async () =>
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetService<MyDBContext>();
await bar(db, msg);
}

});
// you may wait or not when task completes
return "OK.";
}
}

你还应该知道 Asp.Net不是后台任务的最佳位置(例如,当应用程序托管在 IIS 中时,它可能会因为应用程序池回收而关闭)

更新

你的 ServerLogger.Info("SendBigFile finished.");client.PostAsync 时不等待完成了。它在 Task.Run 之后立即记录开始了新的任务。在 client.PostAsync 之后记录完成后你需要输入ServerLogger.Info("SendBigFile finished.");await GoOffAndSend(someProvider, fileProvider, fileBytes);之后:

...
await GoOffAndSend(someProvider, fileProvider, fileBytes);
ServerLogger.Info("SendBigFile finished.");
...

关于c# - .NET Entity Framework Core、依赖注入(inject)和线程的 DBContext System.ObjectDisposed 异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54597324/

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