Search code examples
c#asp.netasp.net-coreobjectdisposedexception

ObjectDisposedException trying implement DDD project


When I try to return my orderList as IQueryable, I get the following error:

"ObjectDisposedException: 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: 'KeplerContext'"

Ps. If I work with the DbContext directly at my controller, everything works fine.

Some help? Thanks in advance.

My controller class is:

public class OrderController : Controller
{
    public readonly IAppOrder _IAppOrder;
    public readonly UserManager<User> _userManager;

    // Ps.: Directly for the context works fine
    //private readonly KeplerContext _context;

    public OrderController(IAppOrder IAppOrder, UserManager<User> userManager) //, KeplerContext context)
    {
        _IAppOrder = IAppOrder;
        _userManager = userManager;

        // Ps.: Directly for the context works fine
        //_context = context;
    }

    public async Task<IActionResult> Index(string sortOrder, int? pageNumber)
    {
        var user = await _userManager.GetUserAsync(User);

        var orderList = _IAppOrder.OrderList(user);
            ^^^^

        if (!orderList.Any())
        {
            TempData["info"] = "no orders.";
        }
        else
        {
            var orderActive = await _IAppOrder.GetLastOrder(user);

            if (orderActive.IdOrderStatus <= 5)
            {
                return RedirectToAction(nameof(OrderController.Order), "Order", new { Area = "App", Id = orderActive.IdOrder });
            }
        }

        // paging
        ViewData["CurrentSort"] = sortOrder;
        ViewData["NumSort"] = String.IsNullOrEmpty(sortOrder) ? "num_asc" : "";

        switch (sortOrder)
        {
            case "num_asc":
                orderList = orderList.OrderBy(s => s.IdOrder);
                break;
            default:
                orderList = orderList.OrderByDescending(s => s.IdOrder);
                break;
        }
        int pageSize = 8;

        return View(await PagingHelper<Order>.CreateAsync(orderList.AsNoTracking(), pageNumber ?? 1, pageSize));
    }
}

My application class: (Ps. I am working with DDD, others classes I have omitted to simplify the question):

    public IQueryable<Order> OrderList (User user)
    {
        return _IOrder.OrderList(user);
    }

My repository class:

public class RepositoryOrder : RepositoryGenerics<Order>, IDomOrder
{
    private readonly DbContextOptions<KeplerContext> _optionsBuilder;

    public RepositoryOrder()
    {
        _optionsBuilder = new DbContextOptions<KeplerContext>();
    }

    public IQueryable<Order> OrderList(User user)
    {
        using var db = new DbContext(_optionsBuilder);

        var result = db.Order
            .Include(p => p.IdOrderStatusNavigation)
            .Include(p => p.IdNavigation)
            .Where(u => u.Id == user.Id)
            .AsQueryable();
         
        return result;
               ^^^^^^ <<< Error happens here!!!
    }
}

My repository generics class:

public class RepositoryGenerics<T> : IDomGeneric<T>, IDisposable where T : class
{
    private readonly DbContextOptions<KeplerContext> _OptionsBuilder;

    public RepositoryGenerics()
    {
        _OptionsBuilder = new DbContextOptions<KeplerContext>();
    }

    public async Task Add(T obj)
    {
        using var data = new KeplerContext(_OptionsBuilder);

        await data.Set<T>().AddAsync(obj);
        await data.SaveChangesAsync();
    }

    public async Task Delete(T obj)
    {
        using var data = new KeplerContext(_OptionsBuilder);

        data.Set<T>().Remove(obj);
        await data.SaveChangesAsync();
    }

    public async Task<T> GetEntityById(int Id)
    {
        using var data = new KeplerContext(_OptionsBuilder);

        return await data.Set<T>().FindAsync(Id);
    }

    public async Task<List<T>> List()
    {
        using var data = new KeplerContext(_OptionsBuilder);

        return await data.Set<T>().AsNoTracking().ToListAsync();
    }

    public async Task Update(T obj)
    {
        using var data = new KeplerContext(_OptionsBuilder);

        data.Set<T>().Update(obj);
        await data.SaveChangesAsync();
    }


    // https://learn.microsoft.com/pt-br/dotnet/standard/garbage-collection/implementing-dispose
    bool disposed = false;
    SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);

    // "Dispose Pattern" 
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposed)
            return;

        if (disposing)
        {
            handle.Dispose();
        }

        disposed = true;
    }
}

Dependency injection inside the Startup.cs:

        // Repository
        services.AddSingleton(typeof(IDomGeneric<>), typeof(RepositoryGenerics<>));
        ...
        services.AddSingleton<IDomOrder, RepositoryOrder>();
        ...

        // Inteface da Application
        ...
        services.AddSingleton<IAppOrder, AppOrder>();
        ...

        // Service Domain
        ...
        services.AddSingleton<IServiceOrder, ServiceOrder>();
        ...

Solution

  • Your RepositoryOrder.OrderList is returning an IQueryable meaning you are not executing the query within the scope of the method. So I believe what you are seeing is that when the query is eventually executed the DbContext has already been disposed of, because it is disposed as soon as you leave the method scope.

    Instead you could call ToList rather than AsQueryable.