当前位置: 首页 > 知识库问答 >
问题:

DBContext系统。ObjectDisposed异常。NET实体框架核心、依赖项注入和线程

赵永新
2023-03-14

我不确定我是否完全正确地处理了这个问题。

背景:我有一个控制器动作GET foo()作为例子。这个foo()需要调用bar(),bar()可能需要很长时间。所以,我需要foo()在bar()完成之前(或无论何时)用“确定”来响应。

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

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

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

然后我打电话给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来获取一些实体,并抛出异常!

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

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

非常感谢一些指导。

使用正在运行的代码进行编辑,但它仍在等待任务。运行以完成,然后返回“确定”(快到了吗?)

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);
    }
}

共有2个答案

锺离嘉茂
2023-03-14

在Asp。net中,注入项的生命周期取决于框架。一旦foo()返回,Asp就不知道您创建的线程仍然需要它提供给您的DbContext,因此Asp会处理该上下文,您就会遇到问题。

您可以自己在线程中创建一个新的DbContext,并决定何时处理它。这并不太好,因为如果您的数据库配置更改,您现在有两个地方可能需要更新。这里有一个例子:

var optionsBuilder = new DbContextOptionsBuilder<MyDBContext>();
optionsBuilder.UseNpgsql(ConnectionString);

using(var dataContext = new MyDBContext(optionsBuilder.Options)){
    //Do stuff here
}

作为第二种选择,Asp。net core还可以使用IHostedService创建后台服务,并在初创企业中注册它们,以及依赖注入。您可以创建一个运行后台任务的后台服务,然后foo()可以将该任务添加到该服务的队列中。这里有一个例子。

祝叶五
2023-03-14

AddDbContext

避免该异常的最简单方法是将IServiceScopeFactory注入控制器,然后使用CreateScope()创建一个新的作用域,并从该作用域请求MyDBContext服务(您不需要担心DbContextOptions)。作业完成后,处理随后处理DbContext的作用域。与其使用线程,不如使用任务<代码>任务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中时,可能会因为应用程序池回收而关闭)

你的服务器记录器。信息(“SendBigFile finished”) 客户端时,code>不会等待。PostAsync已完成。它在任务之后立即记录。运行启动新任务。在客户端之后记录它。PostAsync完成后,您需要放置服务器记录器。信息(“SendBigFile finished”) 紧接着等待GoOffAndSend(someProvider,fileProvider,fileBytes)

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

 类似资料:
  • 如何在.NET核心库项目中将一个类注入另一个类?在API项目中的StartUp类ConfigureServices中,我应该在哪里配置DI?

  • 我有一个ASP。NET核心2.1站点,注册了几个DbContext和一些条件连接字符串。 出于调试目的,我想列出所有注册的<code>DbContext</code>及其连接字符串,以确保所有内容都配置良好。 这只是一个“测试”功能,不用于任何其他用途,因此请不要回答“您不应该这样做”、“它是反模式”等。

  • 复合在这种情况下“消耗”一个子对象数组,从某种意义上说,“吸入”用密钥注册的IFoo的每个实现。这是一个重要的方面:如果您要用一个键注册复合,它将尝试实例化自己,并立即导致StackOverflow异常。 本机DI不支持这些命名注册。 这个例子是从这里提取的

  • 我已经找到了几个这样的帖子,但不能确定我在这里做错了什么。 我试着在两张桌子上各放一个项目。第二项包含对第一项的查找。我可以在没有问题的情况下为父级添加种子,但无法找到正确的语法来为子级添加种子。 下面是我用来种子父级的代码 下面是种子子的代码--这将失败: 根据文档的建议,我使用了一个匿名对象,而不是类本身。我也尝试过同时使用navigation属性、外键和nav道具。ID=1的组肯定已经在db

  • 我已经开始涉足ASP. Net Core,并发现依赖注入是ASP. Net Core框架中的一流公民,它是内置的,可以用于注入各种服务和库。 我想知道他们正在使用哪个依赖注入框架。他们的文档介绍了ASP中的依赖项注入。网芯 ASP。NET Core从一开始就被设计为支持和利用依赖注入。ASP。NET核心应用程序可以通过将内置框架服务注入Startup类中的方法来利用这些服务,应用程序服务也可以配置