Comment mettre à jour un seul champ en utilisant Entity Framework?

Voici la table

utilisateurs

UserId
UserName
Password
EmailAddress

et le code..

public void ChangePassword(int userId, string password){
//code to update the password..
}
144
demandé sur Edward Brey 2010-09-04 16:56:59

13 réponses

Ladislav la réponse de mise à jour pour utiliser DbContext (introduit 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();
  }
}
297
répondu Stuart 2012-04-27 13:27:23

vous pouvez indiquer à EF quelles propriétés doivent être mises à jour de cette manière:

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();
  }
}
49
répondu Ladislav Mrnka 2015-04-17 02:19:01

vous avez essentiellement deux options:

  • allez de L'avant JUSQU'au bout, dans ce cas, vous
    • chargez l'objet en fonction du userId fourni - l'objet entier est chargé
    • mettre à jour le "champ password 1519130920"
    • sauvegardez l'objet en utilisant la méthode .SaveChanges() du contexte."

dans ce cas, c'est à EF comment gérer cela en détail. Je viens de tester cela, et dans le cas où je ne change qu'un seul champ d'un objet, ce que EF crée est à peu près ce que vous créeriez manuellement, aussi - quelque chose comme:

`UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId`

donc EF est assez intelligent pour comprendre quelles colonnes ont effectivement changé, et il va créer une déclaration T-SQL pour gérer seulement les mises à jour qui sont en fait nécessaires.

  • vous définissez une procédure stockée qui fait exactement ce que vous besoin, dans le code T-SQL (il suffit de mettre à jour la colonne Password pour le UserId donné et rien d'autre-exécute essentiellement UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId ) et vous créez une importation de fonction pour cette procédure stockée dans votre modèle EF et vous appelez cette fonction au lieu de faire les étapes décrites ci-dessus
15
répondu marc_s 2010-09-04 13:11:55

j'utilise ceci:

entité:

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; }
}

code d'accès:

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();
9
répondu groggyjava 2012-09-13 19:36:22

en cherchant une solution à ce problème, j'ai trouvé une variation sur la réponse de GONeale à travers le 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();
}

" Comme vous pouvez le voir, il prend comme second paramètre une expression d'un fonction. Cela permettra d'utiliser cette méthode en spécifiant dans un Lambda expression quelle propriété mettre à jour. "

...Update(Model, d=>d.Name);
//or
...Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);

(un peu une solution similaire est également donnée ici: https://stackoverflow.com/a/5749469/2115384 )

la méthode que j'utilise actuellement dans mon propre code , étendu pour traiter aussi (Linq) des Expressions de type ExpressionType.Convert . cela était nécessaire dans mon cas, par exemple avec Guid et d'autres propriétés de l'objet. Ceux-ci ont été "enveloppés" dans un Convert() et ne sont donc pas manipulés par 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();
}
8
répondu Doku-so 2017-05-23 11:54:41

je suis en retard pour le jeu ici, mais c'est comme ça que je le fais, j'ai passé un moment à chercher une solution qui m'a rassasié; cela produit une déclaration UPDATE uniquement pour les champs qui sont modifiés, car vous définissez explicitement ce qu'ils sont à travers un concept de" liste blanche " qui est plus sûr pour empêcher l'injection de forme de web de toute façon.

un extrait de mon dépôt de données 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;
}

cela pourrait être enveloppé dans un essai..catch si vous l'avez souhaité, mais j'aime personnellement que mon interlocuteur soit au courant des exceptions dans ce scénario.

