Voici la table
tilisateurs
UserId
UserName
Password
EmailAddress
et le code ..
public void ChangePassword(int userId, string password){
//code to update the password..
}
La réponse de Ladislav a été mise à 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();
}
}
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();
}
}
Vous avez essentiellement deux options:
userId
fournie - l'objet entier est chargépassword
.SaveChanges()
du contexteDans ce cas, il appartient à EF de gérer cela en détail. Je viens de tester cela, et dans le cas où je ne modifie qu'un seul champ d'un objet, ce que EF crée correspond à peu près à ce que vous créez manuellement: quelque chose comme:
`UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId`
EF est donc assez intelligent pour déterminer quelles colonnes ont bien été modifiées et il créera une instruction T-SQL pour gérer uniquement les mises à jour réellement nécessaires.
Password
pour la variable UserId
et rien d’autre - exécute en gros 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 suivre les étapes décrites ci-dessusj'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();
En cherchant une solution à ce problème, j'ai trouvé une variation de la réponse de GONeale à travers 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'une fonction. Cela permettra d'utiliser cette méthode en spécifiant dans une expression Lambda quelle propriété mettre à jour. "
...Update(Model, d=>d.Name);
//or
...Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
(Une solution un peu similaire est également donnée ici: https://stackoverflow.com/a/5749469/2115384 )
La méthode que j'utilise actuellement dans mon propre code , étendue pour gérer également les expressions (Linq) de type ExpressionType.Convert
. Cela était nécessaire dans mon cas, par exemple avec Guid
et d'autres propriétés d'objet. Ceux qui ont été 'emballés' dans un fichier Convert () et ne sont donc pas géré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 Core, Attach
renvoie l'entrée. Il suffit donc de:
var user = new User { Id = userId, Password = password };
db.Users.Attach(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();
Je suis en retard au jeu ici, mais c’est comme cela que je le fais. J'ai passé un certain temps à chercher une solution qui me satisfaisait; cela produit une instruction UPDATE
UNIQUEMENT pour les champs modifiés, car vous définissez explicitement ce qu’ils sont au moyen d’un concept de "liste blanche" qui est plus sûr pour empêcher l’injection de formulaires Web.
Un extrait 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 emballé dans un try..catch si vous le souhaitiez, mais personnellement, j'aime bien que mon correspondant connaisse les exceptions de ce scénario.
Cela s'appellerait de cette manière (pour moi, c'était 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 également une solution similaire et j'ai décidé de choisir la solution @ Doku-so fournie. 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 'Impossible de convertir une expression Lambda en Type' Expression> [] 'car il ne s'agit pas d'un type de délégué'. Je voulais proposer une petite modification apportée à la solution de @ Doku-so, qui corrige cette erreur si quelqu'un d'autre découvrait ce message et décidait d'utiliser la solution de @ Doku-so.
La question est le deuxième argument de la méthode Update,
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 second argument.
public int Update(T entity, params Expression<Func<T, object>>[] properties)
ou si vous ne souhaitez pas modifier la signature de la méthode, puis pour appeler la méthode Update, vous devez ajouter le signe ' new 'mot-clé, spécifiez la taille du tableau, puis utilisez finalement la syntaxe d'initialiseur d'objet de collection pour chaque propriété à mettre à jour, comme indiqué 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. Vous devez donc transmettre les propriétés pour 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 pour qu'il utilise 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 a fourni une approche intéressante en utilisant des génériques. J'ai utilisé le concept pour résoudre mon problème, mais vous ne pouvez pas utiliser la solution de @ Doku-so en l'état. Dans cet article et dans l'article lié, personne n'a répondu aux questions d'erreur d'utilisation.
Entity Framework suit vos modifications sur les objets que vous avez interrogés depuis une 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();
}
Dans EntityFramework Core 2.x, il n'est pas nécessaire d'utiliser Attach
:
// get a tracked entity
var entity = context.User.Find(userId);
entity.someProp = someValue;
// other property changes might come here
context.SaveChanges();
Essayé cela dans SQL Server et le profiler:
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
La recherche permet de s’assurer que les entités déjà chargées ne déclenchent pas un SELECT et l’attache automatiquement 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.
Je cherchais pareil et finalement j'ai trouvé la solution
using (CString conn = new CString())
{
USER user = conn.USERs.Find(CMN.CurrentUser.ID);
user.PASSWORD = txtPass.Text;
conn.SaveChanges();
}
croyez-moi cela fonctionne pour moi comme un charme.
J'utilise ValueInjecter
nuget pour injecter le modèle de liaison dans la base de données Entity en utilisant les éléments suivants:
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 les propriétés si elles sont null du serveur.
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);
}
}
Usage:
target.InjectFrom<NoNullsInjection>(source);
Recherche cette réponse
Vous ne saurez pas si la propriété est intentionnellement effacée à null OR elle n'a simplement aucune valeur. En d'autres termes, la valeur de la propriété ne peut être remplacée que par une autre valeur, mais pas effacée.
En 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;