使用异步存储库模式 - 无法访问已处置的对象

Posted

技术标签:

【中文标题】使用异步存储库模式 - 无法访问已处置的对象【英文标题】:Using async Repository pattern - Cannot access a disposed object 【发布时间】:2020-01-17 00:48:06 【问题描述】:

我正在尝试实现异步存储库模式并希望异步更新:

我的控制器如下所示:

// PUT api/category
[HttpPut]
public void Put([FromBody] CategoryDto categoryDto)

     var category = _mapper.Map<Categories>(categoryDto);
     _categoryService.UpdateCategory(category);

我的RepositotyBase&lt;T&gt; 班级:

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

我的CategoryService

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

但是,RepositoryBase&lt;T&gt; 的方法 public virtual async Task Update(T entity) 的异步实现会抛出错误:

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

无法访问已处置的对象。此错误的常见原因是 处理从依赖注入解决的上下文和 然后稍后尝试在您的其他地方使用相同的上下文实例 应用。如果您在 上下文,或将上下文包装在 using 语句中。如果你是 使用依赖注入,你应该让依赖注入 容器负责处理上下文实例。

更新: 它是正确的异步实现吗?

我的控制器:

// PUT api/category
[HttpPut]
public void Put([FromBody] CategoryDto categoryDto)

     var category = _mapper.Map<Categories>(categoryDto);
     _categoryService.UpdateCategory(category);

我的通用存储库:

public abstract class RepositoryBase<T> where T : class

    public virtual async Task Update(T entity)
                
        dbSet.Attach(entity);
        shopContext.Entry(entity).State = EntityState.Modified;
    

我的工作单位:

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

我的服务:

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

【问题讨论】:

【参考方案1】:

您需要设置您的控制器操作PUT api/category 以了解异步/等待。 @mjwills 在 cmets 中提到了这一点。

// 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()

这有点离题,但我正在讨论其他一些 cmets。 您还需要将更改保存到 EF 中的数据库。您不需要手动创建自己的任务。据我所知,EF 为您提供了一个SaveChangesAsync 方法: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);
    

【讨论】:

这还不够。其余代码也需要紧急清理,因为它不会异步执行任何操作。事实上,它并没有保存任何东西,它只是修改了 DbContext 而不保存更改 如果我们假设对SaveChangesSaveChangesAsync 的实际调用是在_uow.Commit 内部进行的,UpdateCategory 仍然会阻塞 @PanagiotisKanavos 你能写出正确的版本吗?我真的很难弄清楚应该如何编写正确的实现。我只是被告知我做错了,还有一件事做错了。请告诉我我应该写什么。 @Oliver 谢谢,点赞。我的个人电脑离我很远。对不起。稍后当我可以访问个人计算机时,我会写下结果。 @PanagiotisKanavos 我同意还有一些其他项目可以使用寻址,但我试图直接回答手头的问题。我想也许这段代码是来自一个更好的解决方案的sn-ps。

以上是关于使用异步存储库模式 - 无法访问已处置的对象的主要内容,如果未能解决你的问题,请参考以下文章

C#:使用 IQueryable 注入 DbContext 时,无法访问 ASP.NET Core 中的已处置对象

无法访问已处置的对象 - 如何修复?

System.ObjectDisposedException:'无法访问已处置的对象。对象名称:'OracleConnection'。'

引发线程事件时无法访问已处置的对象

无法访问已处置的对象。对象名称:'System.Net.Sockets.UdpClient'

无法访问已处置的对象 VC++ VS2017 Professional