Mit Passwordmeinst du gehashtes Passwort, oder? :-)
Edward Brey
Antworten:
368
Ladislavs Antwort wurde aktualisiert, um DbContext zu verwenden (eingeführt in 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();}}
Ich konnte diesen Code nur durch Hinzufügen von db.Configuration.ValidateOnSaveEnabled = false zum Laufen bringen. vor db.SaveChanges ()?
Jake Drew
3
Welcher Namespace soll verwendet werden db.Entry(user).Property(x => x.Password).IsModified = true;und nichtdb.Entry(user).Property("Password").IsModified = true;
Johan
5
Dieser Ansatz löst eine OptimisticConcurencyException aus, wenn die Tabelle ein Zeitstempelfeld enthält.
Maksim Vi.
9
Ich denke, es ist erwähnenswert, dass Sie, wenn Sie verwenden db.Configuration.ValidateOnSaveEnabled = false;, das Feld, das Sie aktualisieren, möglicherweise weiter validieren möchten:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Ziul
2
Sie müssen ValidateOnSaveEnabled auf false setzen, wenn Sie Felder in Ihrer Tabelle benötigt haben, die Sie während Ihres Updates nicht bereitstellen
Sal
54
Sie können EF auf folgende Weise mitteilen, welche Eigenschaften aktualisiert werden müssen:
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();}}
Gehen Sie den EF-Weg den ganzen Weg, in diesem Fall würden Sie
Laden Sie das Objekt basierend auf dem userIdbereitgestellten - das gesamte Objekt wird geladen
Aktualisieren Sie das passwordFeld
speichert das Objekt wieder im Kontext des mit .SaveChanges()Methode
In diesem Fall liegt es an EF, wie dies im Detail behandelt wird. Ich habe dies gerade getestet und für den Fall, dass ich nur ein einzelnes Feld eines Objekts ändere, ist das, was EF erstellt, so ziemlich das, was Sie auch manuell erstellen würden - so etwas wie:
`UPDATE dbo.Users SET Password =@Password WHERE UserId =@UserId`
EF ist also klug genug, um herauszufinden, welche Spalten sich tatsächlich geändert haben, und es wird eine T-SQL-Anweisung erstellt, um nur die Aktualisierungen zu verarbeiten, die tatsächlich erforderlich sind.
Sie definieren eine gespeicherte Prozedur, die genau das tut, was Sie benötigen, im T-SQL-Code (aktualisieren Sie einfach die PasswordSpalte für die angegebene UserIdund nichts anderes - wird im Grunde ausgeführt UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId) und erstellen einen Funktionsimport für diese gespeicherte Prozedur in Ihrem EF-Modell und rufen dies auf funktionieren, anstatt die oben beschriebenen Schritte auszuführen
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;}}
Zugangscode:
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();
Ich erhalte Entitätsüberprüfungsfehler, wenn ich dies versuche, aber es sieht auf jeden Fall cool aus.
Devlord
Funktioniert diese Methode nicht !!!: Vielleicht müssen Sie weitere Details zur Verwendung angeben !!! - Dies ist der Fehler: "Das Anhängen einer Entität vom Typ 'Domain.Job' ist fehlgeschlagen, da eine andere Entität desselben Typs bereits denselben Primärschlüsselwert hat. Dies kann passieren, wenn Sie die Methode 'Anhängen' verwenden oder den Status einer Entität festlegen auf 'Unverändert' oder 'Geändert', wenn Entitäten im Diagramm widersprüchliche Schlüsselwerte aufweisen. Dies kann daran liegen, dass einige Entitäten neu sind und noch keine von der Datenbank generierten Schlüsselwerte erhalten haben. "
Lucian Bumb
Perfec! Überprüfen Sie meine Antwort, um einen flexiblen Ansatz für jedes Modell zu sehen!
Kryp
10
Auf der Suche nach einer Lösung für dieses Problem habe ich im Blog von Patrick Desjardins eine Variation von GONeales Antwort gefunden :
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();}
" Wie Sie sehen können, wird als zweiter Parameter ein Ausdruck einer Funktion verwendet. Dadurch können Sie diese Methode verwenden, indem Sie in einem Lambda-Ausdruck angeben, welche Eigenschaft aktualisiert werden soll. "
Die Methode, die ich derzeit in meinem eigenen Code verwende , wurde erweitert, um auch (Linq) Typausdrücke zu verarbeiten ExpressionType.Convert. Dies war in meinem Fall beispielsweise bei Guidund anderen Objekteigenschaften erforderlich . Diese wurden in ein Convert () 'eingewickelt' und daher nicht von verarbeitet System.Web.Mvc.ExpressionHelper.GetExpressionText.
Wenn ich dies verwende, wird folgende Fehlermeldung angezeigt: Der Lambda-Ausdruck kann nicht in den Typ 'Ausdruck <Func <RequestDetail, Objekt >> []' konvertiert werden, da es sich nicht um einen Delegatentyp handelt
Imran Rizvi,
@ImranRizvi, Sie müssen lediglich die Parameter aktualisieren auf: public int Update (T-Entität, Parameter Ausdruck <Func <T, Objekt >> [] Eigenschaften) HINWEIS das Schlüsselwort params vor Ausdruck
dalcam
6
Ich bin zu spät zum Spiel hier, aber so mache ich es. Ich habe eine Weile nach einer Lösung gesucht, mit der ich zufrieden war. Dies erzeugt UPDATENUR eine Anweisung für die Felder, die geändert werden, da Sie explizit definieren, was sie sind, und zwar durch ein "White List" -Konzept, das sicherer ist, um das Einfügen von Webformularen ohnehin zu verhindern.
Ein Auszug aus meinem ISession-Datenrepository:
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;}
Dies könnte in einem Versuch verpackt werden. Wenn Sie dies wünschen, möchte ich persönlich, dass mein Anrufer über die Ausnahmen in diesem Szenario informiert wird.
Es würde in etwa so aufgerufen (für mich über eine ASP.NET-Web-API):
if(!session.Update(franchiseViewModel.Franchise, new[]{"Name","StartDate"}))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
Ihre bessere Lösung ist also, was Elisa? Sie sollten explizit angeben, welche Eigenschaften aktualisiert werden dürfen (genau wie die für den UpdateModelBefehl von ASP.NET MVC erforderliche Whitelist ). Auf diese Weise stellen Sie sicher, dass keine Hacker-Formularinjektion durchgeführt werden kann und keine Felder aktualisiert werden können, die nicht aktualisiert werden dürfen. Wenn aber jemand die String - Array zu irgendeiner Art von Lambda - Ausdrücke Parameter und die Arbeit mit ihm in der umwandeln kann Update<T>, groß
@Elisa Es kann verbessert werden, indem Func <T, List <Objekt>> anstelle von Zeichenfolge [] verwendet wird
SpongeBob-Genosse
Auch später im Spiel, und vielleicht ist dies eine viel neuere Syntax, aber var entity=_context.Set<T>().Attach(item);gefolgt von entity.Property(propertyName).IsModified = true;in der Schleife sollte funktionieren.
Auspex
4
Das Entity Framework verfolgt Ihre Änderungen an Objekten, die Sie über DbContext aus der Datenbank abgefragt haben. Zum Beispiel, wenn Ihr DbContext-Instanzname dbContext ist
public void ChangePassword(int userId, string password){
var user= dbContext.Users.FirstOrDefault(u=>u.UserId == userId);user.password = password;
dbContext.SaveChanges();}
Dies ist falsch, da dadurch das gesamte Benutzerobjekt mit geändertem Kennwort gespeichert wird.
eigenartig
Das ist wahr, aber der Rest des Benutzerobjekts ist derselbe wie zuvor im Kontext. Das einzige, was möglicherweise anders sein wird, ist das Kennwort, sodass es im Wesentlichen nur das Kennwort aktualisiert.
Tomislav3008
3
Ich weiß, dass dies ein alter Thread ist, aber ich suchte auch nach einer ähnlichen Lösung und entschied mich für die Lösung @ Doku-so. Ich kommentiere, um die von @Imran Rizvi gestellte Frage zu beantworten. Ich folgte dem Link @ Doku-so, der eine ähnliche Implementierung zeigt. @ Imran Rizvis Frage war, dass er mit der bereitgestellten Lösung 'Lambda-Ausdruck kann nicht in Typ' Ausdruck> [] 'konvertiert werden, da es sich nicht um einen Delegatentyp handelt' einen Fehler erhalten hat. Ich wollte eine kleine Änderung anbieten, die ich an der Lösung von @ Doku-so vorgenommen habe, um diesen Fehler zu beheben, falls jemand anderes auf diesen Beitrag stößt und sich für die Verwendung der Lösung von @ Doku-so entscheidet.
Das Problem ist das zweite Argument in der Update-Methode.
public int Update(T entity, Expression<Func<T, object>>[] properties).
So rufen Sie diese Methode mit der angegebenen Syntax auf ...
Sie müssen das Schlüsselwort 'params' als solches vor dem zweiten Arugment einfügen.
public int Update(T entity, params Expression<Func<T, object>>[] properties)
Wenn Sie die Methodensignatur nicht ändern möchten, müssen Sie zum Aufrufen der Update-Methode das Schlüsselwort ' new ' hinzufügen , die Größe des Arrays angeben und schließlich die Syntax für die Initialisierung des Sammlungsobjekts für jede Eigenschaft verwenden, die wie angezeigt aktualisiert werden soll unten.
Update(Model, new Expression<Func<T, object>>[3]{ d=>d.Name },{ d=>d.SecondProperty },{ d=>d.AndSoOn });
Im Beispiel von @ Doku-so gibt er ein Array von Ausdrücken an, sodass Sie die zu aktualisierenden Eigenschaften in einem Array übergeben müssen. Aufgrund des Arrays müssen Sie auch die Größe des Arrays angeben. Um dies zu vermeiden, können Sie auch das Ausdrucksargument so ändern, dass IEnumerable anstelle eines Arrays verwendet wird.
Hier ist meine Implementierung der @ Doku-so-Lösung.
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();}
Verwendung:
this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);
@ Doku-so bot einen coolen Ansatz mit Generika. Ich habe das Konzept verwendet, um mein Problem zu lösen, aber Sie können die Lösung von @ Doku-so nicht so verwenden, wie sie ist, und sowohl in diesem Beitrag als auch im verlinkten Beitrag hat niemand die Fragen zu Verwendungsfehlern beantwortet.
Ich habe an Ihrer Lösung gearbeitet, als das Programm die Zeile übergeben hat. entityEntry.State = EntityState.Unchanged;Alle aktualisierten Werte im Parameter entityEntrywerden zurückgesetzt, sodass keine Änderungen gespeichert werden.
Können
2
In EntityFramework Core 2.x ist Folgendes nicht erforderlich Attach:
// get a tracked entity
var entity = context.User.Find(userId);
entity.someProp = someValue;// other property changes might come here
context.SaveChanges();
Versuchte dies in SQL Server und Profilierung:
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
Suchen stellt sicher, dass bereits geladene Entitäten kein SELECT auslösen, und hängt die Entität bei Bedarf automatisch an (aus den Dokumenten):
/// 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.
Wie diese Methode für andere Klassen verfügbar gemacht werden kann, kann wie eine Erweiterungsmethode sein?
Velkumar
In diesem .NET CORE-Lernprogramm werden bewährte Methoden zur Verwendung (des neuen) EF Core zum Aktualisieren bestimmter Eigenschaften in MVC gezeigt. Suchen Sie nach 'TryUpdateModelAsync'.
Guy
1
@ Guy Awesome. Noch einmal, Microsofts "Best Practice" ist es, etwas anderes zu tun als das, was ihre Tools bauen ...
Auspex
Dies ist eine gute Lösung.
Timothy Macharia
1
Ich verwende ValueInjecterNuget, um das Bindungsmodell wie folgt in die Datenbankentität einzufügen:
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();}
Beachten Sie die Verwendung einer benutzerdefinierten Konvention, die Eigenschaften nicht aktualisiert, wenn sie vom Server null sind.
Sie werden nicht wissen, ob die Eigenschaft absichtlich auf null gelöscht wurde oder ob sie einfach keinen Wert hatte. Mit anderen Worten, der Eigenschaftswert kann nur durch einen anderen Wert ersetzt, aber nicht gelöscht werden.
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
meinst du gehashtes Passwort, oder? :-)Antworten:
Ladislavs Antwort wurde aktualisiert, um DbContext zu verwenden (eingeführt in EF 4.1):
quelle
db.Entry(user).Property(x => x.Password).IsModified = true;
und nichtdb.Entry(user).Property("Password").IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
, das Feld, das Sie aktualisieren, möglicherweise weiter validieren möchten:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Sie können EF auf folgende Weise mitteilen, welche Eigenschaften aktualisiert werden müssen:
quelle
Sie haben grundsätzlich zwei Möglichkeiten:
userId
bereitgestellten - das gesamte Objekt wird geladenpassword
Feld.SaveChanges()
MethodeIn diesem Fall liegt es an EF, wie dies im Detail behandelt wird. Ich habe dies gerade getestet und für den Fall, dass ich nur ein einzelnes Feld eines Objekts ändere, ist das, was EF erstellt, so ziemlich das, was Sie auch manuell erstellen würden - so etwas wie:
EF ist also klug genug, um herauszufinden, welche Spalten sich tatsächlich geändert haben, und es wird eine T-SQL-Anweisung erstellt, um nur die Aktualisierungen zu verarbeiten, die tatsächlich erforderlich sind.
Password
Spalte für die angegebeneUserId
und nichts anderes - wird im Grunde ausgeführtUPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId
) und erstellen einen Funktionsimport für diese gespeicherte Prozedur in Ihrem EF-Modell und rufen dies auf funktionieren, anstatt die oben beschriebenen Schritte auszuführenquelle
Gibt in Entity Framework Core
Attach
den Eintrag zurück. Sie benötigen also nur Folgendes:quelle
Ich benutze dies:
Entität:
dbcontext:
Zugangscode:
quelle
Auf der Suche nach einer Lösung für dieses Problem habe ich im Blog von Patrick Desjardins eine Variation von GONeales Antwort gefunden :
(Eine etwas ähnliche Lösung finden Sie auch hier: https://stackoverflow.com/a/5749469/2115384 )
Die Methode, die ich derzeit in meinem eigenen Code verwende , wurde erweitert, um auch (Linq) Typausdrücke zu verarbeiten
ExpressionType.Convert
. Dies war in meinem Fall beispielsweise beiGuid
und anderen Objekteigenschaften erforderlich . Diese wurden in ein Convert () 'eingewickelt' und daher nicht von verarbeitetSystem.Web.Mvc.ExpressionHelper.GetExpressionText
.quelle
Ich bin zu spät zum Spiel hier, aber so mache ich es. Ich habe eine Weile nach einer Lösung gesucht, mit der ich zufrieden war. Dies erzeugt
UPDATE
NUR eine Anweisung für die Felder, die geändert werden, da Sie explizit definieren, was sie sind, und zwar durch ein "White List" -Konzept, das sicherer ist, um das Einfügen von Webformularen ohnehin zu verhindern.Ein Auszug aus meinem ISession-Datenrepository:
Dies könnte in einem Versuch verpackt werden. Wenn Sie dies wünschen, möchte ich persönlich, dass mein Anrufer über die Ausnahmen in diesem Szenario informiert wird.
Es würde in etwa so aufgerufen (für mich über eine ASP.NET-Web-API):
quelle
UpdateModel
Befehl von ASP.NET MVC erforderliche Whitelist ). Auf diese Weise stellen Sie sicher, dass keine Hacker-Formularinjektion durchgeführt werden kann und keine Felder aktualisiert werden können, die nicht aktualisiert werden dürfen. Wenn aber jemand die String - Array zu irgendeiner Art von Lambda - Ausdrücke Parameter und die Arbeit mit ihm in der umwandeln kannUpdate<T>
, großvar entity=_context.Set<T>().Attach(item);
gefolgt vonentity.Property(propertyName).IsModified = true;
in der Schleife sollte funktionieren.Das Entity Framework verfolgt Ihre Änderungen an Objekten, die Sie über DbContext aus der Datenbank abgefragt haben. Zum Beispiel, wenn Ihr DbContext-Instanzname dbContext ist
quelle
Ich weiß, dass dies ein alter Thread ist, aber ich suchte auch nach einer ähnlichen Lösung und entschied mich für die Lösung @ Doku-so. Ich kommentiere, um die von @Imran Rizvi gestellte Frage zu beantworten. Ich folgte dem Link @ Doku-so, der eine ähnliche Implementierung zeigt. @ Imran Rizvis Frage war, dass er mit der bereitgestellten Lösung 'Lambda-Ausdruck kann nicht in Typ' Ausdruck> [] 'konvertiert werden, da es sich nicht um einen Delegatentyp handelt' einen Fehler erhalten hat. Ich wollte eine kleine Änderung anbieten, die ich an der Lösung von @ Doku-so vorgenommen habe, um diesen Fehler zu beheben, falls jemand anderes auf diesen Beitrag stößt und sich für die Verwendung der Lösung von @ Doku-so entscheidet.
Das Problem ist das zweite Argument in der Update-Methode.
So rufen Sie diese Methode mit der angegebenen Syntax auf ...
Sie müssen das Schlüsselwort 'params' als solches vor dem zweiten Arugment einfügen.
Wenn Sie die Methodensignatur nicht ändern möchten, müssen Sie zum Aufrufen der Update-Methode das Schlüsselwort ' new ' hinzufügen , die Größe des Arrays angeben und schließlich die Syntax für die Initialisierung des Sammlungsobjekts für jede Eigenschaft verwenden, die wie angezeigt aktualisiert werden soll unten.
Im Beispiel von @ Doku-so gibt er ein Array von Ausdrücken an, sodass Sie die zu aktualisierenden Eigenschaften in einem Array übergeben müssen. Aufgrund des Arrays müssen Sie auch die Größe des Arrays angeben. Um dies zu vermeiden, können Sie auch das Ausdrucksargument so ändern, dass IEnumerable anstelle eines Arrays verwendet wird.
Hier ist meine Implementierung der @ Doku-so-Lösung.
Verwendung:
@ Doku-so bot einen coolen Ansatz mit Generika. Ich habe das Konzept verwendet, um mein Problem zu lösen, aber Sie können die Lösung von @ Doku-so nicht so verwenden, wie sie ist, und sowohl in diesem Beitrag als auch im verlinkten Beitrag hat niemand die Fragen zu Verwendungsfehlern beantwortet.
quelle
entityEntry.State = EntityState.Unchanged;
Alle aktualisierten Werte im ParameterentityEntry
werden zurückgesetzt, sodass keine Änderungen gespeichert werden.In EntityFramework Core 2.x ist Folgendes nicht erforderlich
Attach
:Versuchte dies in SQL Server und Profilierung:
Suchen stellt sicher, dass bereits geladene Entitäten kein SELECT auslösen, und hängt die Entität bei Bedarf automatisch an (aus den Dokumenten):
quelle
Ich kombiniere mehrere Vorschläge und schlage Folgendes vor:
angerufen von
Oder von
Oder von
quelle
Ich verwende
ValueInjecter
Nuget, um das Bindungsmodell wie folgt in die Datenbankentität einzufügen:Beachten Sie die Verwendung einer benutzerdefinierten Konvention, die Eigenschaften nicht aktualisiert, wenn sie vom Server null sind.
ValueInjecter v3 +
Verwendung:
Wert Injektor V2
Schlagen Sie diese Antwort nach
Vorbehalt
Sie werden nicht wissen, ob die Eigenschaft absichtlich auf null gelöscht wurde oder ob sie einfach keinen Wert hatte. Mit anderen Worten, der Eigenschaftswert kann nur durch einen anderen Wert ersetzt, aber nicht gelöscht werden.
quelle
Ich suchte das gleiche und fand schließlich die Lösung
Glauben Sie mir, es funktioniert für mich wie ein Zauber.
quelle
Dies ist, was ich benutze, mit benutzerdefinierten InjectNonNull (obj dest, obj src) macht es es voll flexibel
quelle
quelle
quelle