En pocas palabras, la excepción se lanza durante la publicación del modelo de envoltura y el cambio del estado de una entrada a 'Modificado'. Antes de cambiar el estado, el estado se establece en 'Separado' pero llamar a Attach () arroja el mismo error. Estoy usando EF6.
Encuentre mi código a continuación (los nombres de los modelos se han cambiado para que sea más fácil de leer)
Modelo
// Wrapper classes
public class AViewModel
{
public A a { get; set; }
public List<B> b { get; set; }
public C c { get; set; }
}
Controlador
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
if (!canUserAccessA(id.Value))
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
var aViewModel = new AViewModel();
aViewModel.A = db.As.Find(id);
if (aViewModel.Receipt == null)
{
return HttpNotFound();
}
aViewModel.b = db.Bs.Where(x => x.aID == id.Value).ToList();
aViewModel.Vendor = db.Cs.Where(x => x.cID == aViewModel.a.cID).FirstOrDefault();
return View(aViewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(AViewModel aViewModel)
{
if (!canUserAccessA(aViewModel.a.aID) || aViewModel.a.UserID != WebSecurity.GetUserId(User.Identity.Name))
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
if (ModelState.IsValid)
{
db.Entry(aViewModel.a).State = EntityState.Modified; //THIS IS WHERE THE ERROR IS BEING THROWN
db.SaveChanges();
return RedirectToAction("Index");
}
return View(aViewModel);
}
Como se muestra arriba de la línea
db.Entry(aViewModel.a).State = EntityState.Modified;
lanza una excepción:
No se pudo adjuntar una entidad de tipo 'A' porque otra entidad del mismo tipo ya tiene el mismo valor de clave principal. Esto puede suceder cuando se usa el método 'Adjuntar' o se establece el estado de una entidad en 'Sin cambios' o 'Modificado' si alguna de las entidades del gráfico tiene valores clave en conflicto. Esto puede deberse a que algunas entidades son nuevas y aún no han recibido valores clave generados por la base de datos. En este caso, use el método 'Agregar' o el estado de la entidad 'Agregado' para rastrear el gráfico y luego establezca el estado de las entidades no nuevas en 'Sin cambios' o 'Modificado' según corresponda.
¿Alguien ve algo incorrecto en mi código o entiende en qué circunstancias arrojaría tal error durante la edición de un modelo?
fuente
EntityState
? Como su entidad proviene de una solicitud de publicación, no debe ser rastreada por el contexto actual, supongo que considera que intenta agregar un elemento con una ID existentedb
instancia es la misma entre sus dos acciones, puede explicar su problema, ya que su elemento es cargado por el método GET (luego es rastreado por el contexto), y es posible que no reconozca el de su método POST como la entidad recuperada antes. .canUserAccessA()
Carga la entidad directamente o como relación de otra entidad?Respuestas:
¡Problema resuelto!
Attach
El método podría ayudar a alguien, pero no ayudaría en esta situación, ya que el documento ya se estaba rastreando mientras se cargaba en la función Editar controlador GET. Adjuntar arrojaría exactamente el mismo error.El problema que encuentro aquí fue causado por una función
canUserAccessA()
que carga la entidad A antes de actualizar el estado del objeto a. Esto estaba arruinando la entidad rastreada y estaba cambiando el estado de un objeto aDetached
.La solución fue modificar
canUserAccessA()
para que el objeto que estaba cargando no fuera rastreado. SeAsNoTracking()
debe llamar a la función mientras se consulta el contexto.Por alguna razón que no podía usar
.Find(aID)
conAsNoTracking()
pero en realidad no importa ya que podía lograr el mismo cambiando la consulta.¡Espero que esto ayude a cualquiera con un problema similar!
fuente
using System.Data.Entity;
usarAsNoTracking()
.Curiosamente:
O si aún no es genérico:
parece haber resuelto mi problema sin problemas.
fuente
AddOrUpdate
es un método de extensión en elSystem.Data.Entity.Migrations
espacio de nombres.Parece que la entidad que está intentando modificar no se está rastreando correctamente y, por lo tanto, no se reconoce como editada, sino que se agrega.
En lugar de establecer el estado directamente, intente hacer lo siguiente:
Además, me gustaría advertirle que su código contiene una vulnerabilidad de seguridad potencial. Si está utilizando la entidad directamente en su modelo de vista, corre el riesgo de que alguien pueda modificar el contenido de la entidad agregando campos con el nombre correcto en el formulario enviado. Por ejemplo, si el usuario agregó un cuadro de entrada con el nombre "A.FirstName" y la entidad contenía dicho campo, entonces el valor se vincularía al modelo de vista y se guardaría en la base de datos, incluso si el usuario no podría cambiarlo en el funcionamiento normal de la aplicación. .
Actualizar:
Para superar la vulnerabilidad de seguridad mencionada anteriormente, nunca debe exponer su modelo de dominio como su modelo de vista, sino utilizar un modelo de vista separado. Entonces su acción recibiría viewmodel que podría mapear de nuevo al modelo de dominio usando alguna herramienta de mapeo como AutoMapper. Esto lo mantendrá a salvo de que el usuario modifique datos confidenciales.
Aquí hay una explicación ampliada:
http://www.stevefenton.co.uk/Content/Blog/Date/201303/Blog/Why-You-Never-Expose-Your-Domain-Model-As-Your-MVC-Model/
fuente
Prueba esto:
fuente
para mí, la copia local fue la fuente del problema. esto lo resolvió
fuente
Mi caso fue que no tenía acceso directo al contexto EF desde mi aplicación MVC.
Entonces, si está utilizando algún tipo de repositorio para la persistencia de la entidad, podría ser apropiado simplemente separar la entidad cargada explícitamente y luego establecer EntityState enlazado en Modificado.
Código de muestra (resumen):
MVC
Repositorio
fuente
Pensé en compartir mi experiencia en este caso, aunque me siento un poco tonto por no darme cuenta antes.
Estoy usando el patrón de repositorio con las instancias de repositorio inyectadas en mis controladores. Los repositorios concretos crean una instancia de mi ModelContext (DbContext) que dura la vida útil del repositorio, que es
IDisposable
eliminado por el controlador.El problema para mí fue que tengo un sello modificado y una versión de fila en mis entidades, por lo que los estaba obteniendo primero para compararlos con los encabezados entrantes. Por supuesto, esto cargó y rastreó la entidad que posteriormente se estaba actualizando.
La solución fue simplemente cambiar el repositorio de un contexto nuevo una vez en el constructor a tener los siguientes métodos:
Esto permite que los métodos del repositorio renueven su instancia de contexto en cada uso llamando
GetDbContext
, o usen una instancia anterior si así lo desean especificando true.fuente
He agregado esta respuesta solo porque el problema se explica en función de un patrón de datos más complejo y me resultó difícil de entender aquí.
Creé una aplicación bastante simple. Este error ocurrió dentro de la acción Editar POST. La acción aceptó ViewModel como parámetro de entrada. La razón para usar ViewModel fue hacer algunos cálculos antes de guardar el registro.
Una vez que la acción pasó por la validación
if(ModelState.IsValid)
, mi mala acción fue proyectar valores de ViewModel en una instancia completamente nueva de Entity. Pensé que tendría que crear una nueva instancia para almacenar datos actualizados y luego guardé dicha instancia.Más tarde me di cuenta de que tenía que leer el registro de la base de datos:
y actualizó este objeto. Todo funciona ahora.
fuente
Tuve este problema con la var local y simplemente lo separé así:
Causas del problema de objetos cargados con la misma clave, por lo que primero separaremos ese objeto y realizaremos la actualización para evitar conflictos entre dos objetos con la misma clave
fuente
Tuve un problema similar, después de sondear durante 2-3 días, se encontró que ".AsNoTracking" debería eliminarse ya que EF no rastrea los cambios y asume que no hay cambios a menos que se adjunte un objeto. Además, si no usamos .AsNoTracking, EF sabe automáticamente qué objeto guardar / actualizar, por lo que no es necesario usar Attach / Added.
fuente
Use
AsNoTracking()
donde obtiene su consulta.fuente
Encontré este error donde
Cambié el método B para tener una declaración de uso y confiar solo en el db2 local . Después:
fuente
De forma similar a lo que dice Luke Puplett, el problema puede deberse a no disponer o crear correctamente su contexto.
En mi caso, tenía una clase que aceptaba un contexto llamado
ContextService
:Mi servicio de contexto tenía una función que actualiza una entidad usando un objeto de entidad instanciado:
Todo esto estuvo bien, mi controlador donde inicialicé el servicio fue el problema. Mi controlador originalmente se veía así:
Lo cambié a esto y el error desapareció:
fuente
Este problema también puede ser visto durante
ViewModel
alEntityModel
mapeo (mediante el usoAutoMapper
, etc.) y tratando de incluircontext.Entry().State
ycontext.SaveChanges()
tal usando bloques como se muestra a continuación resolvería el problema. Tenga en cuenta que elcontext.SaveChanges()
método se usa dos veces en lugar de usar solo después,if-block
ya que también debe usarse en el bloque de uso.Espero que esto ayude...
fuente
Aquí lo que hice en el caso similar.
Esa situación significa que la misma entidad ya existía en el contexto, por lo que seguir puede ayudar
Primero verifique desde ChangeTracker si la entidad está en el contexto
Si existiera
fuente
Me las arreglo para solucionar el problema actualizando el estado. cuando activa la búsqueda o cualquier otra operación de consulta en el mismo estado de registro se ha actualizado con modificado, por lo que debemos establecer el estado en Separado, entonces puede activar su cambio de actualización
fuente
Resuelvo este problema con un bloque "using"
Aquí es donde me sale la idea https://social.msdn.microsoft.com/Forums/sqlserver/es-ES/b4b350ba-b0d5-464d-8656-8c117d55b2af/problema-al-modificar-en-entity-framework?forum = vcses está en español (busque la segunda respuesta)
fuente
puede usar un método agregado como;
pero en muchos casos, si desea utilizar más de un modelo en ese momento, esto no funcionará porque la entidad ya está adjunta a otra entidad. Entonces, en ese momento puede usar el método ADDOrUpdate Entity Migration que simplemente migra el objeto de uno a otro y, como resultado, no obtendrá ningún error.
fuente
Borrar todo el estado
dbContextGlobalERP.ChangeTracker.Entries (). Donde (e => e.Entity! = null) .ToList (). ForEach (e => e.State = EntityState.Detached);
fuente