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