Przez Password, to znaczy hashed hasło, prawda? :-)
Edward Brey
Odpowiedzi:
370
Zaktualizowano odpowiedź Ladislava, by używała DbContext (wprowadzona w 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();}}
Udało mi się sprawić, aby ten kod działał tylko przez dodanie db.Configuration.ValidateOnSaveEnabled = false; przed db.SaveChanges ()?
Jake Drew
3
Której przestrzeni nazw należy użyć, db.Entry(user).Property(x => x.Password).IsModified = true;a której niedb.Entry(user).Property("Password").IsModified = true;
Johan
5
Takie podejście zgłasza wyjątek OptimisticConcurencyException, gdy tabela ma pole znacznika czasu.
Maksim Vi.
9
Myślę, że warto wspomnieć, że jeśli używasz, db.Configuration.ValidateOnSaveEnabled = false;możesz nadal sprawdzać poprawność aktualizowanego pola:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Ziul
2
Musisz ustawić ValidateOnSaveEnabled na false, jeśli masz wymagane pola w tabeli, których nie podajesz podczas aktualizacji
Sal
54
Możesz powiedzieć EF, które właściwości należy zaktualizować w ten sposób:
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 nie jest dostępny dla DBContext
LoxLox
17
Masz w zasadzie dwie opcje:
idź do końca EF, w takim przypadku zrobiłbyś to
załaduj obiekt na podstawie userIddostarczonego - cały obiekt zostanie załadowany
zaktualizuj passwordpole
zapisz obiekt ponownie stosując kontekście za .SaveChanges()metodę
W takim przypadku to EF zależy od tego, jak sobie z tym poradzić. Właśnie to przetestowałem i w przypadku, gdy zmieniam tylko jedno pole obiektu, to, co tworzy EF, jest prawie tym, co stworzyłbyś ręcznie - coś w stylu:
`UPDATE dbo.Users SET Password =@Password WHERE UserId =@UserId`
EF jest więc wystarczająco inteligentny, aby dowiedzieć się, które kolumny rzeczywiście się zmieniły, i utworzy instrukcję T-SQL do obsługi tylko tych aktualizacji, które w rzeczywistości są konieczne.
definiujesz procedurę przechowywaną, która robi dokładnie to, czego potrzebujesz, w kodzie T-SQL (wystarczy zaktualizować Passwordkolumnę dla podanego UserIdi nic więcej - w zasadzie wykonuje się UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId) i tworzysz import funkcji dla tej procedury przechowywanej w modelu EF i wywołujesz to funkcja zamiast wykonywać czynności opisane powyżej
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;}}
kod akcesorium:
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();
Gdy próbuję, otrzymuję błędy sprawdzania poprawności jednostki, ale na pewno wygląda to świetnie.
devlord,
Nie działa ta metoda !!!: może musisz podać więcej szczegółów, jak z niej korzystać !!! - to jest błąd: „Dołączanie encji typu„ Domain.Job ”nie powiodło się, ponieważ inna encja tego samego typu ma już tę samą wartość klucza podstawowego. Może się to zdarzyć, gdy użyjesz metody„ Attach ”lub ustawisz stan encji na „Niezmieniony” lub „Zmodyfikowany”, jeśli dowolne podmioty na wykresie mają sprzeczne wartości kluczy. Może to być spowodowane tym, że niektóre podmioty są nowe i nie otrzymały jeszcze wartości kluczy wygenerowanych przez bazę danych. ”
Lucian Bumb
Perfec! Sprawdź moją odpowiedź, aby zobaczyć elastyczne podejście do każdego modelu!
kryp
10
Szukając rozwiązania tego problemu, znalazłem odmianę odpowiedzi GONeale na blogu Patricka Desjardinsa :
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();}
„ Jak widać, jako drugi parametr przyjmuje wyrażenie funkcji. Pozwoli to na użycie tej metody przez określenie w wyrażeniu Lambda, którą właściwość należy zaktualizować. ”
Metoda, której obecnie używam we własnym kodzie , została rozszerzona o obsługę również wyrażeń typu (Linq) ExpressionType.Convert. Było to konieczne w moim przypadku, na przykład w przypadku Guidi innych właściwości obiektu. Zostały one „opakowane” w Convert () i dlatego nie były obsługiwane przez System.Web.Mvc.ExpressionHelper.GetExpressionText.
Gdy go używam, pojawia się następujący błąd: Nie można przekonwertować wyrażenia Lambda na typ „Wyrażenie <Func <RequestDetail, obiekt >> []”, ponieważ nie jest to typ delegata
Imran Rizvi
@ImranRizvi, wystarczy zaktualizować parametry do: public int Aktualizacja (jednostka T, parametry Wyrażenie <Func <T, obiekt >> [] właściwości) UWAGA słowo kluczowe params przed wyrażeniem
dalcam
6
Spóźniłem się tutaj na grę, ale tak to robię, spędziłem trochę czasu na poszukiwaniu rozwiązania, z którego byłem usatysfakcjonowany; tworzy to UPDATEinstrukcję TYLKO dla pól, które zostały zmienione, ponieważ wyraźnie definiujesz, co to jest za pomocą koncepcji „białej listy”, która jest bezpieczniejsza, aby zapobiec wstrzyknięciu formularza internetowego.
Fragment z mojego repozytorium danych 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;}
Można to zawrzeć w try..catch, jeśli chcesz, ale osobiście lubię mojego rozmówcę, aby wiedział o wyjątkach w tym scenariuszu.
Zostałby wywołany w taki sposób (dla mnie było to przez interfejs API sieci Web ASP.NET):
if(!session.Update(franchiseViewModel.Franchise, new[]{"Name","StartDate"}))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
Więc twoim lepszym rozwiązaniem jest to, co Elisa? Powinieneś wyraźnie określić, jakie właściwości zezwalasz na aktualizację (tak jak biała lista wymagana dla polecenia ASP.NET MVC UpdateModel), w ten sposób upewnisz się, że nie można wprowadzić wstrzyknięcia formularza hakera i nie mogą oni aktualizować pól, których nie mogą aktualizować. Jeśli jednak ktoś może przekonwertować tablicę ciągów znaków na jakiś parametr wyrażeń lambda i pracować z nim wUpdate<T>
@Elisa Można to ulepszyć, używając Func <T, Lista <obiekt>> zamiast ciągu []
Spongebob Comrade
Nawet później do gry, i być może jest to znacznie nowsza składnia, ale powinna var entity=_context.Set<T>().Attach(item);następować entity.Property(propertyName).IsModified = true;w pętli.
Auspex,
4
Struktura Entity śledzi twoje zmiany w obiektach, które sprawdziłeś w bazie danych za pomocą DbContext. Na przykład jeśli nazwa instancji DbContext to dbContext
public void ChangePassword(int userId, string password){
var user= dbContext.Users.FirstOrDefault(u=>u.UserId == userId);user.password = password;
dbContext.SaveChanges();}
Jest to błędne, ponieważ zapisałoby cały obiekt użytkownika ze zmienionym hasłem.
amuliar
to prawda, ale reszta obiektu użytkownika będzie taka sama, jak poprzednio w kontekście, jedyną rzeczą, która prawdopodobnie będzie inna, jest hasło, a więc zasadniczo tylko aktualizacja hasła.
Tomislav3008
3
Wiem, że to stary wątek, ale szukałem również podobnego rozwiązania i postanowiłem skorzystać z dostarczonego rozwiązania @ Doku. Komentuję, aby odpowiedzieć na pytanie zadane przez @Imran Rizvi, podążyłem za linkiem @ Doku-so, który pokazuje podobną implementację. @Imran Rizvi zadał pytanie, że dostaje błąd przy użyciu dostarczonego rozwiązania „Nie można przekonwertować wyrażenia Lambda na typ„ Wyrażenie> [] ”, ponieważ nie jest to typ delegata”. Chciałem zaoferować niewielką modyfikację, którą wprowadziłem w rozwiązaniu @ Doku-so, która naprawia ten błąd w przypadku, gdy ktoś napotka ten post i zdecyduje się użyć rozwiązania @ Doku-so.
Problem jest drugim argumentem w metodzie aktualizacji,
public int Update(T entity, Expression<Func<T, object>>[] properties).
Aby wywołać tę metodę przy użyciu dostarczonej składni ...
Musisz dodać słowo kluczowe „params” przed drugim argumentem.
public int Update(T entity, params Expression<Func<T, object>>[] properties)
lub jeśli nie chcesz zmieniać podpisu metody, a następnie, aby wywołać metodę Update, musisz dodać słowo kluczowe „ new ”, określić rozmiar tablicy, a następnie w końcu użyć składni inicjalizującej obiekt kolekcji dla każdej właściwości do zaktualizowania, jak widać poniżej.
Update(Model, new Expression<Func<T, object>>[3]{ d=>d.Name },{ d=>d.SecondProperty },{ d=>d.AndSoOn });
W przykładzie @ Doku-so określa tablicę wyrażeń, więc musisz przekazać właściwości, aby zaktualizować tablicę, ponieważ ze względu na tablicę musisz także określić jej rozmiar. Aby tego uniknąć, możesz również zmienić argument wyrażenia, aby używał IEnumerable zamiast tablicy.
Oto moja implementacja rozwiązania @ 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();}
Stosowanie:
this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);
@ Doku-so zapewniło fajne podejście przy użyciu ogólnych, użyłem tej koncepcji, aby rozwiązać mój problem, ale po prostu nie możesz użyć rozwiązania @ Doku-so w takiej postaci, w jakiej się znajduje, i zarówno w tym poście, jak i w łączonym poście nikt nie odpowiedział na pytania o błędzie użytkowania.
Pracowałem nad twoim rozwiązaniem, gdy program przejdzie przez linię entityEntry.State = EntityState.Unchanged;wszystkie zaktualizowane wartości w parametrze entityEntryget cofnij, więc żadne zmiany nie są zapisywane, czy możesz pomóc w tym, dziękuję
sairfan
3
W EntityFramework Core 2.x nie ma potrzeby Attach:
// get a tracked entity
var entity = context.User.Find(userId);
entity.someProp = someValue;// other property changes might come here
context.SaveChanges();
Próbowałem tego w SQL Server i profilowałem:
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
Znajdź zapewnia, że już załadowane encje nie wyzwalają SELECT, a także automatycznie dołącza encję w razie potrzeby (z dokumentacji):
/// 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.
Jak udostępnić tę metodę innym klasom, może to być metoda rozszerzenia?
Velkumar
w tym samouczku .NET CORE pokazano najlepsze praktyki przy użyciu (nowego) EF Core do aktualizacji określonych właściwości w MVC. poszukaj „TryUpdateModelAsync”.
Guy
1
@Guy Awesome. Chociaż „najlepszą praktyką” Microsoftu jest zrobienie czegoś innego niż to, co budują ich narzędzia ...
Auspex,
To dobre rozwiązanie.
Timothy Macharia
1
Używam ValueInjecternugetu do wstrzykiwania modelu wiązania do encji bazy danych przy użyciu:
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();}
Zwróć uwagę na użycie niestandardowej konwencji, która nie aktualizuje właściwości, jeśli są one zerowe z serwera.
Nie będziesz wiedział, czy właściwość została celowo wyczyszczona na wartość zerową LUB po prostu nie miała żadnej wartości. Innymi słowy, wartość właściwości może być zastąpiona inną wartością, ale nie wyczyszczona.
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
, to znaczy hashed hasło, prawda? :-)Odpowiedzi:
Zaktualizowano odpowiedź Ladislava, by używała DbContext (wprowadzona w EF 4.1):
źródło
db.Entry(user).Property(x => x.Password).IsModified = true;
a której niedb.Entry(user).Property("Password").IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
możesz nadal sprawdzać poprawność aktualizowanego pola:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Możesz powiedzieć EF, które właściwości należy zaktualizować w ten sposób:
źródło
Masz w zasadzie dwie opcje:
userId
dostarczonego - cały obiekt zostanie załadowanypassword
pole.SaveChanges()
metodęW takim przypadku to EF zależy od tego, jak sobie z tym poradzić. Właśnie to przetestowałem i w przypadku, gdy zmieniam tylko jedno pole obiektu, to, co tworzy EF, jest prawie tym, co stworzyłbyś ręcznie - coś w stylu:
EF jest więc wystarczająco inteligentny, aby dowiedzieć się, które kolumny rzeczywiście się zmieniły, i utworzy instrukcję T-SQL do obsługi tylko tych aktualizacji, które w rzeczywistości są konieczne.
Password
kolumnę dla podanegoUserId
i nic więcej - w zasadzie wykonuje sięUPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId
) i tworzysz import funkcji dla tej procedury przechowywanej w modelu EF i wywołujesz to funkcja zamiast wykonywać czynności opisane powyżejźródło
W Entity Framework Core
Attach
zwraca wpis, więc wszystko czego potrzebujesz to:źródło
używam tego:
jednostka:
dbcontext:
kod akcesorium:
źródło
Szukając rozwiązania tego problemu, znalazłem odmianę odpowiedzi GONeale na blogu Patricka Desjardinsa :
(Nieco podobne rozwiązanie podano również tutaj: https://stackoverflow.com/a/5749469/2115384 )
Metoda, której obecnie używam we własnym kodzie , została rozszerzona o obsługę również wyrażeń typu (Linq)
ExpressionType.Convert
. Było to konieczne w moim przypadku, na przykład w przypadkuGuid
i innych właściwości obiektu. Zostały one „opakowane” w Convert () i dlatego nie były obsługiwane przezSystem.Web.Mvc.ExpressionHelper.GetExpressionText
.źródło
Spóźniłem się tutaj na grę, ale tak to robię, spędziłem trochę czasu na poszukiwaniu rozwiązania, z którego byłem usatysfakcjonowany; tworzy to
UPDATE
instrukcję TYLKO dla pól, które zostały zmienione, ponieważ wyraźnie definiujesz, co to jest za pomocą koncepcji „białej listy”, która jest bezpieczniejsza, aby zapobiec wstrzyknięciu formularza internetowego.Fragment z mojego repozytorium danych ISession:
Można to zawrzeć w try..catch, jeśli chcesz, ale osobiście lubię mojego rozmówcę, aby wiedział o wyjątkach w tym scenariuszu.
Zostałby wywołany w taki sposób (dla mnie było to przez interfejs API sieci Web ASP.NET):
źródło
UpdateModel
), w ten sposób upewnisz się, że nie można wprowadzić wstrzyknięcia formularza hakera i nie mogą oni aktualizować pól, których nie mogą aktualizować. Jeśli jednak ktoś może przekonwertować tablicę ciągów znaków na jakiś parametr wyrażeń lambda i pracować z nim wUpdate<T>
var entity=_context.Set<T>().Attach(item);
następowaćentity.Property(propertyName).IsModified = true;
w pętli.Struktura Entity śledzi twoje zmiany w obiektach, które sprawdziłeś w bazie danych za pomocą DbContext. Na przykład jeśli nazwa instancji DbContext to dbContext
źródło
Wiem, że to stary wątek, ale szukałem również podobnego rozwiązania i postanowiłem skorzystać z dostarczonego rozwiązania @ Doku. Komentuję, aby odpowiedzieć na pytanie zadane przez @Imran Rizvi, podążyłem za linkiem @ Doku-so, który pokazuje podobną implementację. @Imran Rizvi zadał pytanie, że dostaje błąd przy użyciu dostarczonego rozwiązania „Nie można przekonwertować wyrażenia Lambda na typ„ Wyrażenie> [] ”, ponieważ nie jest to typ delegata”. Chciałem zaoferować niewielką modyfikację, którą wprowadziłem w rozwiązaniu @ Doku-so, która naprawia ten błąd w przypadku, gdy ktoś napotka ten post i zdecyduje się użyć rozwiązania @ Doku-so.
Problem jest drugim argumentem w metodzie aktualizacji,
Aby wywołać tę metodę przy użyciu dostarczonej składni ...
Musisz dodać słowo kluczowe „params” przed drugim argumentem.
lub jeśli nie chcesz zmieniać podpisu metody, a następnie, aby wywołać metodę Update, musisz dodać słowo kluczowe „ new ”, określić rozmiar tablicy, a następnie w końcu użyć składni inicjalizującej obiekt kolekcji dla każdej właściwości do zaktualizowania, jak widać poniżej.
W przykładzie @ Doku-so określa tablicę wyrażeń, więc musisz przekazać właściwości, aby zaktualizować tablicę, ponieważ ze względu na tablicę musisz także określić jej rozmiar. Aby tego uniknąć, możesz również zmienić argument wyrażenia, aby używał IEnumerable zamiast tablicy.
Oto moja implementacja rozwiązania @ Doku-so.
Stosowanie:
@ Doku-so zapewniło fajne podejście przy użyciu ogólnych, użyłem tej koncepcji, aby rozwiązać mój problem, ale po prostu nie możesz użyć rozwiązania @ Doku-so w takiej postaci, w jakiej się znajduje, i zarówno w tym poście, jak i w łączonym poście nikt nie odpowiedział na pytania o błędzie użytkowania.
źródło
entityEntry.State = EntityState.Unchanged;
wszystkie zaktualizowane wartości w parametrzeentityEntry
get cofnij, więc żadne zmiany nie są zapisywane, czy możesz pomóc w tym, dziękujęW EntityFramework Core 2.x nie ma potrzeby
Attach
:Próbowałem tego w SQL Server i profilowałem:
Znajdź zapewnia, że już załadowane encje nie wyzwalają SELECT, a także automatycznie dołącza encję w razie potrzeby (z dokumentacji):
źródło
Łącząc kilka sugestii proponuję następujące:
nazwany przez
Lub przez
Lub przez
źródło
Używam
ValueInjecter
nugetu do wstrzykiwania modelu wiązania do encji bazy danych przy użyciu:Zwróć uwagę na użycie niestandardowej konwencji, która nie aktualizuje właściwości, jeśli są one zerowe z serwera.
ValueInjecter v3 +
Stosowanie:
Wartość wtryskiwacza V2
Wyszukaj tę odpowiedź
Zastrzeżenie
Nie będziesz wiedział, czy właściwość została celowo wyczyszczona na wartość zerową LUB po prostu nie miała żadnej wartości. Innymi słowy, wartość właściwości może być zastąpiona inną wartością, ale nie wyczyszczona.
źródło
Szukałem tego samego i wreszcie znalazłem rozwiązanie
uwierz mi, działa dla mnie jak urok.
źródło
Właśnie tego używam, używając niestandardowego InjectNonNull (obj dest, obj src), dzięki czemu jest w pełni elastyczny
źródło
źródło
źródło