'How Working With Entity Framework Detached Objects?
hello community I have been trying to solve this problem for a long time, I am using blazor webassembly , dotnet core, and EntityFramework, I want to update the data of my person object, this object contains related data, neighborhood, municipality and state, if I update only the person object it works well if it updates, but if in the query I include the navigation properties with an inlcude it no longer updates the navigation properties
Error:
System.InvalidOperationException: The instance of entity type 'Status' cannot be tracked because another instance with the same key value for {'StatusId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
I have seen that there are already similar questions, but the truth is that I cannot find a solution for my problem.
I tried what the following blog says:
but it didn't work quite right
[HttpPut]
public async Task<ActionResult> Put(Persona persona)
{
var user = await _userManager.GetUserAsync(HttpContext.User);
var oldpersona = await context.Personas.FindAsync(persona.PersonaId);
context.Entry(oldpersona).CurrentValues.SetValues(persona);
context.Entry(oldpersona).CurrentValues.SetValues(persona.Colonia);
context.Entry(oldpersona).CurrentValues.SetValues(persona.Municipio);
context.Entry(oldpersona).CurrentValues.SetValues(persona.Estado);
await context.SaveChangesAsync(user.Id);
return NoContent();
}
I have read that I can use .AsNoTracking() to not track the entities but if I need to track them I am going to edit them.
this is my code in controller Person:
[HttpGet("{id}")]
public async Task<ActionResult<Persona>> Get(int id)
{
var persona = await context.Personas.Where(x => x.PersonaId == id)
.Include(x => x.Colonia)
.Include(x => x.Municipio)
.Include(x => x.Estado)
.FirstOrDefaultAsync();
if (persona == null) { return NotFound(); }
return persona;
}
[HttpPut]
public async Task<ActionResult> Put(Persona persona)
{
context.Attach(persona).State = EntityState.Modified;
await context.SaveChangesAsync();
return NoContent();
}
this is my related classes:
public class Persona
{
[Key]
public int PersonaId { get; set; }
public string Nombre { get; set; }
public string Ap_Paterno { get; set; }
public string Ap_Materno { get; set; }
public Sexo Sexo { get; set; }
public DateTime? FechadeNacimiento {get; set;}
public int Edad
{
get
{
DateTime now = DateTime.Today;
int Edad = DateTime.Today.Year - FechadeNacimiento.Value.Year;
if (DateTime.Today < FechadeNacimiento.Value.AddYears(Edad))
return --Edad;
else
return Edad;
}
}
public string Email { get; set; }
public string Telefono { get; set; }
public string Celular { get; set; }
[StringLength(12)]
public string Nss { get; set; }
[StringLength(18)]
public string Curp { get; set; }
[StringLength(13)]
public string Rfc { get; set; }
public string Observaciones { get; set; }
public string Calle { get; set; }
public string Num_Ext { get; set; }
public string Num_Int { get; set; }
public int CodigoPostal { get; set; }
public DateTime? FechaRegistro { get; set; }
public bool Activo { get; set; }
//propiedades de navegacion
public int? ColoniaId { get; set; }
public virtual Colonia Colonia { get; set; }
public int? MunicipioId { get; set; }
public virtual Municipio Municipio { get; set; }
public int? EstadoId { get; set; }
public virtual Estado Estado { get; set; }
}
public enum Sexo
{
Masculino,
Femenino,
Indefinido
}
public class Estado
{
[Key]
public int EstadoId { get; set; }
[Required(ErrorMessage = "El campo {0} es requerido")]
public string Nombre { get; set; }
public DateTime? FechaRegistro { get; set; }
public bool Activo { get; set; }/
//propiedades de navegacion
public int ZonaId{ get; set; }
public virtual Zona Zona { get; set; }
}
public class Municipio
{
[Key]
public int MunicipioId { get; set; }
[Required(ErrorMessage = "El campo {0} es requerido")]
public string Nombre { get; set; }
public DateTime? FechaRegistro { get; set; }
public bool Activo { get; set; }
//propiedades de navegacion
public int EstadoId { get; set; }
public virtual Estado Estado { get; set; }
}
public class Colonia
{
[Key]
public int ColoniaId { get; set; }
[Required(ErrorMessage = "El campo {0} es requerido")]
public int CodigoPostal { get; set; }
public Asentamiento Asentamiento { get; set; }
[Required(ErrorMessage = "El campo {0} es requerido")]
public string NombreColonia { get; set; }
public DateTime? FechaRegistro { get; set; }
public bool Activo { get; set; }
//propiedades de navegacion
public int MunicipioId { get; set; }
public virtual Municipio Municipio { get; set; }
public int EstadoId { get; set; }
public virtual Estado Estado { get; set; }
}
public enum Asentamiento
{
Colonia,
Fraccionamiento,
Condominio,
[Display(Name = "Unidad habitacional")]
Unidadhabitacional,
Barrio,
Equipamiento,
[Display(Name = "Zona comercial")]
Zonacomercial,
Rancho,
Rancheria,
[Display(Name = "Parque industrial")]
Parqueindustrial,
Granja,
Pueblo,
Ejido,
[Display(Name = " Zona federal")]
Zonafederal,
Aeropuerto,
Hacienda,
Paraje
}
I guess the problem is that when I do the include, in municipality inside it brings state and colony inside it brings state and municipality, that's why EF tracks those entities more than once, but how can I solve it?
var person = await context.People.Where(x => x.PersonId == id) .Include(x => x.Colony) .Include(x => x.Municipality) .Include(x => x.Status) .FirstOrDefaultAsync();
There isn't a way to do something like this:
var person = await context.People.Where(x => x.PersonId == id) .Include(x => x.Colony) .Include(x => x.Municipality).ThenInclude(x=>x.State).AsNoTracking() .Include(x => x.Status) .FirstOrDefaultAsync(); if (person == null) { return NotFound(); } return person;
so that in the municipality it does not track the entity that is within it in this case state and does not interfere with the state include that I already have
what is the best way to edit entities with navigation properties ??
I have also seen that they use Detach but I really don't know how to use this in my code.
Entry<Persona>(persona).State = EntityState.Detached;
Solution 1:[1]
just try this
[HttpPut]
public async Task<ActionResult> Put(Persona persona)
{
var existedPersona = await context.Personas.Where(x => x.PersonaId == persona.PersonaId)
.FirstOrDefaultAsync();
if (existedPersona == null) { return NotFound(); }
context.Entry(existedPersona).CurrentValues.SetValues(persona);
await context.SaveChangesAsync();
return NoContent();
}
if you have some updates in navigation properties too, then include them
var existedPersona = await context.Personas.Where(x => x.PersonaId == id)
.Include(x => x.Colonia)
.Include(x => x.Municipio)
.Include(x => x.Estado)
.FirstOrDefaultAsync();
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Serge |
