DbContext缺陷注入问题-System.ObjectDisposedException:无法访问已处置的对象

时间:2019-05-17 14:28:06

标签: asp.net .net entity-framework asp.net-core-mvc

我已经在Ubuntu计算机上使用MVC框架编写了我的第一个.Net核心程序。在该程序中,我尝试与SQLite数据库进行交互。通过控制器类进行处理时,数据库CRUD操作可以正常工作。但是,当我尝试在控制器外部的数据库上进行操作时,出现以下错误

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

对于控制器类之外的操作,我创建了一个名为 MyDbWatch.cs 的类(在项目根目录中)

    public interface IMyDbWatch { }

    public class MyDbWatch : IMyDbWatch
    {  
        private readonly MyDbContext _dbContext; 
        private static Timer _timer;
        private AutoResetEvent _autoEvent = null;

        public MyDbWatch(MyDbContext context)
        {
            _dbContext = context;   

            _autoEvent = new AutoResetEvent(false);
            _timer = new Timer(
                callback: async s => await OnTimerEventAsync(s),
                state: _autoEvent,
                dueTime: 5000,  
                period: 10000);              
        }    

        public async Task OnTimerEventAsync(Object stateInfo)    
        {    
            Console.WriteLine("retreiving from db - 1");
            var ienStates = from m in _dbContext.IenState select m;  
            Console.WriteLine("retreiving from db - 2");         
            var listdb = await ienStates.ToListAsync();
            Console.WriteLine("retreiving from db - 3");                             
        } 
    }

这是我如何在 Startup.cs 文件

中注入不同的依赖项
public class Startup
{
    private MyDbWatch _myDbWatch;

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {                
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });            

        services.AddDbContext<IpointWebMcmContext>(options =>
            options.UseSqlite(Configuration.GetConnectionString("IpointContext")));           

        services.AddScoped<IMyDbWatch, MyDbWatch>(); 

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, 
    IMyDbWatch dbwatch)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");                
            app.UseHsts();
        }

        _myDbWatch = dbwatch;           

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseCookiePolicy();           

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

首次调用 MyDbWatch.cs 中的计时器回调函数 OnTimerEventAsync ,并在控制台中打印调试文本“从db接收-1”。然后我得到了错误

未处理的异常:System.ObjectDisposedException:无法访问已处置的对象...

对于解决此问题的任何帮助,我们将不胜感激。我需要对数据库进行这种监视,以通过使用SignalR集线器框架(尚未包含在代码中)将数据推送到客户端。

1 个答案:

答案 0 :(得分:0)

这就是为什么应该避免静电的原因。几乎每次您有这样的静态对象时,都有一些开发人员会跳过它,因为他们没有考虑实际的工作方式。

static关键字不是魔术。您已经获得了一个范围化的服务,您要在其中保持状态(您的计时器),因此您只需在其上拍一个static并称之为一天。但是,此服务使用其他范围内的服务(您的上下文),这些服务现在与该静态计时器不同步,即计时器会停留在周围,但上下文不会。

首先,如果需要在整个应用程序生命周期内维护状态,则应使用单例作用域。这使您摆脱了static的恐惧。但是,然后,您将需要利用服务器定位器模式来获取上下文,因为您无法将范围限定的实例注入到单例中。

public class MyDbWatch : IMyDbWatch
{  
    private readonly IServiceProvider _serviceProvider; 
    private readonly Timer _timer;
    private AutoResetEvent _autoEvent = null;

    public MyDbWatch(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;

        _autoEvent = new AutoResetEvent(false);
        _timer = new Timer(
            callback: async s => await OnTimerEventAsync(s),
            state: _autoEvent,
            dueTime: 5000,  
            period: 10000);              
    }    

    public async Task OnTimerEventAsync(Object stateInfo)    
    {    
        using (var scope = _serviceProvider.CreateScope())
        {
            var context = scope.ServiceProvider.GetRequiredService<MyDbContext>();

            Console.WriteLine("retreiving from db - 1");
            var ienStates = from m in context.IenState select m;  
            Console.WriteLine("retreiving from db - 2");         
            var listdb = await ienStates.ToListAsync();
            Console.WriteLine("retreiving from db - 3");                             
         }
     } 
}

然后,在ConfigureServices中:

services.AddSingleton<IMyDbWatch, MyDbWatch>(); 

现在,我不知道您实际上要用哪种方法来完成什么,因为您的代码没有多大意义,但是以上是您可以安全地完成此操作的唯一方法