Проблема с внедрением зависимости DbContext - System.ObjectDisposedException: невозможно получить доступ к удаленному объекту - PullRequest
0 голосов
/ 17 мая 2019

Я написал свою первую базовую программу .Net с использованием инфраструктуры MVC на машине с Ubuntu.В программе я пытаюсь взаимодействовать с базой данных 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 file

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

Функция обратного вызова таймера OnTimerEventAsync in MyDbWatch.cs вызывается в первый раз, а отладочный текст 'retrieve from db - 1' печатается в консоли.И после этого я получаю ошибку

Необработанное исключение: System.ObjectDisposedException: Невозможно получить доступ к удаленному объекту ... .

Любая помощь в решении этой проблемы будет принята с благодарностью.Мне нужно такое наблюдение за базой данных, чтобы передать данные клиенту с помощью инфраструктуры хабов SignalR (пока не включенной в код).

1 Ответ

0 голосов
/ 17 мая 2019

И именно поэтому статики следует избегать. Практически каждый раз, когда у вас появляется такая статика, какой-то разработчик собирается опрокинуть ее, потому что они не думают о том, как все работает на самом деле.

Ключевое слово 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>(); 

Теперь я понятия не имею, чего вы на самом деле пытаетесь достичь с помощью всего этого, поскольку ваш код не имеет особого смысла, но вышесказанное - единственный способ, которым вы сможете безопасно это сделать. .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...