如何从我的 ASP.NET Core 5 Web 应用程序中的 SignalR 操作中解决错误“无法访问已释放的上下文实例”?

Posted

技术标签:

【中文标题】如何从我的 ASP.NET Core 5 Web 应用程序中的 SignalR 操作中解决错误“无法访问已释放的上下文实例”?【英文标题】:How do I resolve the error "Cannot access a disposed context instance" from a SignalR action in my ASP.NET Core 5 web application? 【发布时间】:2022-01-07 01:31:06 【问题描述】:

我在我的 Web 应用程序中使用了 KendoUI SignalR Bound 网格。网格是可编辑的,允许用户通过单击一个小垃圾桶图标来删除记录。在这个特定的例子中,被删除的项目在不同的表中有很多相关的数据。

我在集线器控制器内部使用具有依赖注入的通用存储库模式来处理网格的读取/销毁/更新事件。

这是我目前的集线器控制器:

public async void Destroy(ViewNewswires model)
    
        IEnumerable<NewswireCharterer> nwc = _newswireCharterer.GetAll().Where(x => x.Newswire_Id == model.Id).ToList();
        IEnumerable<NewswireOwner> nwo = _newswireOwner.GetAll().Where(x => x.Newswire_Id == model.Id).ToList();
        IEnumerable<NewswireProject> nwp = _newswireProject.GetAll().Where(x => x.Newswire_Id == model.Id).ToList();
        IEnumerable<NewswireRegion> nwr = _newswireRegion.GetAll().Where(x => x.Newswire_Id == model.Id).ToList();
        IEnumerable<NewswireScope> nws = _newswireScope.GetAll().Where(x => x.Newswire_Id == model.Id).ToList();
        IEnumerable<NewswireVessel> nwv = _newswireVessel.GetAll().Where(x => x.Newswire_Id == model.Id).ToList();

        foreach (var charterer in nwc) 
            await _newswireCharterer.DeleteAsync(charterer);
        ;
        
        foreach (var owner in nwo)
        
            await _newswireOwner.DeleteAsync(owner);
        ;
        
        foreach (var project in nwp)
        
            await _newswireProject.DeleteAsync(project);
        ;
        
        foreach (var region in nwr)
        
            await _newswireRegion.DeleteAsync(region);
        ;
        
        foreach (var scope in nws)
        
            await _newswireScope.DeleteAsync(scope);
        ;
        
        foreach (var vessel in nwv)
        
            await _newswireVessel.DeleteAsync(vessel);
        ;
        Newswire nw = _newswires.GetAll().Where(x => x.Id == model.Id).FirstOrDefault();
        await _newswires.DeleteAsync(nw);

        await Clients.OthersInGroup(GetGroupName()).SendAsync("destroy", model);
    

你会从上面的动作中看到,有一些上下文实例被使用并且被声明为列表,这样做的原因是为了防止以这种方式使用依赖注入时可能发生的数据读取器打开错误。此外,该操作是异步的,其唯一目的是确保在进行下一个操作之前完成每个操作,未能进行此异步操作会导致线程冲突错误。

当我尝试使用上述操作配置从网格中删除项目时,我收到错误:

无法访问已释放的上下文实例。造成这种情况的一个常见原因 错误正在处理已解决的上下文实例 依赖注入,然后尝试使用相同的上下文 在您的应用程序的其他地方实例。如果您是 在上下文实例上调用“Dispose”,或将其包装在 using 陈述。如果你使用依赖注入,你应该让 依赖注入容器负责处理上下文 实例。

这让我想到可能是 .ToList() 导致了这个问题,所以我删除了它,当我这样做时,我自然会得到一个错误,即存在一个未处理的开放数据读取器。所以我的问题是,我该如何解决这个问题并让这个删除操作正常工作?

这是我的参考资料库:

IRepository.cs

using System.Linq;
using System.Threading.Tasks;

namespace MyCompany.Repo

    public interface IRepository<TEntity> where TEntity : class
    

        IQueryable<TEntity> GetAll();
        Task<TEntity> CreateAsync(TEntity entity);
        Task<TEntity> UpdateAsync(TEntity entity);
        Task<TEntity> DeleteAsync(TEntity entity);
    

Repository.cs

namespace MyCompany.Repo

    public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    

        protected readonly HaglandContext _context;
        #endregion

        public Repository(HaglandContext context)
        
            _context = context;
        
        public IQueryable<TEntity> GetAll()
        
            try
            
                return _context.Set<TEntity>();
            
            catch (Exception ex)
            
                throw new Exception($"Couldn't retrieve entities: ex.Message");
            
                
        public async Task<TEntity> CreateAsync(TEntity entity)
        
            if (entity == null)
            
                throw new ArgumentNullException($"nameof(CreateAsync) entity must not be null");
            

            try
            
                await _context.AddAsync(entity);
                await _context.SaveChangesAsync();

                return entity;
            
            catch (Exception ex)
            
                throw new Exception($"nameof(entity) could not be saved: ex.Message");
            
        
        public async Task<TEntity> DeleteAsync(TEntity entity)
        
            if (entity == null)
            
                throw new ArgumentNullException($"nameof(DeleteAsync) entity must not be null");
            

            try
            
                _context.Remove(entity);
                await _context.SaveChangesAsync();

                return entity;
            
            catch (Exception ex)
            
                throw new Exception($"nameof(entity) could not be saved: ex.Message");
            
        
        public async Task<TEntity> UpdateAsync(TEntity entity)
        
            if (entity == null)
            
                throw new ArgumentNullException($"nameof(CreateAsync) entity must not be null");
            

            try
            
                _context.Update(entity);
                await _context.SaveChangesAsync();

                return entity;
            
            catch (Exception ex)
            
                throw new Exception($"nameof(entity) could not be updated: ex.Message");
            
        
    

