Cuando estoy en un escenario separado y obtengo un dto del cliente que mapeo en una entidad para guardarlo, hago esto:
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
Porque ¿qué es entonces el DbSet.Attach(entity)
o ¿por qué debería usar el método .Attach cuando EntityState.Modified ya adjunta la entidad?
c#
entity-framework
entity-framework-6
Elisabeth
fuente
fuente
Respuestas:
Cuando lo haces
context.Entry(entity).State = EntityState.Modified;
, no solo estás adjuntando la entidad alDbContext
, también estás marcando a toda la entidad como sucia. Esto significa que cuando lo hagacontext.SaveChanges()
, EF generará una declaración de actualización que actualizará todos los campos de la entidad.Esto no siempre se desea.
Por otro lado,
DbSet.Attach(entity)
adjunta la entidad al contexto sin marcarlo como sucio. Es equivalente a hacercontext.Entry(entity).State = EntityState.Unchanged;
Al adjuntar de esta manera, a menos que proceda a actualizar una propiedad en la entidad, la próxima vez que llame
context.SaveChanges()
, EF no generará una actualización de la base de datos para esta entidad.Incluso si planea realizar una actualización de una entidad, si la entidad tiene muchas propiedades (columnas de base de datos) pero solo desea actualizar algunas, puede que le resulte ventajoso hacer una
DbSet.Attach(entity)
, y luego solo actualizar las pocas propiedades que necesitan una actualización. Hacerlo de esta manera generará una declaración de actualización más eficiente de EF. EF solo actualizará las propiedades que modificó (a diferencia decontext.Entry(entity).State = EntityState.Modified;
que hará que se actualicen todas las propiedades / columnas)Documentación relevante: Add / Attach y Entity States .
Ejemplo de código
Digamos que tienes la siguiente entidad:
Si su código se ve así:
El SQL generado se verá así:
Observe cómo la declaración de actualización anterior actualizará todas las columnas, independientemente de si realmente ha cambiado los valores o no.
Por el contrario, si su código utiliza el adjunto "normal" de esta manera:
Entonces la declaración de actualización generada es diferente:
Como puede ver, la declaración de actualización solo actualiza los valores que realmente se cambiaron después de que adjuntó la entidad al contexto. Dependiendo de la estructura de su mesa, esto puede tener un impacto positivo en el rendimiento.
Ahora bien, qué opción es mejor para usted depende completamente de lo que esté tratando de hacer.
fuente
WHERE
cláusula que contiene solo la clave principal y sin ninguna verificación de concurrencia. Para tener verificación de concurrencia, necesito configurar explícitamente una columna como un token de concurrencia o rowVersion. En ese caso, laWHERE
cláusula solo tendrá la clave principal y la columna del token de simultaneidad, no todos los campos. Si sus pruebas muestran lo contrario, me encantaría saberlo.DbContext.Entry(person).CurrentValues
yDbContext.Entry(person).OriginalValues
.Cuando usa el
DbSet.Update
método, Entity Framework marca todas las propiedades de su entidad comoEntityState.Modified
, por lo que las rastrea. Si desea cambiar solo algunas de sus propiedades, no todas, utiliceDbSet.Attach
. Este método crea todas sus propiedadesEntityState.Unchanged
, por lo que debe crear las propiedades que desea actualizarEntityState.Modified
. Por lo tanto, cuando la aplicaciónDbContext.SaveChanges
llegue, solo operará propiedades modificadas.fuente
Además (de la respuesta marcada) hay una diferencia importante entre
context.Entry(entity).State = EntityState.Unchanged
ycontext.Attach(entity)
(en EF Core):Hice algunas pruebas para entenderlo más por mí mismo (por lo tanto, esto también incluye algunas pruebas de referencia general), así que este es mi escenario de prueba:
QueryTrackingBehavior.NoTracking
Estos son los modelos:
Estos son los datos de prueba (originales) en la base de datos:
Para obtener el pedido:
Ahora las pruebas:
Actualización simple con EntityState :
Actualización simple con adjuntar :
Actualización con el cambio de Child-Ids con EntityState :
Actualización con cambio de ID de niños con Attach :
Nota: Esto arroja una excepción, no importa si la identificación se cambió o se estableció en el valor original, parece que el estado de la identificación se establece en "cambiado" y esto no está permitido (porque es la clave principal)
Actualizar con el cambio de Child-Ids como nuevo (no hay diferencia entre EntityState y Attach):
Nota: Vea la diferencia con la actualización con EntityState sin nuevo (arriba). Esta vez, el nombre se actualizará debido a la nueva instancia de usuario.
Actualización con el cambio de Reference-Ids con EntityState :
Actualización con el cambio de los ID de referencia con Adjuntar :
Nota: La referencia se cambiará a Usuario 3, pero también se actualizará el usuario 1, supongo que esto se debe a
order.OrderedByUser.Id
que no ha cambiado (sigue siendo 1).Conclusión Con EntityState tiene más control, pero debe actualizar las subpropiedades (segundo nivel) usted mismo. Con Attach puedes actualizar todo (supongo que con todos los niveles de propiedades), pero tienes que estar atento a las referencias. Solo por ejemplo: si User (OrderedByUser) fuera un dropDown, cambiar el valor a través de un dropDown podría sobrescribir todo el objeto User. En este caso, el dropDown-Value original se sobrescribirá en lugar de la referencia.
Para mí, el mejor caso es establecer objetos como OrderedByUser en nulo y solo establecer el order.OrderedByUserId en el nuevo valor, si solo quiero cambiar la referencia (no importa si EntityState o Attach).
Espero que esto ayude, sé que es mucho texto: D
fuente