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

Posted

技术标签:

【中文标题】C#:使用 IQueryable 注入 DbContext 时,无法访问 ASP.NET Core 中的已处置对象【英文标题】:C#: Cannot access a disposed object in ASP.NET Core when injecting DbContext with IQueryable 【发布时间】:2020-10-21 03:53:04 【问题描述】:

我收到以下错误。目前遵循 Async/await 模式并使用 IServiceScopeFactory。尝试将 Repo 与 IQueryable 链接。

接收错误如下:

无法访问已处置的对象。此错误的一个常见原因是释放从依赖注入中解析的上下文,然后尝试在应用程序的其他地方使用相同的上下文实例。如果您在上下文上调用 Dispose() 或将上下文包装在 using 语句中,则可能会发生这种情况。如果你使用依赖注入,你应该让依赖注入容器负责处理上下文实例。 对象名称:'PropertyContext'。

存储库:

public class AddressRepository

    private readonly IServiceScopeFactory _factory;
    public AddressRepository(IServiceScopeFactory factory) : base(factory)
    
        _factory = factory;

    [EnableQuery]
    public IQueryable<LkAddressType> GetAddressTypes()
    
        using (var scope = _factory.CreateScope())
        
            var db = scope.ServiceProvider.GetService<PropertyContext>();
            return db.LkAddressType.AsNoTracking();
        
    

应用服务:

    public async Task<AddressTypeResponse> GetAddressTypes()
    
        var data = await (_addressRepository.GetAddressTypes()).ToListAsync();
        var mapped = _mapper.Map<ICollection<AddressTypeModel>>(data);
        var dto = _mapper.Map<ICollection<AddressTypeDto>>(mapped);
        return new AddressTypeResponse Body = dto;
    

控制器:

    [HttpGet("AddressType")]
    public async Task<ActionResult<AddressTypeResponse>> GetAddressTypes()
    
         return Ok(await _service.GetAddressTypes());
    

目前使用 Net Core 2.2、Entity Framework Core 和 OData。

【问题讨论】:

【参考方案1】:

作用域会自动释放它创建的实现IDisosable 的作用域(和瞬态)对象,因此当GetAddressTypes 结束时,scopedb 将被释放。因此,您要么需要返回一些客户端将处理的包装器,要么具体化查询并从方法返回结果:

[EnableQuery]
public Task<List<LkAddressType>> GetAddressTypes()

    using (var scope = _factory.CreateScope())
    
        var db = scope.ServiceProvider.GetService<PropertyContext>();
        return await db.LkAddressType.AsNoTracking().ToListAsync();
    

或者将您的上下文直接注入到存储库中,让框架确定范围:

public class AddressRepository

    private readonly PropertyContext _ctx;
    public AddressRepository(PropertyContext ctx,IServiceScopeFactory factory) : base(factory)
    
        _ctx= ctx;
     

    [EnableQuery]
    public IQueryable<LkAddressType> GetAddressTypes()
    
        return _ctx.LkAddressType.AsNoTracking();
    
 

【讨论】:

@marksmith527 为什么要创建新范围?为什么不将PropertyContext 注入AddressRepository 并让框架接受? @marksmith527 你也在AppService.GetAddressTypes 中实现查询,然后控制器才可以使用它。 嗨@GuruStron,现在它可以工作并选择所有数据,但是OData 不起作用,localhost:6878/api/Address/AddressType?$top=2localhost:6878/api/Address/AddressType?$select=addressTypeCode 我在这里发布了一个更详细的问题,我可以发送积分并关闭它,你碰巧知道这个答案吗? ***.com/questions/62650392/… @marksmith527 尝试按照第二个 sn-p 的建议将上下文注入存储库。我会说通常(并非总是)手动创建范围是一种代码味道。

以上是关于C#:使用 IQueryable 注入 DbContext 时,无法访问 ASP.NET Core 中的已处置对象的主要内容,如果未能解决你的问题,请参考以下文章

C# Entity Framework中的IQueryable和IQueryProvider详解

C# IQueryable和IEnumerable的区别

C# 向IQueryable添加一个Include扩展方法

温故知新C#中 IEnumerable 与IQueryable

C#怎么遍历IQueryable<Infrom>Inform为数据库类型

IQueryable 不包含 SingleOrDefault 的定义