Par Password, vous voulez dire un mot de passe haché, non? :-)
Edward Brey
Réponses:
370
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();}}
Je n'ai pu faire fonctionner ce code qu'en ajoutant db.Configuration.ValidateOnSaveEnabled = false; avant db.SaveChanges ()?
Jake Drew
3
Quel espace de noms inclure à utiliser db.Entry(user).Property(x => x.Password).IsModified = true;et nondb.Entry(user).Property("Password").IsModified = true;
Johan
5
Cette approche lève une exception OptimisticConcurencyException lorsque la table a un champ d'horodatage.
Maksim Vi.
9
Je pense qu'il vaut la peine de mentionner que si vous utilisez, db.Configuration.ValidateOnSaveEnabled = false;vous voudrez peut-être continuer à valider le champ que vous mettez à jour:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Ziul
2
Vous devez définir ValidateOnSaveEnabled sur false si vous avez des champs obligatoires dans votre table que vous ne fournissez pas lors de votre mise à jour
Sal
54
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();}}
ObjectStateManager n'est pas disponible pour DBContext
LoxLox
17
Vous avez essentiellement deux options:
aller jusqu'au bout de l'EF, dans ce cas, vous
charger l'objet en fonction de ce qui est userIdfourni - l'objet entier est chargé
mettre à jour le passwordchamp
sauvegarder l'objet en utilisant la .SaveChanges()méthode 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éez manuellement aussi - quelque chose comme:
`UPDATE dbo.Users SET Password =@Password WHERE UserId =@UserId`
Ainsi, EF est suffisamment intelligent pour déterminer quelles colonnes ont effectivement changé, et il créera une instruction T-SQL pour gérer uniquement 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, en code T-SQL (mettez simplement à jour la Passwordcolonne pour le donné UserIdet rien d'autre - s'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 ceci fonction au lieu de suivre les étapes décrites ci-dessus
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 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();
J'obtiens des erreurs de validation d'entité lorsque j'essaye, mais ça a l'air cool.
devlord
Cette méthode ne fonctionne pas !!!: peut-être avez-vous besoin de plus de détails sur son utilisation !!! - ceci est l'erreur: "L'association d'une entité de type 'Domain.Job' a échoué car une autre entité du même type a déjà la même valeur de clé primaire. Cela peut se produire lors de l'utilisation de la méthode 'Attach' ou de la définition de l'état d'une entité à "Inchangé" ou "Modifié" si des entités du graphique ont des valeurs de clé en conflit. Cela peut être dû au fait que certaines entités sont nouvelles et n'ont pas encore reçu de valeurs de clé générées par la base de données. "
Lucian Bumb
Perfec! Vérifiez ma réponse pour voir une approche flexible pour n'importe quel modèle!
kryp
10
En cherchant une solution à ce problème, j'ai trouvé une variante de la réponse de GONeale sur 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 deuxième paramètre l'expression d'une fonction. Cela permettra d'utiliser cette méthode en spécifiant dans une expression Lambda la propriété à mettre à jour. "
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 Guidet d'autres propriétés d'objet. Celles-ci ont été «enveloppées» dans un Convert () et donc non gérées par System.Web.Mvc.ExpressionHelper.GetExpressionText.
Lorsque j'utilise cela, cela me donne l'erreur suivante, Impossible de convertir l'expression Lambda en type 'Expression <Func <RequestDetail, object >> []' car ce n'est pas un type de délégué
Imran Rizvi
@ImranRizvi, il vous suffit de mettre à jour les paramètres en: public int Update (entité T, params Expression <Func <T, object >> [] properties) REMARQUE le mot-clé params avant expression
dalcam
6
Je suis en retard au jeu ici, mais c'est comme ça que je le fais, j'ai passé un certain temps à chercher une solution dont j'étais satisfait; cela produit une UPDATEinstruction UNIQUEMENT pour les champs qui sont modifiés, car vous définissez explicitement ce qu'ils sont via un concept de «liste blanche» qui est plus sûr pour empêcher l'injection de formulaire Web de toute façon.
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 enveloppé dans un try..catch si vous le souhaitez, mais j'aime personnellement que mon interlocuteur connaisse les exceptions de ce scénario.
Il serait appelé de cette façon (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));
Alors, votre meilleure solution est ce qu'Elisa? Vous devez indiquer explicitement les propriétés que vous autorisez à être mises à jour (tout comme la liste blanche requise pour la UpdateModelcommande d' ASP.NET MVC ), de cette façon vous vous assurez que l'injection de formulaire de pirate ne peut pas se produire et qu'ils ne peuvent pas mettre à jour les champs qu'ils ne sont pas autorisés à mettre à jour. Si toutefois quelqu'un peut convertir le tableau de chaînes en une sorte de paramètre d'expressions lambda et travailler avec lui dans le Update<T>, super
@Elisa Il peut être amélioré en utilisant Func <T, List <object>> au lieu de string []
Spongebob Comrade
Même plus tard dans le jeu, et c'est peut-être une syntaxe beaucoup plus récente, mais var entity=_context.Set<T>().Attach(item);suivie par entity.Property(propertyName).IsModified = true;dans la boucle devrait fonctionner.
Auspex
4
Le cadre d'entité suit vos modifications sur les objets que vous avez interrogé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();}
C'est faux car cela enregistrerait tout l'objet utilisateur avec un mot de passe modifié.
amuliar
c'est vrai, mais le reste de l'objet User sera le même que précédemment dans le contexte, la seule chose qui sera peut-être différente est le mot de passe, donc il ne met essentiellement à jour que le mot de passe.
Tomislav3008
3
Je sais que c'est un vieux fil mais je cherchais également une solution similaire et j'ai décidé d'aller avec 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 l'expression Lambda en type' Expression> [] 'car ce n'est pas un type délégué'. Je voulais proposer une petite modification que j'ai apportée à la solution de @ Doku-so qui corrige cette erreur au cas où quelqu'un d'autre rencontrerait ce message et déciderait d'utiliser la solution de @ Doku-so.
Le problème 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 ...
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 signature de la méthode, pour appeler la méthode Update, vous devez ajouter le mot-clé ' new ', spécifier la taille du tableau, puis enfin utiliser la syntaxe d'initialisation d'objet de collection pour chaque propriété à mettre à jour comme indiqué au dessous de.
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 à mettre à jour dans un tableau, à cause du tableau, vous devez également spécifier la taille du tableau. Pour éviter cela, vous pouvez également modifier l'argument de l'expression pour utiliser IEnumerable au lieu d'un tableau.
Voici ma mise en œuvre 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();}
Usage:
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 tout simplement pas utiliser la solution de @ Doku-so telle quelle et dans cet article et dans l'article lié, personne n'a répondu aux questions sur les erreurs d'utilisation.
Je travaillais sur votre solution lorsque le programme passe la ligne entityEntry.State = EntityState.Unchanged;toutes les valeurs mises à jour dans le paramètre entityEntrysont annulées, donc aucune modification n'est enregistrée, pouvez-vous s'il vous plaît aider, merci
sairfan
3
Dans EntityFramework Core 2.x, il n'est pas nécessaire 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é cela dans SQL Server et en le profilant:
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 garantit que les entités déjà chargées ne déclenchent pas de SELECT et attache également automatiquement l'entité si nécessaire (à partir de la documentation):
/// 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.
Comment rendre cette méthode disponible pour une autre classe, peut être comme une méthode d'extension?
Velkumar le
dans ce didacticiel .NET CORE, ils montrent les meilleures pratiques en utilisant (le nouveau) EF Core pour mettre à jour des propriétés spécifiques dans MVC. recherchez «TryUpdateModelAsync».
Guy
1
@Guy Awesome. Cependant, une fois de plus, la "meilleure pratique" de Microsoft est de faire autre chose que ce que leurs outils construisent ...
Auspex
C'est une bonne solution.
Timothy Macharia le
1
J'utilise ValueInjecternuget pour injecter le modèle de liaison dans l'entité de base de données en 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 d'une convention personnalisée qui ne met pas à jour les propriétés si elles sont nulles à partir du serveur.
Vous ne saurez pas si la propriété est intentionnellement effacée à null OU si elle n'a simplement aucune valeur. En d'autres termes, la valeur de propriété ne peut être remplacée que par une autre valeur mais pas effacée.
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
, vous voulez dire un mot de passe haché, non? :-)Réponses:
La réponse de Ladislav a été mise à jour pour utiliser DbContext (introduit dans EF 4.1):
la source
db.Entry(user).Property(x => x.Password).IsModified = true;
et nondb.Entry(user).Property("Password").IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
vous voudrez peut-être continuer à valider le champ que vous mettez à jour:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Vous pouvez indiquer à EF quelles propriétés doivent être mises à jour de cette manière:
la source
Vous avez essentiellement deux options:
userId
fourni - l'objet entier est chargépassword
champ.SaveChanges()
méthode du contexteDans 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éez manuellement aussi - quelque chose comme:
Ainsi, EF est suffisamment intelligent pour déterminer quelles colonnes ont effectivement changé, et il créera une instruction T-SQL pour gérer uniquement les mises à jour qui sont en fait nécessaires.
Password
colonne pour le donnéUserId
et rien d'autre - s'exécute essentiellementUPDATE 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 ceci fonction au lieu de suivre les étapes décrites ci-dessusla source
Dans Entity Framework Core,
Attach
renvoie l'entrée, donc tout ce dont vous avez besoin est:la source
j'utilise ceci:
entité:
dbcontext:
code d'accès:
la source
En cherchant une solution à ce problème, j'ai trouvé une variante de la réponse de GONeale sur le blog de Patrick Desjardins :
(Une solution quelque 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 avecGuid
et d'autres propriétés d'objet. Celles-ci ont été «enveloppées» dans un Convert () et donc non gérées parSystem.Web.Mvc.ExpressionHelper.GetExpressionText
.la source
Je suis en retard au jeu ici, mais c'est comme ça que je le fais, j'ai passé un certain temps à chercher une solution dont j'étais satisfait; cela produit une
UPDATE
instruction UNIQUEMENT pour les champs qui sont modifiés, car vous définissez explicitement ce qu'ils sont via un concept de «liste blanche» qui est plus sûr pour empêcher l'injection de formulaire Web de toute façon.Un extrait de mon référentiel de données ISession:
Cela pourrait être enveloppé dans un try..catch si vous le souhaitez, mais j'aime personnellement que mon interlocuteur connaisse les exceptions de ce scénario.
Il serait appelé de cette façon (pour moi, c'était via une API Web ASP.NET):
la source
UpdateModel
commande d' ASP.NET MVC ), de cette façon vous vous assurez que l'injection de formulaire de pirate ne peut pas se produire et qu'ils ne peuvent pas mettre à jour les champs qu'ils ne sont pas autorisés à mettre à jour. Si toutefois quelqu'un peut convertir le tableau de chaînes en une sorte de paramètre d'expressions lambda et travailler avec lui dans leUpdate<T>
, supervar entity=_context.Set<T>().Attach(item);
suivie parentity.Property(propertyName).IsModified = true;
dans la boucle devrait fonctionner.Le cadre d'entité suit vos modifications sur les objets que vous avez interrogés à partir de la base de données via DbContext. Par exemple, si votre nom d'instance DbContext est dbContext
la source
Je sais que c'est un vieux fil mais je cherchais également une solution similaire et j'ai décidé d'aller avec 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 l'expression Lambda en type' Expression> [] 'car ce n'est pas un type délégué'. Je voulais proposer une petite modification que j'ai apportée à la solution de @ Doku-so qui corrige cette erreur au cas où quelqu'un d'autre rencontrerait ce message et déciderait d'utiliser la solution de @ Doku-so.
Le problème est le deuxième argument de la méthode Update,
Pour appeler cette méthode en utilisant la syntaxe fournie ...
Vous devez ajouter le mot-clé «params» devant le deuxième argument comme tel.
ou si vous ne voulez pas changer la signature de la méthode, pour appeler la méthode Update, vous devez ajouter le mot-clé ' new ', spécifier la taille du tableau, puis enfin utiliser la syntaxe d'initialisation d'objet de collection pour chaque propriété à mettre à jour comme indiqué au dessous de.
Dans l'exemple de @ Doku-so, il spécifie un tableau d'expressions, vous devez donc transmettre les propriétés à mettre à jour dans un tableau, à cause du tableau, vous devez également spécifier la taille du tableau. Pour éviter cela, vous pouvez également modifier l'argument de l'expression pour utiliser IEnumerable au lieu d'un tableau.
Voici ma mise en œuvre de la solution de @ Doku-so.
Usage:
@ 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 tout simplement pas utiliser la solution de @ Doku-so telle quelle et dans cet article et dans l'article lié, personne n'a répondu aux questions sur les erreurs d'utilisation.
la source
entityEntry.State = EntityState.Unchanged;
toutes les valeurs mises à jour dans le paramètreentityEntry
sont annulées, donc aucune modification n'est enregistrée, pouvez-vous s'il vous plaît aider, merciDans EntityFramework Core 2.x, il n'est pas nécessaire de
Attach
:J'ai essayé cela dans SQL Server et en le profilant:
Find garantit que les entités déjà chargées ne déclenchent pas de SELECT et attache également automatiquement l'entité si nécessaire (à partir de la documentation):
la source
En combinant plusieurs suggestions, je propose les suivantes:
appelé par
Ou par
Ou par
la source
J'utilise
ValueInjecter
nuget pour injecter le modèle de liaison dans l'entité de base de données en utilisant ce qui suit:Notez l'utilisation d'une convention personnalisée qui ne met pas à jour les propriétés si elles sont nulles à partir du serveur.
ValueInjecter v3 +
Usage:
Value Injecter V2
Recherchez cette réponse
Caveat
Vous ne saurez pas si la propriété est intentionnellement effacée à null OU si elle n'a simplement aucune valeur. En d'autres termes, la valeur de propriété ne peut être remplacée que par une autre valeur mais pas effacée.
la source
Je cherchais la même chose et j'ai finalement trouvé la solution
croyez-moi, cela fonctionne pour moi comme un charme.
la source
C'est ce que j'utilise, en utilisant InjectNonNull personnalisé (obj dest, obj src), cela le rend totalement flexible
la source
la source
la source