il serait appelé dans quelque chose comme cette façon (pour moi, c'était via un ASP.NET API Web):

if (!session.Update(franchiseViewModel.Franchise, new[]
    {
      "Name",
      "StartDate"
  }))
  throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
5
répondu GONeale 2012-12-20 04:30:45

dans Entity Framework Core, Attach renvoie l'entrée, donc tout ce dont vous avez besoin est:

var user = new User { Id = userId, Password = password };
db.Users.Attach(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();
5
répondu Edward Brey 2017-10-30 17:29:56

Entity framework suit vos modifications sur les objets que vous avez demandés à partir de la base de données via DbContext. Par exemple, si votre nom D'instance DbContext est dbContext

public void ChangePassword(int userId, string password){
     var user = dbContext.Users.FirstOrDefault(u=>u.UserId == userId);
     user.password = password;
     dbContext.SaveChanges();
}
3
répondu Bahtiyar Özdere 2016-10-17 07:40:24

je sais que c'est un vieux fil, mais je cherchais aussi une solution similaire et j'ai décidé d'aller avec la solution @Doku-ainsi prévu. Je commente pour répondre à la question posée par @Imran Rizvi, j'ai suivi le lien @Doku-so qui montre une implémentation similaire. La question de @Imran Rizvi était qu'il obtenait une erreur en utilisant la solution fournie " ne peut pas convertir L'expression Lambda en Type 'Expression> [] 'parce que ce n'est pas un type de délégué'. Je voulais offrir une petite modification que j'ai faite La solution de @Doku-so qui corrige cette erreur au cas où quelqu'un d'autre tomberait sur ce post et déciderait d'utiliser la solution de @Doku-so.

Le problème est que le deuxième argument de la méthode de mise à Jour,

public int Update(T entity, Expression<Func<T, object>>[] properties). 

pour appeler cette méthode en utilisant la syntaxe fournie...

Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn); 

vous devez ajouter le mot-clé 'params' devant le deuxième argument comme tel.

public int Update(T entity, params Expression<Func<T, object>>[] properties)

ou si vous ne voulez pas changer la méthode signature puis pour appeler la méthode de mise à jour, vous devez ajouter le ' nouveau ' mot-clé, spécifier la taille du tableau, puis finalement utiliser la syntaxe collection objet initialiseur pour chaque propriété à mettre à jour comme vu ci-dessous.

Update(Model, new Expression<Func<T, object>>[3] { d=>d.Name }, { d=>d.SecondProperty }, { d=>d.AndSoOn });

dans L'exemple de @Doku-so il spécifie un tableau d'Expressions de sorte que vous devez passer les propriétés à mettre à jour dans un tableau, en raison du tableau vous devez également spécifier la taille du tableau. Éviter ceci vous pouvez également changer l'argument d'expression pour utiliser IEnumerable au lieu d'un tableau.

Voici mon implémentation de la solution 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();
    }

Utilisation:

this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);

@Doku - so fourni une approche cool en utilisant generic's, j'ai utilisé le concept pour résoudre mon problème, mais vous ne pouvez tout simplement pas utiliser @Doku-so la solution comme elle est et à la fois dans ce post et le lien post no répondit l'erreur d'utilisation questions.

2
répondu null 2015-02-25 01:24:03

j'utilise ValueInjecter nuget pour injecter de Liaison de Modèle dans la base de données de l'Entité à l'aide suivante:

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();
}

Remarque l'utilisation de la convention personnalisée qui ne met pas à jour les propriétés si elles sont nulles du serveur.

ValueInjecter v3+

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);
    }
}

Utilisation:

target.InjectFrom<NoNullsInjection>(source);

Valeur Injecteur V2

Recherche cette réponse

Avertissement

vous ne saurez pas si la propriété est intentionnellement autorisée à null ou si elle n'a tout simplement pas de valeur. En d'autres termes, la valeur de la propriété ne peut être remplacé par une autre valeur, mais pas effacé.

1
répondu Korayem 2017-05-23 11:54:41

combinant plusieurs suggestions, je propose ce qui suit:

    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;
        } 
    }

appelé par

UpdateDbEntryAsync(dbc, d => d.Property1);//, d => d.Property2, d => d.Property3, etc. etc.);

ou par

await UpdateDbEntryAsync(dbc, d => d.Property1);

ou par

bool b = UpdateDbEntryAsync(dbc, d => d.Property1).Result;
0
répondu Guy 2015-04-10 07:08:02
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;
    }
}
-1
répondu pintu gupta 2017-11-01 10:31:07
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();
  }
}
-6
répondu Rahul 2014-01-09 04:13:54