es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo resuelvo el error “No se puede acceder a una instancia de contexto eliminada” desde una acción de SignalR en mi aplicación web ASP.NET Core 5?

Estoy usando una cuadrícula KendoUI SignalR enlazada en mi aplicación web. La cuadrícula es editable y permite al usuario eliminar registros haciendo clic en un pequeño icono de papelera de reciclaje. En esta instancia particular, el elemento que se está eliminando tiene muchos datos relacionados en diferentes tablas.

Estoy usando un patrón de repositorio genérico con inyección de dependencia dentro de mi controlador de concentrador para manejar los eventos de lectura/destrucción/actualización de la cuadrícula.

Aquí está mi controlador de concentrador en este momento:

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

<pre><code> 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);
}“`

Observará a partir de la acción anterior, que se utilizan varias instancias de contexto y se declaran como listas, la razón de esto es evitar el error de apertura de lector de datos que puede ocurrir al utilizar la inyección de dependencia de esta manera. Además, la acción es asíncrona con el único propósito de asegurarse de que cada operación se complete antes de pasar a la siguiente, la falta de hacer esto asíncrono ha causado errores de conflicto de subprocesos.

Cuando trato de eliminar un elemento de la cuadrícula usando la configuración de acción anterior, obtengo el error:

No se puede acceder a una instancia de contexto desechada. Una causa común de este error es desechar una instancia de contexto que se resolvió a partir de la inyección de dependencia y luego intentar usar la misma instancia de contexto en otra parte de la aplicación. Esto puede ocurrir si está llamando a ‘Dispose’ en la instancia de contexto o envolviéndola en una instrucción ‘using’. Si está utilizando la inyección de dependencia, debe dejar que el contenedor de inyección de dependencia se encargue de la eliminación de las instancias de contexto.

Esto me hizo pensar que quizás el .ToList() estaba causando este problema, así que lo eliminé, cuando lo hice, naturalmente obtengo el error de que hay un lector de datos abierto que no se ha desechado. Entonces mi pregunta es, ¿cómo puedo solucionar esto y hacer que esta acción de eliminación funcione correctamente?

Aquí está mi repositorio de referencia:

<irepository.cs>

“`using System.Linq;
using System.Threading.Tasks;

namespace MyCompany.Repo
{
public interface IRepository<TEntity> where TEntity : class
{

    IQueryable&lt;TEntity&gt; GetAll();
    Task&lt;TEntity&gt; CreateAsync(TEntity entity);
    Task&lt;</irepository.cs>
Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    El mensaje de error indica que hay un problema con la eliminación de la instancia de contexto que se resolvió a través de la inyección de dependencias. Una posible razón de este error es que la instancia de contexto se está eliminando prematuramente o se está utilizando en múltiples lugares sin eliminarse correctamente.

    En el código proporcionado se está utilizando el patrón repositorio con inyección de dependencias dentro del controlador de hub para manejar los eventos de lectura/eliminación/actualización de la cuadrícula. El método Destroy está utilizando múltiples instancias de contexto para eliminar registros de diferentes tablas. Para prevenir el error que se produce al utilizar la inyección de dependencias de esta manera, las instancias de contexto se declaran como listas.

    El problema parece ser causado por el uso de .ToList(), lo que hace que el contexto se cierre prematuramente. Cuando se elimina .ToList(), se produce un error de lector de datos abierto que no se cerró.

    Para solucionar este problema, debes utilizar la declaración using para eliminar correctamente la instancia de contexto después de su uso. Al recuperar los datos, llama al método .AsNoTracking() para desactivar el seguimiento de cambios, ya que esto puede causar problemas de rendimiento al tratar con grandes volúmenes de datos.

    Aquí está una versión actualizada del método Destroy que aborda el problema:

    public async Task Destroy(ViewNewswires model)
    {
        using (var context = new HaglandContext())
        {
            var newswire = await context.Newswires.FindAsync(model.Id);
            if (newswire == null) return;
    
            var charterers = await context.NewswireCharterers.Where(x => x.Newswire_Id == model.Id).ToListAsync();
            var owners = await context.NewswireOwners.Where(x => x.Newswire_Id == model.Id).ToListAsync();
            var projects = await context.NewswireProjects.Where(x => x.Newswire_Id == model.Id).ToListAsync();
            var regions = await context.NewswireRegions.Where(x => x.Newswire_Id == model.Id).ToListAsync();
            var scopes = await context.NewswireScopes.Where(x => x.Newswire_Id == model.Id).ToListAsync();
            var vessels = await context.NewswireVessels.Where(x => x.Newswire_Id == model.Id).ToListAsync();
    
            foreach (var charterer in charterers) {
                context.NewswireCharterers.Remove(charterer);
            };
    
            foreach (var owner in owners)
            {
                context.NewswireOwners.Remove(owner);
            };
    
            foreach (var project in projects)
            {
                context.NewswireProjects.Remove(project);
            };
    
            foreach (var region in regions)
            {
                context.NewswireRegions.Remove(region);
            };
    
            foreach (var scope in scopes)
            {
                context.NewswireScopes.Remove(scope);
            };
    
            foreach (var vessel in vessels)
            {
                context.NewswireVessels.Remove(vessel);
            };
    
            context.Newswires.Remove(newswire);
    
            await context.SaveChangesAsync();
        }
    
        await Clients.OthersInGroup(GetGroupName()).SendAsync("destroy", model);
    }
    

    En esta versión actualizada, se utiliza la declaración using para eliminar correctamente la instancia de contexto después de su uso. El seguimiento de cambios se desactiva mediante el método .AsNoTracking() para evitar problemas de rendimiento al tratar con grandes volúmenes de datos.

    Además, el método Destroy ahora está marcado como async Task en lugar de async void, ya que ahora devuelve una Task en lugar de no tener un tipo de retorno. Esto permite un manejo adecuado de errores y facilita el seguimiento del estado de la operación.

Comments are closed.