Respuestas:
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();
}
}
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:
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();
}
}
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:
`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.
Password
columna para el dado UserId
y 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 anteriormenteEn Entity Framework Core, Attach
devuelve la entrada, por lo que todo lo que necesita es:
var user = new User { Id = userId, Password = password };
db.Users.Attach(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();
estoy usando esto:
entidad:
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 is not 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();
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 " .
...Update(Model, d=>d.Name);
//or
...Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
(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, con Guid
y otras propiedades del objeto. Esos fueron 'envueltos' en un Convert () y por lo tanto no manejados por System.Web.Mvc.ExpressionHelper.GetExpressionText
.
public int Update(T entity, Expression<Func<T, object>>[] properties)
{
DbEntityEntry<T> entry = dataContext.Entry(entity);
entry.State = EntityState.Unchanged;
foreach (var property in properties)
{
string propertyName = "";
Expression 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);
}
entry.Property(propertyName).IsModified = true;
}
dataContext.Configuration.ValidateOnSaveEnabled = false;
return dataContext.SaveChanges();
}
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:
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));
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 el Update<T>
, excelente
var entity=_context.Set<T>().Attach(item);
seguida por entity.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
public void ChangePassword(int userId, string password){
var user = dbContext.Users.FirstOrDefault(u=>u.UserId == userId);
user.password = password;
dbContext.SaveChanges();
}
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 ...
Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
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.
entityEntry.State = EntityState.Unchanged;
todos los valores actualizados en el parámetro entityEntry
get Revert, por lo que no se guardan los cambios, puede usted por favor ayuda en él, gracias
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 primary key values. If an entity with the given primary key values
/// 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 database for an entity with the given primary key values
/// and this entity, if found, is attached to the context and returned. If no entity is found, then
/// null is returned.
Combinando varias sugerencias, propongo lo siguiente:
async Task<bool> UpdateDbEntryAsync<T>(T entity, params Expression<Func<T, object>>[] properties) where T : class
{
try
{
var entry = db.Entry(entity);
db.Set<T>().Attach(entity);
foreach (var property in properties)
entry.Property(property).IsModified = true;
await db.SaveChangesAsync();
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("UpdateDbEntryAsync exception: " + ex.Message);
return false;
}
}
llamado por
UpdateDbEntryAsync(dbc, d => d.Property1);//, d => d.Property2, d => d.Property3, etc. etc.);
O por
await UpdateDbEntryAsync(dbc, d => d.Property1);
O por
bool b = UpdateDbEntryAsync(dbc, d => d.Property1).Result;
Utilizo ValueInjecter
nuget 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.
public class NoNullsInjection : LoopInjection
{
protected override void SetValue(object source, object target, PropertyInfo sp, PropertyInfo tp)
{
if (sp.GetValue(source) == null) return;
base.SetValue(source, target, sp, tp);
}
}
Uso:
target.InjectFrom<NoNullsInjection>(source);
Busca esta respuesta
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.
Estaba buscando lo mismo y finalmente encontré la solución
using (CString conn = new CString())
{
USER user = conn.USERs.Find(CMN.CurrentUser.ID);
user.PASSWORD = txtPass.Text;
conn.SaveChanges();
}
créeme, me funciona como un encanto.
Esto es lo que uso, usando InjectNonNull personalizado (obj dest, obj src) lo hace completamente flexible
[HttpPost]
public async Task<IActionResult> Post( [FromQuery]Models.Currency currency ) {
if ( ModelState.IsValid ) {
// find existing object by Key
Models.Currency currencyDest = context.Currencies.Find( currency.Id );
context.Currencies.Attach( currencyDest );
// update only not null fields
InjectNonNull( currencyDest, currency );
// save
await context.SaveChangesAsync( );
}
return Ok();
}
// Custom method
public static T InjectNonNull<T>( T dest, T src ) {
foreach ( var propertyPair in PropertyLister<T, T>.PropertyMap ) {
var fromValue = propertyPair.Item2.GetValue( src, null );
if ( fromValue != null && propertyPair.Item1.CanWrite ) {
propertyPair.Item1.SetValue( dest, fromValue, null );
}
}
return dest;
}
public async Task<bool> UpdateDbEntryAsync(TEntity entity, params Expression<Func<TEntity, object>>[] properties)
{
try
{
this.Context.Set<TEntity>().Attach(entity);
EntityEntry<TEntity> entry = this.Context.Entry(entity);
entry.State = EntityState.Modified;
foreach (var property in properties)
entry.Property(property).IsModified = true;
await this.Context.SaveChangesAsync();
return true;
}
catch (Exception ex)
{
throw ex;
}
}
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? :-)