none
Error al hacer Upate (Generic Repository) RRS feed

  • Pregunta

  • Buenas,

    Tengo el Update de la siguiente manera en el repositorio genérico:

    public virtual TEntity Update(TEntity item)
    {
    	try
    	{
    		TEntity result = item;
    
    		result = databaseContext.Set<TEntity>().Attach(item);
    		databaseContext.Entry(item).State = EntityState.Modified;
    
    		return result;
    	}
    	catch (Exception ex)
    	{
    		throw ex;
    	}
    }

    Me está lanzando una excepción al ejecutarse el Attach.

    Attaching an entity of type 'Domain.Core.Entities.Seguridad.CredentialsPolicy' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

    Envío la entidad de la siguiente manera en la presentación:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Index(CredentialsPolicyViewModel vm)
    {
    	if (ModelState.IsValid)
    	{
    		var entidad = Mapper.Map<CredentialsPolicyViewModel, CredentialsPolicy>(vm);
    
    		if (vm.Id == 0)
    		{
    			credentialsPolicyAppService.Add(entidad);
    		}
    		else
    		{
    			credentialsPolicyAppService.Update(entidad);
    		}
    
    		return RedirectToAction("Confirmacion");
    	}
    
    	ModelState.AddModelError("", "Ingrese la información.");
    
    	return View();
    }

    Tengo el DbContext configurado de la siguiente manera:

    AutoDetectChangesEnabled: false;

    LazyLoadingEnabled: false;

    ProxyCreationEnabled: false;


    • Editado eduar2083 sábado, 20 de abril de 2019 12:30
    sábado, 20 de abril de 2019 12:29

Respuestas

  • Para recuperar la entidad estaba invocando a este método del repositorio genérico pero siempre "trackeando" y eso quizás generaba conflicto de seguimiento en las otras consultas, es por ello, que lo modifiqué para que no haga Tracking.

    public virtual async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> filter = null)
    {
    	try
    	{
    		if (filter == null)
    
    			return await databaseContext.Set<TEntity>().AsNoTracking().FirstOrDefaultAsync();
    
    		return await databaseContext.Set<TEntity>().AsNoTracking().FirstOrDefaultAsync(filter);
    	}
    	catch (Exception ex)
    	{
    		throw ex;
    	}
    }

    Adicionalmente, implementé otro método en el repositorio genérico pero este sí hará Tracking y el cual debe ser invocado al momento de realizar el Update:

    public async Task<TEntity> FirstOrDefaulWithTrackingAsync(Expression<Func<TEntity, bool>> filter = null)
    {
    	try
    	{
    		if (filter == null)
    
    			return await databaseContext.Set<TEntity>().FirstOrDefaultAsync();
    
    		return await databaseContext.Set<TEntity>().FirstOrDefaultAsync(filter);
    	}
    	catch (Exception ex)
    	{
    		throw ex;
    	}
    }

    En método Update del repositorio genérico quedó de la siguiente manera:

    public virtual void Update(TEntity item)
    {
    	databaseContext.Entry(item).State = EntityState.Modified;
    }

    No hago SaveChanges porque estoy aplicando Unit of Work y este es aplicado desde los servicios del Dominio no desde los repositorios.

    public virtual int Update(TEntity item)
    {
    	try
    	{
    		using (var uow = unitOfWorkFactory.Create())
    		{
    			genericRepository.Update(item);
    			return uow.Commit();
    		}
    	}
    	catch (Exception ex)
    	{
    		throw ex;
    	}
    }

    De momento me está funcionando bien, si tuviera algún problema los estaré molestando. Si hay algo que estoy haciendo mal me comentan por favor.

    Muchas gracias.


    sábado, 20 de abril de 2019 20:04
  • La entidad se estaba recuperando previamente en varios lugares y con suguimiento habilitado (Tracking) quizás por eso no dejaba actualizar.la desde un lugar específico. He hecho que las otras consultas que recuperan la entidad y que no vayan a actualizarla, lo hagan sin seguimiento y con esto ya va bien.

    Muchas gracias. Saludos.

    sábado, 20 de abril de 2019 18:20

