К Password, вы имеете в виду хэш пароля, не так ли? :-)
Эдвард Брей
Ответы:
370
Ответ Ладислава обновлен для использования DbContext (представлен в 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();}}
Этот подход вызывает исключение OptimisticConcurencyException, когда в таблице есть поле метки времени.
Максим Ви.
9
Я думаю, стоит упомянуть, что, если вы используете, db.Configuration.ValidateOnSaveEnabled = false;вы можете продолжить проверять поле, которое вы обновляете:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Ziul
2
Вам необходимо установить для ValidateOnSaveEnabled значение false, если у вас есть обязательные поля в таблице, которые вы не предоставляете во время обновления
Sal
54
Вы можете сказать EF, какие свойства должны быть обновлены таким образом:
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();}}
загрузить объект на основе userIdпредоставленного - весь объект загружается
обновить passwordполе
сохранить объект обратно с помощью контекста в .SaveChanges()метод
В этом случае это зависит от EF, как обращаться с этим подробно. Я только что проверил это, и в случае, если я изменяю только одно поле объекта, то, что создает EF, - это почти то же самое, что вы бы тоже создали вручную - что-то вроде:
`UPDATE dbo.Users SET Password =@Password WHERE UserId =@UserId`
Таким образом, EF достаточно умен, чтобы выяснить, какие столбцы действительно изменились, и он создаст оператор T-SQL для обработки только тех обновлений, которые действительно необходимы.
Вы определяете хранимую процедуру, которая делает именно то, что вам нужно, в коде T-SQL (просто обновите Passwordстолбец для заданного UserIdи ничего больше - в основном выполняется UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId), и вы создаете функцию импорта для этой хранимой процедуры в вашей модели EF, и вы вызываете это функция вместо того, чтобы делать шаги, описанные выше
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;}}
код доступа:
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();
Когда я пытаюсь это сделать, я получаю ошибки проверки сущности, но это выглядит здорово.
Devlord
Не работает этот метод !!!: может быть, вам нужно дать более подробную информацию, как его использовать !!! - это ошибка: «Не удалось подключить объект типа« Domain.Job », потому что другой объект того же типа уже имеет такое же значение первичного ключа. Это может произойти при использовании метода« Присоединить »или установке состояния объекта в значение «Не изменено» или «Изменено», если какие-либо объекты в графе имеют конфликтующие значения ключей. Это может быть связано с тем, что некоторые объекты являются новыми и еще не получили значения ключей, сгенерированные базой данных ».
Лучиан Бамб
Perfec! Проверьте мой ответ, чтобы увидеть гибкий подход для любой модели!
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();}
« Как видите, он принимает в качестве второго параметра выражение функции. Это позволит использовать этот метод, указав в лямбда-выражении, какое свойство обновлять ».
Метод, который я сейчас использую в своем собственном коде , расширен также для обработки (Linq) выражений типа ExpressionType.Convert. Это было необходимо в моем случае, например, с Guidдругими свойствами объекта. Они были «обернуты» в Convert () и поэтому не были обработаны System.Web.Mvc.ExpressionHelper.GetExpressionText.
Когда я использую это, я получаю следующую ошибку: «Невозможно преобразовать лямбда-выражение в тип« Выражение <Func <RequestDetail, объект >> [] », поскольку это не тип делегата
Имран Ризви,
@ImranRizvi, вам просто нужно обновить параметры: public int Update (свойства T, параметры params <Func <T, object >> [] свойства) ПРИМЕЧАНИЕ ключевое слово params перед выражением
dalcam
6
Я опаздываю к игре здесь, но вот как я это делаю, я потратил некоторое время на поиски решения, которое меня удовлетворило; это создает UPDATEоператор ТОЛЬКО для измененных полей, так как вы явно определяете их через концепцию «белого списка», которая в любом случае более безопасна для предотвращения внедрения веб-форм.
Выдержка из моего хранилища данных 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;}
Это можно обернуть в попытку… поймать, если вы того пожелаете, но мне лично нравится, чтобы звонивший знал об исключениях в этом сценарии.
Он будет вызываться примерно так (для меня это было через веб-API ASP.NET):
if(!session.Update(franchiseViewModel.Franchise, new[]{"Name","StartDate"}))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
Значит, твое лучшее решение - это то, что Элиза? Вы должны явно указать, какие свойства вы разрешаете обновлять (точно так же, как белый список, необходимый для команды ASP.NET MVC UpdateModel), таким образом вы гарантируете, что внедрение хакерской формы не может произойти, и они не могут обновить поля, которые им не разрешено обновлять. Если, однако, кто-то может преобразовать строковый массив в какой-то параметр лямбда-выражений и работать с ним в Update<T>отличном состоянии
@Elisa Это можно улучшить, используя Func <T, List <object >> вместо строки []
Губка Боб Товарищ
Даже позже в игре, и, возможно, это гораздо более поздний синтаксис, но var entity=_context.Set<T>().Attach(item);затем entity.Property(propertyName).IsModified = true;в цикле должно работать.
Auspex
4
Entity Framework отслеживает ваши изменения объектов, которые вы запросили из базы данных через DbContext. Например, если у вас имя экземпляра DbContext - dbContext
public void ChangePassword(int userId, string password){
var user= dbContext.Users.FirstOrDefault(u=>u.UserId == userId);user.password = password;
dbContext.SaveChanges();}
Это неправильно, потому что это сохранит весь объект User с измененным паролем.
amuliar
это правда, но остальная часть объекта User будет такой же, как это было ранее в контексте, единственное, что, возможно, будет отличаться, это пароль, так что, по сути, он только обновляет пароль.
Tomislav3008
3
Я знаю, что это старая ветка, но я также искал похожее решение и решил воспользоваться решением @ Doku-so. Я комментирую, чтобы ответить на вопрос, заданный @Imran Rizvi, я перешел по ссылке @ Doku-so, на которой показана аналогичная реализация. Вопрос Имрана Ризви состоял в том, что он получал ошибку, используя предоставленное решение «Невозможно преобразовать лямбда-выражение в тип« Выражение> [] », потому что это не тип делегата». Я хотел предложить небольшую модификацию решения @ Doku-so, которую я сделал, чтобы исправить эту ошибку в случае, если кто-то еще наткнется на этот пост и решит использовать решение @ Doku-so.
Проблема является вторым аргументом в методе обновления,
public int Update(T entity, Expression<Func<T, object>>[] properties).
Для вызова этого метода используется синтаксис, предоставленный ...
Вы должны добавить ключевое слово 'params' перед вторым arugment как таковое.
public int Update(T entity, params Expression<Func<T, object>>[] properties)
или если вы не хотите изменять сигнатуру метода, то для вызова метода Update вам нужно добавить ключевое слово ' new ', указать размер массива, а затем, наконец, использовать синтаксис инициализатора объекта коллекции для каждого свойства, чтобы обновить, как видно ниже.
Update(Model, new Expression<Func<T, object>>[3]{ d=>d.Name },{ d=>d.SecondProperty },{ d=>d.AndSoOn });
В примере @ Doku-so он указывает массив выражений, поэтому вы должны передать свойства для обновления в массиве, потому что для массива вы также должны указать размер массива. Чтобы избежать этого, вы также можете изменить аргумент выражения, чтобы использовать IEnumerable вместо массива.
Вот моя реализация решения @ 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();}
Использование:
this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);
@ Doku-so предоставил классный подход с использованием дженериков, я использовал концепцию для решения своей проблемы, но вы просто не можете использовать решение @ Doku-so как есть, и в этом посте, и в связанном посте никто не ответил на вопросы об ошибках использования.
Я работал над вашим решением, когда программа передает строку, entityEntry.State = EntityState.Unchanged;все обновленные значения в параметре entityEntryget возвращаются, поэтому никакие изменения не сохраняются, не могли бы вы помочь с этим, спасибо
sairfan
3
В EntityFramework Core 2.x нет необходимости Attach:
// get a tracked entity
var entity = context.User.Find(userId);
entity.someProp = someValue;// other property changes might come here
context.SaveChanges();
Попробовал это в SQL Server и профилировать его:
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 гарантирует, что уже загруженные сущности не вызывают SELECT, а также автоматически присоединяет сущность при необходимости (из документов):
/// 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.
Как сделать этот метод доступным для другого класса, может быть как метод расширения?
Велкумар
В этом уроке .NET CORE они показывают лучшие практики использования (нового) EF Core для обновления определенных свойств в MVC. ищите 'TryUpdateModelAsync'.
Парень
1
@ Гай Круто. Тем не менее, еще раз «лучшая практика» Microsoft - это делать что-то помимо того, что создают их инструменты ...
Auspex
Это хорошее решение.
Тимоти Мачария
1
Я использую ValueInjecternuget для внедрения Binding Model в Entity базы данных, используя следующее:
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();}
Обратите внимание на использование пользовательского соглашения, которое не обновляет свойства, если они нулевые с сервера.
Вы не будете знать, было ли свойство намеренно очищено до нуля, ИЛИ оно просто не имело никакого значения. Другими словами, значение свойства может быть заменено только другим значением, но не очищено.
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
, вы имеете в виду хэш пароля, не так ли? :-)Ответы:
Ответ Ладислава обновлен для использования DbContext (представлен в EF 4.1):
источник
db.Entry(user).Property(x => x.Password).IsModified = true;
и нетdb.Entry(user).Property("Password").IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
вы можете продолжить проверять поле, которое вы обновляете:if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Вы можете сказать EF, какие свойства должны быть обновлены таким образом:
источник
У вас есть в основном два варианта:
userId
предоставленного - весь объект загружаетсяpassword
поле.SaveChanges()
методВ этом случае это зависит от EF, как обращаться с этим подробно. Я только что проверил это, и в случае, если я изменяю только одно поле объекта, то, что создает EF, - это почти то же самое, что вы бы тоже создали вручную - что-то вроде:
Таким образом, EF достаточно умен, чтобы выяснить, какие столбцы действительно изменились, и он создаст оператор T-SQL для обработки только тех обновлений, которые действительно необходимы.
Password
столбец для заданногоUserId
и ничего больше - в основном выполняетсяUPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId
), и вы создаете функцию импорта для этой хранимой процедуры в вашей модели EF, и вы вызываете это функция вместо того, чтобы делать шаги, описанные вышеисточник
В Entity Framework Core
Attach
возвращает запись, поэтому все, что вам нужно, это:источник
Я использую это:
организация:
DbContext:
код доступа:
источник
В поисках решения этой проблемы я нашел вариант ответа GONeale в блоге Патрика Дежарденса :
(Несколько похожее решение также приводится здесь: https://stackoverflow.com/a/5749469/2115384 )
Метод, который я сейчас использую в своем собственном коде , расширен также для обработки (Linq) выражений типа
ExpressionType.Convert
. Это было необходимо в моем случае, например, сGuid
другими свойствами объекта. Они были «обернуты» в Convert () и поэтому не были обработаныSystem.Web.Mvc.ExpressionHelper.GetExpressionText
.источник
Я опаздываю к игре здесь, но вот как я это делаю, я потратил некоторое время на поиски решения, которое меня удовлетворило; это создает
UPDATE
оператор ТОЛЬКО для измененных полей, так как вы явно определяете их через концепцию «белого списка», которая в любом случае более безопасна для предотвращения внедрения веб-форм.Выдержка из моего хранилища данных ISession:
Это можно обернуть в попытку… поймать, если вы того пожелаете, но мне лично нравится, чтобы звонивший знал об исключениях в этом сценарии.
Он будет вызываться примерно так (для меня это было через веб-API ASP.NET):
источник
UpdateModel
), таким образом вы гарантируете, что внедрение хакерской формы не может произойти, и они не могут обновить поля, которые им не разрешено обновлять. Если, однако, кто-то может преобразовать строковый массив в какой-то параметр лямбда-выражений и работать с ним вUpdate<T>
отличном состоянииvar entity=_context.Set<T>().Attach(item);
затемentity.Property(propertyName).IsModified = true;
в цикле должно работать.Entity Framework отслеживает ваши изменения объектов, которые вы запросили из базы данных через DbContext. Например, если у вас имя экземпляра DbContext - dbContext
источник
Я знаю, что это старая ветка, но я также искал похожее решение и решил воспользоваться решением @ Doku-so. Я комментирую, чтобы ответить на вопрос, заданный @Imran Rizvi, я перешел по ссылке @ Doku-so, на которой показана аналогичная реализация. Вопрос Имрана Ризви состоял в том, что он получал ошибку, используя предоставленное решение «Невозможно преобразовать лямбда-выражение в тип« Выражение> [] », потому что это не тип делегата». Я хотел предложить небольшую модификацию решения @ Doku-so, которую я сделал, чтобы исправить эту ошибку в случае, если кто-то еще наткнется на этот пост и решит использовать решение @ Doku-so.
Проблема является вторым аргументом в методе обновления,
Для вызова этого метода используется синтаксис, предоставленный ...
Вы должны добавить ключевое слово 'params' перед вторым arugment как таковое.
или если вы не хотите изменять сигнатуру метода, то для вызова метода Update вам нужно добавить ключевое слово ' new ', указать размер массива, а затем, наконец, использовать синтаксис инициализатора объекта коллекции для каждого свойства, чтобы обновить, как видно ниже.
В примере @ Doku-so он указывает массив выражений, поэтому вы должны передать свойства для обновления в массиве, потому что для массива вы также должны указать размер массива. Чтобы избежать этого, вы также можете изменить аргумент выражения, чтобы использовать IEnumerable вместо массива.
Вот моя реализация решения @ Doku-so.
Использование:
@ Doku-so предоставил классный подход с использованием дженериков, я использовал концепцию для решения своей проблемы, но вы просто не можете использовать решение @ Doku-so как есть, и в этом посте, и в связанном посте никто не ответил на вопросы об ошибках использования.
источник
entityEntry.State = EntityState.Unchanged;
все обновленные значения в параметреentityEntry
get возвращаются, поэтому никакие изменения не сохраняются, не могли бы вы помочь с этим, спасибоВ EntityFramework Core 2.x нет необходимости
Attach
:Попробовал это в SQL Server и профилировать его:
Find гарантирует, что уже загруженные сущности не вызывают SELECT, а также автоматически присоединяет сущность при необходимости (из документов):
источник
Объединяя несколько предложений, я предлагаю следующее:
называется
Или
Или
источник
Я использую
ValueInjecter
nuget для внедрения Binding Model в Entity базы данных, используя следующее:Обратите внимание на использование пользовательского соглашения, которое не обновляет свойства, если они нулевые с сервера.
ValueInjecter v3 +
Использование:
Значение Injecter V2
Найти этот ответ
Предостережение
Вы не будете знать, было ли свойство намеренно очищено до нуля, ИЛИ оно просто не имело никакого значения. Другими словами, значение свойства может быть заменено только другим значением, но не очищено.
источник
Я искал то же самое и, наконец, я нашел решение
поверьте мне, это работает для меня как шарм.
источник
Это то, что я использую, используя собственный InjectNonNull (obj dest, obj src), что делает его полностью гибким
источник
источник
источник