Antworten:
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();
}
}
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:
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();
}
}
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:
`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.
Password
Spalte für die angegebene UserId
und 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ührenGibt in Entity Framework Core Attach
den Eintrag zurück. Sie benötigen also nur Folgendes:
var user = new User { Id = userId, Password = password };
db.Users.Attach(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();
Ich benutze dies:
Entität:
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 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();
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. "
...Update(Model, d=>d.Name);
//or
...Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
(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 bei Guid
und anderen Objekteigenschaften erforderlich . Diese wurden in ein Convert () 'eingewickelt' und daher nicht von verarbeitet 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();
}
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:
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));
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 kann Update<T>
, groß
var entity=_context.Set<T>().Attach(item);
gefolgt von entity.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
public void ChangePassword(int userId, string password){
var user = dbContext.Users.FirstOrDefault(u=>u.UserId == userId);
user.password = password;
dbContext.SaveChanges();
}
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 ...
Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
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.
entityEntry.State = EntityState.Unchanged;
Alle aktualisierten Werte im Parameter entityEntry
werden zurückgesetzt, sodass keine Änderungen gespeichert werden.
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 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.
Ich kombiniere mehrere Vorschläge und schlage Folgendes vor:
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;
}
}
angerufen von
UpdateDbEntryAsync(dbc, d => d.Property1);//, d => d.Property2, d => d.Property3, etc. etc.);
Oder von
await UpdateDbEntryAsync(dbc, d => d.Property1);
Oder von
bool b = UpdateDbEntryAsync(dbc, d => d.Property1).Result;
Ich verwende ValueInjecter
Nuget, 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.
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);
}
}
Verwendung:
target.InjectFrom<NoNullsInjection>(source);
Schlagen Sie diese Antwort nach
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.
Ich suchte das gleiche und fand schließlich die Lösung
using (CString conn = new CString())
{
USER user = conn.USERs.Find(CMN.CurrentUser.ID);
user.PASSWORD = txtPass.Text;
conn.SaveChanges();
}
Glauben Sie mir, es funktioniert für mich wie ein Zauber.
Dies ist, was ich benutze, mit benutzerdefinierten InjectNonNull (obj dest, obj src) macht es es voll flexibel
[HttpPost]
public async Task<IActionResult> Post( [FromQuery]Models.Currency currency ) {
if ( ModelState.IsValid ) {
// find existing object by Key
Models.Currency currencyDest = context.Currencies.Find( currency.Id );
context.Currencies.Attach( currencyDest );
// update only not null fields
InjectNonNull( currencyDest, currency );
// save
await context.SaveChangesAsync( );
}
return Ok();
}
// Custom method
public static T InjectNonNull<T>( T dest, T src ) {
foreach ( var propertyPair in PropertyLister<T, T>.PropertyMap ) {
var fromValue = propertyPair.Item2.GetValue( src, null );
if ( fromValue != null && propertyPair.Item1.CanWrite ) {
propertyPair.Item1.SetValue( dest, fromValue, null );
}
}
return dest;
}
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();
}
}
Password
meinst du gehashtes Passwort, oder? :-)