Por Password, te refieres a la contraseña hash, ¿verdad? :-)
Edward Brey
Respuestas:
370
La respuesta de Ladislav se actualizó para usar DbContext (introducido en EF 4.1):
public void ChangePassword(int userId, string password){
var user= new User(){ Id = userId, Password = password };using(var db = new MyEfContextName()){
db.Users.Attach(user);
db.Entry(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();}}
Solo pude hacer que este código funcione agregando db.Configuration.ValidateOnSaveEnabled = false; antes de db.SaveChanges ()?
Jake Drew
3
Qué espacio de nombre incluir para usar db.Entry(user).Property(x => x.Password).IsModified = true;y nodb.Entry(user).Property("Password").IsModified = true;
Johan
55
Este enfoque arroja una OptimisticConcurencyException cuando la tabla tiene un campo de marca de tiempo.
Maksim Vi.
9
Creo que vale la pena mencionar que si está utilizando, db.Configuration.ValidateOnSaveEnabled = false;es posible que desee seguir validando el campo que está actualizando:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Ziul
2
Debe establecer ValidateOnSaveEnabled en falso si ha requerido campos en su tabla que no está proporcionando durante su actualización
Sal
54
Puede decirle a EF qué propiedades deben actualizarse de esta manera:
public void ChangePassword(int userId, string password){
var user= new User{ Id = userId, Password = password };using(var context = new ObjectContext(ConnectionString)){
var users = context.CreateObjectSet<User>();
users.Attach(user);
context.ObjectStateManager.GetObjectStateEntry(user).SetModifiedProperty("Password");
context.SaveChanges();}}
ObjectStateManager no está disponible para DBContext
LoxLox
17
Básicamente tienes dos opciones:
siga el camino EF hasta el final, en ese caso, lo haría
cargar el objeto según lo userIdproporcionado: se carga todo el objeto
actualizar el passwordcampo
guardar el objeto de nuevo usando el .SaveChanges()método del contexto
En este caso, depende de EF cómo manejar esto en detalle. Acabo de probar esto, y en el caso de que solo cambie un solo campo de un objeto, lo que EF crea es más o menos lo que crearías manualmente, algo como:
`UPDATE dbo.Users SET Password =@Password WHERE UserId =@UserId`
Por lo tanto, EF es lo suficientemente inteligente como para descubrir qué columnas realmente han cambiado, y creará una declaración T-SQL para manejar solo las actualizaciones que de hecho son necesarias.
define un procedimiento almacenado que hace exactamente lo que necesita, en el código T-SQL (simplemente actualice la Passwordcolumna para el dado UserIdy nada más, básicamente se ejecuta UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId) y crea una importación de función para ese procedimiento almacenado en su modelo EF y llama a esto funcionar en lugar de seguir los pasos descritos anteriormente
public class Thing
{[Key]public int Id { get;set;}public string Info { get;set;}public string OtherStuff { get;set;}}
dbcontext:
public class MyDataContext : DbContext
{public DbSet<Thing > Things { get;set;}}
código de acceso:
MyDataContext ctx = new MyDataContext();// FIRST create a blank object
Thing thing = ctx.Things.Create();// SECOND set the ID
thing.Id = id;// THIRD attach the thing (id isnot marked as modified)
db.Things.Attach(thing);// FOURTH set the fields you want updated.
thing.OtherStuff ="only want this field updated.";// FIFTH save that thing
db.SaveChanges();
Recibo errores de validación de entidad cuando intento esto, pero seguro que se ve genial.
devlord
¡¡¡No funciona este método !!!: tal vez necesites dar más detalles sobre cómo usarlo !!! - este es el error: "No se pudo adjuntar una entidad del tipo 'Domain.Job' porque otra entidad del mismo tipo ya tiene el mismo valor de clave principal. Esto puede suceder cuando se usa el método 'Attach' o se establece el estado de una entidad "Sin cambios" o "Modificado" si alguna entidad en el 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 ".
Lucian Bumb
Perfec! ¡Comprueba mi respuesta para ver un enfoque flexible para cualquier modelo!
kryp
10
Mientras buscaba una solución a este problema, encontré una variación en la respuesta de GONeale a través del blog de Patrick Desjardins :
public int Update(T entity, Expression<Func<T, object>>[] properties){
DatabaseContext.Entry(entity).State = EntityState.Unchanged;
foreach (var property in properties){
var propertyName = ExpressionHelper.GetExpressionText(property);
DatabaseContext.Entry(entity).Property(propertyName).IsModified = true;}return DatabaseContext.SaveChangesWithoutValidation();}
" Como puede ver, toma como segundo parámetro la expresión de una función. Esto permitirá utilizar este método al especificar en una expresión Lambda qué propiedad actualizar " .
El método que estoy usando actualmente en mi propio código , extendido para manejar también Expresiones de tipo (Linq) ExpressionType.Convert. Esto fue necesario en mi caso, por ejemplo, con Guidy otras propiedades del objeto. Esos fueron 'envueltos' en un Convert () y por lo tanto no manejados por System.Web.Mvc.ExpressionHelper.GetExpressionText.
Cuando uso esto me da el siguiente error: No se puede convertir la expresión Lambda a Tipo 'Expresión <Func <RequestDetail, objeto >> []' porque no es un tipo de delegado
Imran Rizvi
@ImranRizvi, simplemente necesita actualizar los parámetros a: public int Update (entidad T, expresión de parámetros <Func <T, objeto >> [] propiedades) NOTA la palabra clave params antes de la expresión
dalcam
6
Llego tarde al juego aquí, pero así es como lo estoy haciendo, pasé un tiempo buscando una solución con la que estaba satisfecho; esto produce una UPDATEdeclaración SOLO para los campos que se cambian, ya que usted define explícitamente qué son a través de un concepto de "lista blanca" que es más seguro para evitar la inyección de formularios web de todos modos.
Un extracto de mi repositorio de datos ISession:
public bool Update<T>(T item, params string[] changedPropertyNames)where T
: class, new(){
_context.Set<T>().Attach(item);
foreach (var propertyName in changedPropertyNames){//If we can't find the property, this line wil throw an exception,//which is good as we want to know about it
_context.Entry(item).Property(propertyName).IsModified = true;}return true;}
Esto podría envolverse en un intento ... captura si así lo desea, pero personalmente me gusta que mi interlocutor sepa acerca de las excepciones en este escenario.
Se llamaría de esta manera (para mí, esto fue a través de una API web ASP.NET):
if(!session.Update(franchiseViewModel.Franchise, new[]{"Name","StartDate"}))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
¿Entonces tu mejor solución es lo que Elisa? Debe indicar explícitamente qué propiedades permite que se actualicen (al igual que la lista blanca requerida para el UpdateModelcomando ASP.NET MVC ), de esa manera se asegura de que la inyección de formularios de piratas informáticos no pueda ocurrir y que no puedan actualizar los campos que no pueden actualizar. Sin embargo, si alguien puede convertir la matriz de cadena en algún tipo de parámetro de expresiones lambda y trabajar con ella en el Update<T>, excelente
@Elisa Se puede mejorar usando Func <T, List <object>> en lugar de string []
Spongebob Comrade
Incluso más tarde al juego, y tal vez esta es una sintaxis mucho más reciente, pero var entity=_context.Set<T>().Attach(item);seguida por entity.Property(propertyName).IsModified = true;el ciclo debería funcionar.
Auspex
4
Entity Framework realiza un seguimiento de sus cambios en los objetos que ha consultado desde la base de datos a través de DbContext. Por ejemplo, si el nombre de la instancia de DbContext es dbContext
public void ChangePassword(int userId, string password){
var user= dbContext.Users.FirstOrDefault(u=>u.UserId == userId);user.password = password;
dbContext.SaveChanges();}
Esto es incorrecto porque guardaría todo el objeto Usuario con una contraseña modificada.
amuliar el
eso es cierto, pero el resto del objeto Usuario será el mismo que anteriormente en el contexto, lo único que posiblemente sea diferente es la contraseña, por lo que esencialmente solo se actualiza la contraseña.
Tomislav3008
3
Sé que este es un hilo antiguo, pero también estaba buscando una solución similar y decidí ir con la solución @ Doku, así que la proporcioné. Estoy comentando para responder la pregunta hecha por @Imran Rizvi, seguí el enlace @ Doku-so que muestra una implementación similar. La pregunta de @Imran Rizvi fue que estaba recibiendo un error al usar la solución provista 'No se puede convertir la expresión Lambda a Tipo' Expresión> [] 'porque no es un tipo delegado'. Quería ofrecer una pequeña modificación que hice a la solución de @ Doku-so que corrige este error en caso de que alguien más se encuentre con esta publicación y decida usar la solución de @ Doku-so.
El problema es el segundo argumento en el método de actualización,
public int Update(T entity, Expression<Func<T, object>>[] properties).
Para llamar a este método utilizando la sintaxis proporcionada ...
Debe agregar la palabra clave 'params' delante del segundo argumento como tal.
public int Update(T entity, params Expression<Func<T, object>>[] properties)
o si no desea cambiar la firma del método, para llamar al método Actualizar, debe agregar la palabra clave ' nueva ', especificar el tamaño de la matriz y, finalmente, usar la sintaxis del inicializador del objeto de colección para que cada propiedad se actualice como se ve abajo.
Update(Model, new Expression<Func<T, object>>[3]{ d=>d.Name },{ d=>d.SecondProperty },{ d=>d.AndSoOn });
En el ejemplo de @ Doku-so, está especificando una matriz de Expresiones, por lo que debe pasar las propiedades para actualizar en una matriz, debido a la matriz también debe especificar el tamaño de la matriz. Para evitar esto, también puede cambiar el argumento de expresión para usar IEnumerable en lugar de una matriz.
Aquí está mi implementación de la solución de @ Doku-so.
public int Update<TEntity>(LcmsEntities dataContext, DbEntityEntry<TEntity> entityEntry, params Expression<Func<TEntity, object>>[] properties)where TEntity: class
{
entityEntry.State = System.Data.Entity.EntityState.Unchanged;
properties.ToList().ForEach((property)=>{
var propertyName = string.Empty;
var bodyExpression = property.Body;if(bodyExpression.NodeType == ExpressionType.Convert&& bodyExpression is UnaryExpression){
Expression operand =((UnaryExpression)property.Body).Operand;
propertyName =((MemberExpression)operand).Member.Name;}else{
propertyName = System.Web.Mvc.ExpressionHelper.GetExpressionText(property);}
entityEntry.Property(propertyName).IsModified = true;});
dataContext.Configuration.ValidateOnSaveEnabled = false;return dataContext.SaveChanges();}
Uso:
this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);
@ Doku-so proporcionó un enfoque genial usando genéricos, utilicé el concepto para resolver mi problema, pero simplemente no puede usar la solución de @ Doku-so como está y en esta publicación y en la publicación vinculada nadie respondió las preguntas de error de uso.
Yo estaba trabajando en su solución cuando pases programa de línea de entityEntry.State = EntityState.Unchanged;todos los valores actualizados en el parámetro entityEntryget Revert, por lo que no se guardan los cambios, puede usted por favor ayuda en él, gracias
sairfan
3
En EntityFramework Core 2.x no hay necesidad de Attach:
// get a tracked entity
var entity = context.User.Find(userId);
entity.someProp = someValue;// other property changes might come here
context.SaveChanges();
Probé esto en SQL Server y lo perfilé:
exec sp_executesql N'SET NOCOUNT ON;
UPDATE [User] SET [someProp] = @p0
WHERE [UserId] = @p1;
SELECT @@ROWCOUNT;
',N'@p1 int,@p0 bit',@p1=1223424,@p0=1
Find asegura que las entidades ya cargadas no activen un SELECT y también adjunta automáticamente la entidad si es necesario (de los documentos):
/// Finds an entity with the given primarykeyvalues.If an entity with the given primarykeyvalues///is being tracked by the context,then it is returned immediately without making a request to the
///database. Otherwise, a query is made to the databasefor an entity with the given primarykeyvalues///and this entity,if found,is attached to the context and returned.If no entity is found,then///nullis returned.
¿Cómo hacer que este método esté disponible para otra clase, puede ser como un método de extensión?
Velkumar
en este tutorial de .NET CORE muestran las mejores prácticas usando (el nuevo) EF Core para actualizar propiedades específicas en MVC. busque 'TryUpdateModelAsync'.
Guy
1
@Guy Impresionante. Sin embargo, una vez más, la "mejor práctica" de Microsoft es hacer algo diferente a lo que construyen sus herramientas ...
Auspex
Esta es una buena solución.
Timothy Macharia
1
Utilizo ValueInjecternuget para inyectar el modelo de enlace en la entidad de base de datos usando lo siguiente:
public async Task<IHttpActionResult>Add(CustomBindingModel model){
var entity= await db.MyEntities.FindAsync(model.Id);if(entity==null)return NotFound();
entity.InjectFrom<NoNullsInjection>(model);
await db.SaveChangesAsync();return Ok();}
Observe el uso de la convención personalizada que no actualiza las propiedades si son nulas desde el servidor.
No sabrá si la propiedad se borró intencionalmente como nula O simplemente no tenía ningún valor. En otras palabras, el valor de la propiedad solo se puede reemplazar con otro valor pero no se borra.
public void ChangePassword(int userId, string password){
var user= new User{ Id = userId, Password = password };using(var db = new DbContextName()){
db.Entry(user).State = EntityState.Added;
db.SaveChanges();}}
Password
, te refieres a la contraseña hash, ¿verdad? :-)Respuestas:
La respuesta de Ladislav se actualizó para usar DbContext (introducido en EF 4.1):
fuente
db.Entry(user).Property(x => x.Password).IsModified = true;
y nodb.Entry(user).Property("Password").IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
es posible que desee seguir validando el campo que está actualizando:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Puede decirle a EF qué propiedades deben actualizarse de esta manera:
fuente
Básicamente tienes dos opciones:
userId
proporcionado: se carga todo el objetopassword
campo.SaveChanges()
método del contextoEn este caso, depende de EF cómo manejar esto en detalle. Acabo de probar esto, y en el caso de que solo cambie un solo campo de un objeto, lo que EF crea es más o menos lo que crearías manualmente, algo como:
Por lo tanto, EF es lo suficientemente inteligente como para descubrir qué columnas realmente han cambiado, y creará una declaración T-SQL para manejar solo las actualizaciones que de hecho son necesarias.
Password
columna para el dadoUserId
y nada más, básicamente se ejecutaUPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId
) y crea una importación de función para ese procedimiento almacenado en su modelo EF y llama a esto funcionar en lugar de seguir los pasos descritos anteriormentefuente
En Entity Framework Core,
Attach
devuelve la entrada, por lo que todo lo que necesita es:fuente
estoy usando esto:
entidad:
dbcontext:
código de acceso:
fuente
Mientras buscaba una solución a este problema, encontré una variación en la respuesta de GONeale a través del blog de Patrick Desjardins :
(Aquí también se ofrece una solución algo similar: https://stackoverflow.com/a/5749469/2115384 )
El método que estoy usando actualmente en mi propio código , extendido para manejar también Expresiones de tipo (Linq)
ExpressionType.Convert
. Esto fue necesario en mi caso, por ejemplo, conGuid
y otras propiedades del objeto. Esos fueron 'envueltos' en un Convert () y por lo tanto no manejados porSystem.Web.Mvc.ExpressionHelper.GetExpressionText
.fuente
Llego tarde al juego aquí, pero así es como lo estoy haciendo, pasé un tiempo buscando una solución con la que estaba satisfecho; esto produce una
UPDATE
declaración SOLO para los campos que se cambian, ya que usted define explícitamente qué son a través de un concepto de "lista blanca" que es más seguro para evitar la inyección de formularios web de todos modos.Un extracto de mi repositorio de datos ISession:
Esto podría envolverse en un intento ... captura si así lo desea, pero personalmente me gusta que mi interlocutor sepa acerca de las excepciones en este escenario.
Se llamaría de esta manera (para mí, esto fue a través de una API web ASP.NET):
fuente
UpdateModel
comando ASP.NET MVC ), de esa manera se asegura de que la inyección de formularios de piratas informáticos no pueda ocurrir y que no puedan actualizar los campos que no pueden actualizar. Sin embargo, si alguien puede convertir la matriz de cadena en algún tipo de parámetro de expresiones lambda y trabajar con ella en elUpdate<T>
, excelentevar entity=_context.Set<T>().Attach(item);
seguida porentity.Property(propertyName).IsModified = true;
el ciclo debería funcionar.Entity Framework realiza un seguimiento de sus cambios en los objetos que ha consultado desde la base de datos a través de DbContext. Por ejemplo, si el nombre de la instancia de DbContext es dbContext
fuente
Sé que este es un hilo antiguo, pero también estaba buscando una solución similar y decidí ir con la solución @ Doku, así que la proporcioné. Estoy comentando para responder la pregunta hecha por @Imran Rizvi, seguí el enlace @ Doku-so que muestra una implementación similar. La pregunta de @Imran Rizvi fue que estaba recibiendo un error al usar la solución provista 'No se puede convertir la expresión Lambda a Tipo' Expresión> [] 'porque no es un tipo delegado'. Quería ofrecer una pequeña modificación que hice a la solución de @ Doku-so que corrige este error en caso de que alguien más se encuentre con esta publicación y decida usar la solución de @ Doku-so.
El problema es el segundo argumento en el método de actualización,
Para llamar a este método utilizando la sintaxis proporcionada ...
Debe agregar la palabra clave 'params' delante del segundo argumento como tal.
o si no desea cambiar la firma del método, para llamar al método Actualizar, debe agregar la palabra clave ' nueva ', especificar el tamaño de la matriz y, finalmente, usar la sintaxis del inicializador del objeto de colección para que cada propiedad se actualice como se ve abajo.
En el ejemplo de @ Doku-so, está especificando una matriz de Expresiones, por lo que debe pasar las propiedades para actualizar en una matriz, debido a la matriz también debe especificar el tamaño de la matriz. Para evitar esto, también puede cambiar el argumento de expresión para usar IEnumerable en lugar de una matriz.
Aquí está mi implementación de la solución de @ Doku-so.
Uso:
@ Doku-so proporcionó un enfoque genial usando genéricos, utilicé el concepto para resolver mi problema, pero simplemente no puede usar la solución de @ Doku-so como está y en esta publicación y en la publicación vinculada nadie respondió las preguntas de error de uso.
fuente
entityEntry.State = EntityState.Unchanged;
todos los valores actualizados en el parámetroentityEntry
get Revert, por lo que no se guardan los cambios, puede usted por favor ayuda en él, graciasEn EntityFramework Core 2.x no hay necesidad de
Attach
:Probé esto en SQL Server y lo perfilé:
Find asegura que las entidades ya cargadas no activen un SELECT y también adjunta automáticamente la entidad si es necesario (de los documentos):
fuente
Combinando varias sugerencias, propongo lo siguiente:
llamado por
O por
O por
fuente
Utilizo
ValueInjecter
nuget para inyectar el modelo de enlace en la entidad de base de datos usando lo siguiente:Observe el uso de la convención personalizada que no actualiza las propiedades si son nulas desde el servidor.
ValueInjecter v3 +
Uso:
Value Injecter V2
Busca esta respuesta
Consideración
No sabrá si la propiedad se borró intencionalmente como nula O simplemente no tenía ningún valor. En otras palabras, el valor de la propiedad solo se puede reemplazar con otro valor pero no se borra.
fuente
Estaba buscando lo mismo y finalmente encontré la solución
créeme, me funciona como un encanto.
fuente
Esto es lo que uso, usando InjectNonNull personalizado (obj dest, obj src) lo hace completamente flexible
fuente
fuente
fuente