Question

Voici la table

Utilisateurs

UserId
UserName
Password
EmailAddress

et le code ..

public void ChangePassword(int userId, string password){
//code to update the password..
}
Était-ce utile?

La solution

La réponse de Ladislav mis à jour pour utiliser DbContext (introduit dans 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();
  }
}

Autres conseils

Vous pouvez dire EF dont les propriétés doivent être mises à jour ainsi:

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

Vous avez essentiellement deux options:

  • aller dans le sens EF tout le chemin, dans ce cas, vous
    • charger l'objet sur la base du userId fourni - l'ensemble de l'objet se charge
    • mettre à jour le champ password
    • Enregistrer l'arrière de l'objet en utilisant la méthode .SaveChanges() du contexte

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

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

EF est assez intelligent pour comprendre les colonnes ont bien changé, et il va créer une instruction 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 dont vous avez besoin, dans le code T-SQL (juste mettre à jour la colonne Password pour l'autre UserId et rien donné - 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 que vous appelez cette fonction au lieu de faire les étapes décrites ci-dessus

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 accesseur:

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

Alors que la recherche d'une solution à ce problème, je l'ai trouvé une variation sur la réponse de GONeale par Patrick blog de 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   une fonction. Cela permettra d'utiliser cette méthode en spécifiant dans un Lambda   expression propriété mise à jour. "

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

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

La méthode que je suis actuellement en utilisant dans mon propre code , étendu à gérer aussi (LINQ) 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. Ce étaient 'enveloppé' dans une Convert () et donc pas traité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();
}

Dans Entity Framework de base, le rendement de l'Attach d'entrée, donc tout ce que vous avez besoin est:

var user = new User { Id = userId, Password = password };
db.Users.Attach(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();

Je suis en retard dans le jeu, mais voilà comment je le fais, j'ai passé un certain temps la chasse pour une solution que je SATISIFIED avec; cela produit une déclaration UPDATE seulement pour les champs qui sont modifiés, comme vous définissez explicitement ce qu'ils sont par le biais d'un concept de « liste blanche » qui est plus sûr pour éviter l'injection de formulaire Web de toute façon.

extrait Un de mon référentiel 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 try..catch si vous le désiriez, mais personnellement, je comme mon interlocuteur de connaître les exceptions dans ce scénario.

Il serait appelé à quelque chose comme la mode (pour moi, ce fut via une API Web ASP.NET):

if (!session.Update(franchiseViewModel.Franchise, new[]
    {
      "Name",
      "StartDate"
  }))
  throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));

Je sais que c'est un vieux fil, mais je cherchais aussi une solution similaire et a décidé d'aller avec la solution @ Doku-ainsi fournie. Je commente répondre à la question posée par @Imran Rizvi, je suivais @ Doku-so lien qui montre une mise en œuvre similaire. La question de @Imran Rizvi était qu'il obtenait une erreur en utilisant la solution fournie «Impossible de convertir l'expression lambda Type « expression> [] » parce qu'il est pas un type de délégué. Je voulais offrir une petite modification que je fis à @ Doku-donc la solution de cette erreur que les solutions au cas où quelqu'un d'autre vient sur ce poste et décide d'utiliser la solution de @ Doku-couça.

La question est 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 « params » mot-clé devant la deuxième arugment comme ainsi.

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

ou si vous ne voulez pas changer la signature de méthode, puis d'appeler la méthode de mise à jour, vous devez ajouter le « nouveau » mot-clé, indiquez la taille de la tableau, puis finalement utiliser l'objet de collection initialiseur syntaxe pour chaque propriété de mise à jour comme on le voit ci-dessous.

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

Dans l'exemple de @ Doku-donc il spécifie un tableau des expressions que vous devez transmettre les propriétés à la mise à jour dans un tableau, en raison du tableau, vous devez également spécifier la taille du tableau. Pour éviter cela, vous pouvez également changer l'argument d'expression à utiliser IEnumerable au lieu d'un tableau.

Voici ma mise en œuvre de la solution de @ Doku-couça.

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 fraîche à l'aide de génériques, j'ai utilisé le concept pour résoudre mon problème, mais vous ne pouvez pas utiliser @ solution de Doku-afin est et à la fois ce poste et aucun poste lié répondait l'utilisation des questions d'erreur.

Entity Framework suivi de vos modifications sur les objets que vous avez interrogés la base de données via DbContext. Par exemple, si vous le 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();
}

Dans EntityFramework de base 2.x il n'y a pas besoin de Attach:

 // get a tracked entity
 var entity = context.User.Find(userId);
 entity.someProp = someValue;
 // other property changes might come here
 context.SaveChanges();

J'ai essayé ceci dans SQL Server et le profilage il:

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

Trouvez des entités déjà Garantit que les chargés ne déclenchent pas un SELECT et attache également une automatiquement l'entité si nécessaire (à partir de la documentation):

    ///     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.

J'utilise ValueInjecter NuGet pour injecter dans la base de données de liaison Modèle d'entité utilisant ce qui suit:

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

Notez l'utilisation de la convention personnalisée qui ne met pas à jour des 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 Injecter V2

cette réponse

caveat

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

Je cherchais même et, enfin, je trouve la solution

using (CString conn = new CString())
{
    USER user = conn.USERs.Find(CMN.CurrentUser.ID);
    user.PASSWORD = txtPass.Text;
    conn.SaveChanges();
}

croyez-moi ça marche pour moi comme un charme.

La combinaison de plusieurs suggestions que 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;
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();
  }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top