Wie nur ein Feld mit Entity Framework aktualisieren?
-
30-09-2019 - |
Frage
Hier ist die Tabelle
Nutzer
UserId
UserName
Password
EmailAddress
und der Code ..
public void ChangePassword(int userId, string password){
//code to update the password..
}
Lösung
Ladislav Antwort aktualisiert DbContext 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();
}
}
Andere Tipps
Sie können EF sagen, welche Eigenschaften haben auf diese Weise aktualisiert werden:
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:
- die EF Art und Weise den ganzen Weg gehen, in diesem Fall würden Sie
- laden Sie das Objekt auf der Grundlage der
userId
vorgesehen - das gesamte Objekt geladen wird, - Aktualisieren der
password
Feld - save das Objekt wieder im Kontext des
.SaveChanges()
Methode mit
- laden Sie das Objekt auf der Grundlage der
In diesem Fall ist es zu EF auf, wie dies im Detail zu behandeln. Ich dies nur getestet, und in dem Fall, dass ich nur ein einzelnes Feld eines Objekts ändern, was EF schafft, ist ziemlich viel, was Sie manuell erstellen würden, auch - so etwas wie:
`UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId`
So EF ist intelligent genug, um herauszufinden, was Spalt tatsächlich geändert hat, und es wird eine T-SQL-Anweisung zu behandeln nur das Updates erstellen, die in der Tat notwendig.
- definieren Sie eine gespeicherte Prozedur, die genau das tut, was Sie brauchen, in T-SQL-Code (Update nur die
Password
Spalte für die gegebeneUserId
und sonst nichts - im Grunde führtUPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId
) und Sie erstellen eine Funktion Import für die gespeicherte Prozedur in Ihrer EF-Modell und Sie diese Funktion aufrufen, anstatt die Schritte tun oben skizzierte
ich verwende diese:
Einheit:
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; }
}
Zugriffscode:
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();
Während dieses Problem nach einer Lösung suchen, fand ich eine Variation auf GONeale Antwort durch Patrick Desjardins' Blog :
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, dauert es als zweiten Parameter ein Ausdruck einer Funktion. Auf diese Weise kann diese Methode verwenden, indem sie in einem Lambda Angabe Ausdruck, die Eigenschaft zu aktualisieren. "
...Update(Model, d=>d.Name);
//or
...Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
(Eine ähnliche Lösung auch hier gegeben: https://stackoverflow.com/a/5749469/2115384 )
Die Methode, die ich zur Zeit in meinem eigenen Code am , erstreckte sich auch auf handhaben (Linq) Ausdrücke vom Typ ExpressionType.Convert
. Dies war in meinem Fall notwendig, zum Beispiel mit Guid
und anderen Objekteigenschaften. Das war 'eingewickelt' in der Convert () und daher nicht von System.Web.Mvc.ExpressionHelper.GetExpressionText
behandelt.
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();
}
Im Entity Framework Core Attach
gibt den Eintrag, so alles, was Sie brauchen, ist:
var user = new User { Id = userId, Password = password };
db.Users.Attach(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();
Ich bin zu spät, um das Spiel hier, aber das ist, wie ich es tue, verbrachte ich während der Jagd für eine Lösung, die ich mit satisified wurde; Dies erzeugt eine UPDATE
Aussage nur für die Felder, die geändert werden, wie Sie explizit definieren, was sie durch eine „weiße Liste“ -Konzept sind, die sicherer ist sowieso Injektions Webformular zu verhindern.
Ein Auszug aus meinem ISession Daten-Repository:
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 try..catch eingewickelt werden, wenn Sie dies wünschen, aber ich persönlich mag meine Anrufer über die Ausnahmen in diesem Szenario kennen.
Es wäre in so etwas wie diese Art und Weise genannt werden (für mich war dies über eine ASP.NET Web API):
if (!session.Update(franchiseViewModel.Franchise, new[]
{
"Name",
"StartDate"
}))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
Ich weiß, das ist ein alter Thread, aber ich war auch auf der Suche nach einer ähnlichen Lösung und entschied sich mit der Lösung zu gehen @ Doku-so zur Verfügung gestellt. Ich kommentiere die Frage nach @Imran Rizvi nicht beantwortet zu werden, folgte ich @ Doku-so Link, dass eine ähnliche Umsetzung. @Imran Rizvi Frage war, dass er einen Fehler bekommen die bereitgestellte Lösung unter Verwendung des Ausdrucks ‚Kann Lambda Ausdruck Typ nicht konvertieren‚> []‘, weil es nicht eine Art delegieren ist‘. Ich wollte eine kleine Änderung biete ich gemacht @ Doku-so-Lösung, dass behebt diesen Fehler, falls jemand über dieses Thema kommt und entscheidet @ Doku-so-Lösung zu verwenden.
Das Problem ist, das zweite Argument in der Update-Methode,
public int Update(T entity, Expression<Func<T, object>>[] properties).
Um diese Methode rufen Sie die Syntax zur Verfügung gestellt ...
Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
Sie müssen hinzufügen ‚params‘ Schlüsselwort vor dem zweiten arugment als so.
public int Update(T entity, params Expression<Func<T, object>>[] properties)
oder, wenn Sie nicht die Methode Signatur ändern möchten dann die Update-Methode aufrufen müssen Sie hinzufügen ‚ neue ‘ Schlüsselwort, geben Sie die Größe der Array, dann verwenden Sie schließlich die Sammlung Objektinitialisierer Syntax für jede Eigenschaft zu aktualisieren, wie unten zu sehen.
Update(Model, new Expression<Func<T, object>>[3] { d=>d.Name }, { d=>d.SecondProperty }, { d=>d.AndSoOn });
@ Doku-so das Beispiel er eine Reihe von Ausdrücken spezifiziert, so dass Sie die Eigenschaften Aktualisierung in einem Array übergeben werden muss, weil der Array Sie auch die Größe des Arrays angeben müssen. Um dies zu vermeiden Sie könnten auch den Ausdruck Argument ändern IEnumerable verwenden anstelle eines Arrays.
Hier ist meine Implementierung von @ 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();
}
Verbrauch:
this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);
@ Doku-so einen kühlen Ansatz generic die vorgesehen ist, habe ich das Konzept mein Problem zu lösen, aber man kann einfach nicht verwenden @ Doku-so-Lösung wie und sowohl in diesem Beitrag und der verlinkten Beitrag nicht beantworten man die Nutzung Fehler Fragen.
Entity Framework verfolgt Ihre Änderungen auf Objekte, die Sie aus der Datenbank über DbContext abgefragt. Wenn Sie zum Beispiel DbContext Instanzname ist DbContext
public void ChangePassword(int userId, string password){
var user = dbContext.Users.FirstOrDefault(u=>u.UserId == userId);
user.password = password;
dbContext.SaveChanges();
}
In EntityFramework Core-2.x gibt es keine Notwendigkeit für Attach
ist:
// get a tracked entity
var entity = context.User.Find(userId);
entity.someProp = someValue;
// other property changes might come here
context.SaveChanges();
versucht, diese in SQL Server und Profilierungs es:
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
stellt sicher finden, die bereits geladen Entitäten auslösen keine SELECT und auch misst automatisch die Einheit bei Bedarf (aus der Dokumentation):
/// 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 benutze ValueInjecter
nuget einzuspritzen Bindungsmodell in der Datenbank Entity mit folgenden:
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 von benutzerdefinierter Konvention, die nicht Eigenschaften nicht aktualisiert Wenn sie null vom Server.
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);
}
}
Verbrauch:
target.InjectFrom<NoNullsInjection>(source);
Wert Injecter V2
Caveat
Sie werden nicht wissen, ob die Eigenschaft absichtlich auf null gelöscht oder es hat einfach keinen Wert es. Mit anderen Worten, kann der Wert die Eigenschaft nur mit einem anderen Wert, aber nicht gelöscht ersetzt werden.
Ich war auf der Suche für gleiche und schließlich fand ich 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 für mich wie ein Zauber funktionieren.
Die Kombination mehrere Vorschläge Ich schlage die folgende:
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;
}
}
genannt durch
UpdateDbEntryAsync(dbc, d => d.Property1);//, d => d.Property2, d => d.Property3, etc. etc.);
oder durch
await UpdateDbEntryAsync(dbc, d => d.Property1);
oder durch
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();
}
}