Mattieeec3
Mattieeec3

Reputation: 114

c# - DataContext disposed in ASP.NET Core scheduler

I have included Coravel scheduler in my ASP.NET application.

Everything seems to work well, but when the scheduler starts running it returns System.ObjectDisposedException on the DataContext. I use Dependency Injection in my repository classes, does the context only get created when it receives a request from the browser? How can I fix this so the context is available when my scheduler runs the method?

Below is my repository code

using System.Collections.Generic;
using System.Threading.Tasks;
using Project.Models;
using Microsoft.EntityFrameworkCore;
using System.Linq;


namespace Project.Data.CustomerRepo
{
    public class CustomerRepository : ICustomerRepository
    {
        private readonly DataContext _context;
        public CustomerRepository(DataContext context)
        {
            this._context = context;

        }
        public async Task<Customer> Create(Customer customer)
        {
            await _context.Customers.AddAsync(customer);
            return customer;
        }

        public async Task<IEnumerable<Customer>> Create(IEnumerable<Customer> customers)
        {
            await _context.Customers.AddRangeAsync(customers);
            await _context.SaveChangesAsync();
            return customers;
        }

        public async Task Delete(Customer customer)
        {
            _context.Customers.Remove(customer);
            await _context.SaveChangesAsync();
        }

        public async Task<Customer> Read(int id)
        {
            var customer = await _context.Customers.FirstOrDefaultAsync(customerItem => customerItem.Id == id);
            return customer;
        }
        public async Task<Customer> ReadFromFacebookId(string id)
        {
            var customer = await _context.Customers.Where(customerItem => customerItem.FacebookId == id).FirstOrDefaultAsync();
            return customer;
        }

        public async Task<IEnumerable<Customer>> Read()
        {
            var customers = await _context.Customers.ToListAsync();
            return customers;
        }

        public async Task<Customer> Update(Customer customer)
        {
            _context.Update(customer);
            await _context.SaveChangesAsync();
            return customer;
        }
    }
}

Upvotes: 1

Views: 1827

Answers (1)

Ryan
Ryan

Reputation: 20126

services.AddDbContext<>()registers the DataContext as a service with ServiceLifetime.Scoped which means that your DataContext is created per web request. It is disposed when request is completed.

You could inject IServiceScopeFactory which is singleton into your controller/repository, then create a new scope using CreateScope() and request the DataContext service from that scope

public class CustomerRepository : ICustomerRepository
{
    IServiceScopeFactory _serviceScopeFactory
    public CustomerRepository(IServiceScopeFactory serviceScopeFactory)
    {
        _serviceScopeFactory = serviceScopeFactory;
    }
    public async Task<Customer> Create(Customer customer)
    {
        using (var scope = _serviceScopeFactory.CreateScope())
        {
            var context = scope.ServiceProvider.GetRequiredService<DataContext>();              
            await context.Customers.AddAsync(customer);
            return customer;
        }

        return customer;

    }
}

Refer to Access DbContext service from background task

Upvotes: 4

Related Questions