Grid.js

function load_newswire_grid(e)     
    let go = $('#newswire_grid').kendoGrid(
        dataSource:             
            autoSync: true,
            pageSize: 100,
            schema: 
                model: 
                    id: "Id"                   
                
            ,
            type: "signalr",
            sort: [
                
                    field: "Id", 
                    dir: "desc"
                ,
            ],
            transport:                 
                signalr: 
                    promise: market_hub_start,
                    hub: market_hub,
                    server: 
                        read: "read",
                        update: "update",
                        create: "create",
                        destroy: "destroy"
                    ,//server
                    client: 
                        read: "read",
                        update: "update",
                        create: "create",
                        destroy: "destroy"
                    //client
                //signalr
            //transport
        ,//dataSource
        filterable: true,
        autoBind: true,             
        reorderable: true,
        scrollable: true,
        sortable: true,
        pageable: true,
        editable: "inline",

        columns: [
            
                field: "Id",
                hidden: true
            ,
            
                field: "Newswire_Title",
                title: "Source",
                template: '<a href="\\#pablo" data-newswire-id="#=Id#" onclick="open_newswire_window(this)">#=Newswire_Title#</a>'
            ,
            
                field: "Newswire_Text",
                title: "Text",
                template: "<div data-bs-toggle='popover' data-bs-trigger='hover' data-bs-html='true' data-bs-content='#=kendo.toString(Newswire_Text)#'><span>#=Newswire_Text#</span></div>"
            ,
            
                field: "Vessel_Names",
                title: "Vessels",
                template: "#if(Vessel_Names !=null) # #=vessel_tag_template(data)# # # # #"
            ,
            
                field: "Charterer_Names",
                title: "Charterers",
                width: 200,
                template: "#if(Charterer_Names !=null) # #=charterer_tag_template(data)# # else # # #"
            ,
            
                field: "Owner_Names",
                title: "Owners",
                width: 200,
                template: "#if(Owner_Names !=null) # #=owner_tag_template(data)# # else # # #"
            ,
            
                field: "Scope_Name",
                title: "Scopes",
                width: 200,
                template: "#if(Scope_Name !=null) # #=scope_tag_template(data)# # else ## #"
            ,
            
                field: "Region_Names",
                title: "Regions",
                width: 200,
                template: "#if(Region_Names !=null) # #=region_tag_template(data)# # else # # #"
            ,
            
                field: "Project",
                title: "Project",
                width: 200,
                template: "#if(Region_Names !=null) # #=project_tag_template(data)# # else # # #"
            ,
            
                field: "Created_Date",
                title: "Created",
                width: 100,

                template: "#if(Created_Date !=null) # <div data-toggle='popover' data-content='#=Created_User#' data-trigger='hover'>#=kendo.toString(kendo.parseDate(Created_Date, 'yyyy-MM-dd'), 'dd/MM/yyyy')#</div> # #"
            ,
            
                title: "&nbsp;",
                width: 40,
                template: '<div class="text-end dark-grid-button-group"><button class="k-button k-grid-delete"><span class="k-icon k-i-trash"></span></button></div>'
            
        ]//columns
    )//kendoGrid;
//function

【问题讨论】:

【参考方案1】:

您的“HaglandContext”是否注册为临时服务或范围服务? (应该是作用域)

您的“RepositoryTEntity”是否注册为临时服务或范围服务? (应该是暂时的)

您的 repo 和上下文之间可能存在一些注入不匹配

【讨论】:

存储库作为临时添加如下:services.AddTransient(typeof(IRepository&lt;&gt;), typeof(Repository&lt;&gt;));,上下文作为DbContext 添加如下:` services.AddDbContext(options => options.UseSqlServer(Configuration .GetConnectionString("MyConnection"), b => b.MigrationsAssembly("Hagland.Repo")));`

以上是关于如何从我的 ASP.NET Core 5 Web 应用程序中的 SignalR 操作中解决错误“无法访问已释放的上下文实例”?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 ASP.NET Core 3 中的中间 Web API 传递未更改的 HTTP 响应?

如何确定在 C# ASP.NET CORE MVC 5.0 中选择了哪个单选按钮

EF Core 5.0 - 更新 ASP.NET Core Web API 中的多对多实体

用于 ASP.NET Core 5 MVC 的 NuGet 包

从我的自定义中间件返回到角度时,asp .net core api cors 问题

如何将项目引用添加到 ASP.NET Core 1.0 MVC 项目