Using async Repository pattern - Cannot access a disposed object

asp.net-core async-await c# entity-framework-core repository-pattern

Question

I am trying to implement async Repository pattern and would like to update asynchronously:

My controller looks like this:

// PUT api/category
[HttpPut]
public void Put([FromBody] CategoryDto categoryDto)
{
     var category = _mapper.Map<Categories>(categoryDto);
     _categoryService.UpdateCategory(category);
}

My RepositotyBase<T> class:

public abstract class RepositoryBase<T> where T : class
{
    public virtual async Task Update(T entity)
    {            
        // This code throws error: Cannot access a disposed object. A common 
        // cause of this error is disposing ...
        await Task.Run(() => { // I have **await** here 
            dbSet.Attach(entity);
            shopContext.Entry(entity).State = EntityState.Modified;
        });
    }
}

My CategoryService:

public class CategoryService : ICategoryService
{    
    public async Task UpdateCategory(Categories category)
    {
        await _categoryRepository.Update(category); // I have **await** here 
        _unitOfWork.Commit();
        return;
    }
}

However, the async implementation of method public virtual async Task Update(T entity) of RepositoryBase<T> throws an error:

public abstract class RepositoryBase<T> where T : class
{
    public virtual async Task Update(T entity)
    {            
        // This code throws error: Cannot access a disposed object. A common 
        // cause of this error is disposing ...
        await Task.Run(() => {
            dbSet.Attach(entity); // I have **await** here 
            shopContext.Entry(entity).State = EntityState.Modified;
        });
    }
}

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.

UPDATE: Is it correct async implemetation?

My controller:

// PUT api/category
[HttpPut]
public void Put([FromBody] CategoryDto categoryDto)
{
     var category = _mapper.Map<Categories>(categoryDto);
     _categoryService.UpdateCategory(category);
}

My generic repository:

public abstract class RepositoryBase<T> where T : class
{
    public virtual async Task Update(T entity)
    {            
        dbSet.Attach(entity);
        shopContext.Entry(entity).State = EntityState.Modified;
    }
}

My unit of work:

public class UnitOfWork : IUnitOfWork
{
    private readonly IDbFactory dbFactory;
    private StoreEntities dbContext;

    public UnitOfWork(IDbFactory dbFactory)
    {
        this.dbFactory = dbFactory;
    }

    public StoreEntities DbContext
    {
        get { return dbContext ?? (dbContext = dbFactory.Init()); }
    }

    public async Task CommitAsync()
    {
        //DbContext.Commit();
        await DbContext.SaveChangesAsync();
    }
}

My service:

public class CategoryService : ICategoryService
{    
    public async Task UpdateCategory(Categories category)
    {
        _categoryRepository.Update(category); // I have **await** here 
        await Task.Run(()=> {_unitOfWork.Commit()}); // 
        return;
    }
}
1
0
9/16/2019 8:53:37 AM

Accepted Answer

You need to setup your controller action PUT api/category to be async/await aware. @mjwills has alluded to this in the comments.

// PUT api/category
[HttpPut]
public async Task Put([FromBody] CategoryDto categoryDto)
{
     var category = _mapper.Map<Categories>(categoryDto);
     await _categoryService.UpdateCategory(category);
     // you probably want to set a response code. e.g return OK()
}

This is a little more offtopic but I am addressing some of the other comments. You also need to save the changes to the database in EF. You shouldn't need to manually create your own tasks. To my knowledge EF provides a SaveChangesAsync method for you: https://docs.microsoft.com/en-us/ef/core/saving/async.

public abstract class RepositoryBase<T> where T : class
{
    public virtual async Task Update(T entity)
    {            
        dbSet.Attach(entity);
        shopContext.Entry(entity).State = EntityState.Modified;
        await shopContext.SaveChangesAsync();
    }
}

public class CategoryService : ICategoryService
{    
    public async Task UpdateCategory(Categories category)
    {
        return await _categoryRepository.Update(category);
    }
}
1
9/16/2019 1:33:14 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow