.net core .net临时数据库上下文被过早释放

.net core .net临时数据库上下文被过早释放,.net-core,dependency-injection,asp.net-core-mvc,entity-framework-core,.net Core,Dependency Injection,Asp.net Core Mvc,Entity Framework Core,我正在将使用EF6的asp.net mvc5应用程序移动到使用EF core的asp.net core MVC 3.0 在我的mvc5应用程序中,我有一些修改数据库的管理操作,需要很长时间,因此我在创建一个与请求上下文无关的新DBContext时使用了一种模式,然后使用task.run在后台运行任务。这几年来一直运作良好 在转换到.NETCore时,不清楚如何以我在旧代码库中的方式创建新的DBContext。在这些情况下,我似乎应该能够创建一个临时DBContext,一切都应该很好 因此,我创建

我正在将使用EF6的asp.net mvc5应用程序移动到使用EF core的asp.net core MVC 3.0

在我的mvc5应用程序中,我有一些修改数据库的管理操作,需要很长时间,因此我在创建一个与请求上下文无关的新DBContext时使用了一种模式,然后使用task.run在后台运行任务。这几年来一直运作良好

在转换到.NETCore时,不清楚如何以我在旧代码库中的方式创建新的DBContext。在这些情况下,我似乎应该能够创建一个临时DBContext,一切都应该很好

因此,我创建了一个名为MyTransientDbContex的MyDbContext子类,并在配置类中添加了此服务:

            services.AddDbContext<MyTransientDbContex>(options =>
                options.UseSqlServer(
                    context.Configuration.GetConnectionString("MyContextConnection")),
                ServiceLifetime.Transient, ServiceLifetime.Transient);
我不希望在最后一个块之前处理我的临时上下文。但我在尝试访问后台线程上的上下文时遇到此异常:

Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'MyTransientContext'.'
实际上,上下文对象上的_disposed标志设置为true

我在MyTransientContext的构造函数上放置了一个断点,并为此指针创建了一个对象ID,以便跟踪该对象。正在创建此瞬态对象,它与注入控制器操作的对象相同。它也是引发异常时我试图引用的同一个对象

我尝试在_disposed成员上设置一个数据断点,以便在disposed设置为true时获得调用堆栈,但断点不会绑定

我还尝试重写MyTransientContext上的Dispose方法,直到抛出并捕获异常之后,在finally块中显式Dispose才调用它

我觉得我错过了一些基本的东西。这不是临时服务的目的吗?如何处置临时服务

最后一个细节—MyTransientContext源自MyContext,MyContext又源自IdentityDbContext Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext

编辑:我选择使用瞬态的原因是因为这个ef核心文档页面:。它指出…任何显式并行执行多个线程的代码都应该确保DbContext实例永远不会被并发访问。使用依赖项注入,可以通过将上下文注册为作用域并使用IServiceScopeFactory为每个线程创建作用域,或者通过使用带有ServiceLifetime参数的AddDbContext重载将DbContext注册为瞬态来实现


正如xabikos所指出的,asp.net DI系统的作用域似乎覆盖了这一点,该系统创建的任何内容都被应用于请求上下文,包括瞬态对象。有人能指出记录在哪里,以便我更好地理解如何处理这些限制吗?

如果您想管理服务的生命周期,可以手动实例化或使用工厂:

公共行动结果更新 { Task.Run=> { usingvar context=新建MyTransientContext。。。 { 尝试 { //使用上下文执行一些长时间运行的操作 } 捕获异常e { //报告例外情况 } } } 返回重定向状态; } 或者,您可以使用IServiceProvider获取和管理服务:

公共类MyController { 私人IServiceProvider服务; 公共MyControllerIServiceProvider服务 { _服务=服务; } 公共行动结果更新 { var context=MyTransientContext_services.GetServicetypeofMyTransientContext; Task.Run=> { 使用上下文 { 尝试 { //使用上下文执行一些长时间运行的操作 } 捕获异常e { //报告例外情况 } } } 返回重定向状态; } }
您混合了asp.net core提供的内部DI容器创建的瞬态对象的概念

您可以将MyTransientContext配置为在内部DI系统中是瞬态的。这实际上意味着每次创建范围时,都会返回一个新实例。对于asp.net应用程序,此作用域与HTTP请求匹配。当请求结束时,如果适用,则处理所有对象

现在在您的代码中,这是一个同步操作方法,您可以使用Task.Run生成一个任务。这是一个异步操作,您无需等待。实际上,在执行过程中,这将被启动,但不会等待完成,重定向将发生,请求将结束。此时,如果您尝试使用注入的实例,您将得到异常

如果你想解决 为此,您需要更改为异步操作,并等待Task.Run。而且很可能您不需要生成新任务。但是您需要了解,这可能不是最好的方法,因为在重定向发生之前,长操作需要完成


另一种方法是使用消息传递机制,并发送触发此操作的消息。您还有另一个组件,比如侦听并处理这些消息。

谢谢@xabikos-您的描述解释了为什么会发生这种情况。在我的背景下,这两种方法都不实用。我认为从长远来看,我将关注辅助服务,但我仍然需要弄清楚如何创建一个不受请求上下文范围限制的DbContext,以使任何解决方案都能工作,而不是使操作异步。关于如何做到这一点有什么提示吗?谢谢@Orwel-是的,我要替换的基本上是工厂模式。问题在于,在我的mvc5/ef6解决方案中,很容易创建断开连接的DbContext。我不确定在.net核心环境下如何做到这一点。我所看到的一切都是假设一个人通过DI创建DbContext。我想得太多了-使用这种模式的唯一困难是将连接字符串获取到构造函数,在我的情况下,我可以从现有连接中挖掘连接字符串,因为我一直都有可用的连接。但我想在其他情况下,传入一个配置对象也会起作用。也许使用IServiceProvider?请参阅我答案中的编辑。
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'MyTransientContext'.'