Todas las respuestas

  • Hola, prueba este código. 

    public virtual TEntity Update(TEntity item)
    {
        var entry = context.Entry(entity);
        if (entry.State == EntityState.Detached || entry.State == EntityState.Modified)
        {
            entry.State = EntityState.Modified; //do it here
    
            context.Set<TEntity>().Attach(entity); //attach
    
            context.SaveChanges(); //save it
        }
        return entry;
    }
    Establece el estado antes see realizar el Attach e inmediatamente el SaveChanges. Seguramente te falte aplicar el SaveChanges en tu código 

    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos


    sábado, 20 de abril de 2019 14:20
    Moderador
  • Sergio,

    Me sigue arrojando el mismo error:

    Attaching an entity of type 'Domain.Core.Entities.Seguridad.CredentialsPolicy' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

    public virtual TEntity Update(TEntity item)
    {
    	try
    	{
    		TEntity result = null;
    		
    		var entry = databaseContext.Entry(item);
    		if (entry.State == EntityState.Detached || entry.State == EntityState.Modified)
    		{
    			entry.State = EntityState.Modified;
    			result = databaseContext.Set<TEntity>().Attach(item);
    		}
    
    		return result;
    	}
    	catch (Exception ex)
    	{
    		throw ex;
    	}
    }
    La excepción ocurre en la línea que que establece el estado, antes del Attach. No coloco el SaveChanges aquí porque de eso se encarga el UnitOfWork que está por fuera pero que sí tiene acceso al Repositorio en cuestión.



    • Editado eduar2083 sábado, 20 de abril de 2019 16:11
    sábado, 20 de abril de 2019 16:07
  • Hola, veo que en tu código no aplicas

    context.SaveChanges();
    Después de realizar el Attach. Puedes aplicarlo como te comenté en mi respuesta? 


    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    sábado, 20 de abril de 2019 16:36
    Moderador
  • La entidad se estaba recuperando previamente en varios lugares y con suguimiento habilitado (Tracking) quizás por eso no dejaba actualizar.la desde un lugar específico. He hecho que las otras consultas que recuperan la entidad y que no vayan a actualizarla, lo hagan sin seguimiento y con esto ya va bien.

    Muchas gracias. Saludos.

    sábado, 20 de abril de 2019 18:20
  • Efectivamente es lo que iba a proponerte. Hacer un AsNoTracking() para que no se haga el seguimiento. Puedes poner el código que te ha funcionado? 

    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó, vótala como útil. Saludos

    sábado, 20 de abril de 2019 18:51
    Moderador
  • Para recuperar la entidad estaba invocando a este método del repositorio genérico pero siempre "trackeando" y eso quizás generaba conflicto de seguimiento en las otras consultas, es por ello, que lo modifiqué para que no haga Tracking.

    public virtual async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> filter = null)
    {
    	try
    	{
    		if (filter == null)
    
    			return await databaseContext.Set<TEntity>().AsNoTracking().FirstOrDefaultAsync();
    
    		return await databaseContext.Set<TEntity>().AsNoTracking().FirstOrDefaultAsync(filter);
    	}
    	catch (Exception ex)
    	{
    		throw ex;
    	}
    }

    Adicionalmente, implementé otro método en el repositorio genérico pero este sí hará Tracking y el cual debe ser invocado al momento de realizar el Update:

    public async Task<TEntity> FirstOrDefaulWithTrackingAsync(Expression<Func<TEntity, bool>> filter = null)
    {
    	try
    	{
    		if (filter == null)
    
    			return await databaseContext.Set<TEntity>().FirstOrDefaultAsync();
    
    		return await databaseContext.Set<TEntity>().FirstOrDefaultAsync(filter);
    	}
    	catch (Exception ex)
    	{
    		throw ex;
    	}
    }

    En método Update del repositorio genérico quedó de la siguiente manera:

    public virtual void Update(TEntity item)
    {
    	databaseContext.Entry(item).State = EntityState.Modified;
    }

    No hago SaveChanges porque estoy aplicando Unit of Work y este es aplicado desde los servicios del Dominio no desde los repositorios.

    public virtual int Update(TEntity item)
    {
    	try
    	{
    		using (var uow = unitOfWorkFactory.Create())
    		{
    			genericRepository.Update(item);
    			return uow.Commit();
    		}
    	}
    	catch (Exception ex)
    	{
    		throw ex;
    	}
    }

    De momento me está funcionando bien, si tuviera algún problema los estaré molestando. Si hay algo que estoy haciendo mal me comentan por favor.

    Muchas gracias.


    sábado, 20 de abril de 2019 20:04