Com Password, você quer dizer senha com hash, certo? :-)
Edward Brey
Respostas:
370
Resposta de Ladislav atualizada para usar o DbContext (introduzido no 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();}}
Eu só consegui fazer esse código funcionar adicionando db.Configuration.ValidateOnSaveEnabled = false; antes de db.SaveChanges ()?
Jake de Drew
3
Que namespace para incluir a usar db.Entry(user).Property(x => x.Password).IsModified = true;e nãodb.Entry(user).Property("Password").IsModified = true;
Johan
5
Essa abordagem lança uma OptimisticConcurencyException quando a tabela possui um campo de carimbo de data / hora.
Maksim Vi.
9
Eu acho que vale a pena mencionar que, se você estiver usando, db.Configuration.ValidateOnSaveEnabled = false;poderá continuar validando o campo que está atualizando: #if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
221
2
Você precisa definir ValidateOnSaveEnabled como false se tiver exigido campos em sua tabela que você não está fornecendo durante a atualização.
Sal
54
Você pode informar ao EF quais propriedades devem ser atualizadas desta maneira:
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 não está disponível para DBContext
LoxLox
17
Você tem basicamente duas opções:
percorrer todo o caminho da EF, nesse caso, você faria
carregar o objeto com base no userIdfornecido - o objeto inteiro é carregado
atualize o passwordcampo
salve o objeto de volta usando o .SaveChanges()método do contexto
Nesse caso, cabe à EF como lidar com isso em detalhes. Acabei de testar isso e, no caso de alterar apenas um único campo de um objeto, o que o EF cria é basicamente o que você criaria manualmente também - algo como:
`UPDATE dbo.Users SET Password =@Password WHERE UserId =@UserId`
Portanto, a EF é inteligente o suficiente para descobrir quais colunas realmente mudaram e criará uma instrução T-SQL para lidar apenas com as atualizações necessárias.
você define um procedimento armazenado que faz exatamente o que precisa, no código T-SQL (basta atualizar a Passwordcoluna para o dado UserIde nada mais - basicamente é executado UPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId) e cria uma importação de função para esse procedimento armazenado no seu modelo EF e você chama isso em vez de executar as etapas descritas acima
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;}}
código do acessador:
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();
Eu recebo erros de validação de entidade quando tento isso, mas com certeza parece legal.
Devlord # /
Não funciona esse método !!!: talvez você precise fornecer mais detalhes de como usá-lo !!! - este é o erro: "Anexar uma entidade do tipo 'Domain.Job' falhou porque outra entidade do mesmo tipo já possui o mesmo valor de chave primária. Isso pode acontecer ao usar o método 'Attach' ou ao definir o estado de uma entidade para 'Inalterado' ou 'Modificado' se alguma entidade no gráfico tiver valores-chave conflitantes. Isso pode ocorrer porque algumas entidades são novas e ainda não receberam valores-chave gerados pelo banco de dados. "
Lucian Bumb
Perfec! Verifique minha resposta para ver uma abordagem flexível para qualquer modelo!
Kryp
10
Enquanto procurava uma solução para esse problema, encontrei uma variação na resposta de GONeale no blog de Patrick Desjardins :
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();}
" Como você pode ver, assume como segundo parâmetro uma expressão de uma função. Isso permitirá usar esse método especificando em uma expressão Lambda qual propriedade atualizar. "
O método que estou usando atualmente no meu próprio código , estendido para manipular também (Linq) expressões do tipo ExpressionType.Convert. Isso foi necessário no meu caso, por exemplo, com Guide outras propriedades do objeto. Esses foram 'agrupados' em um Convert () e, portanto, não são tratados por System.Web.Mvc.ExpressionHelper.GetExpressionText.
Quando eu uso este que me dá seguinte erro, não é possível converter expressão Lambda para 'Expression <Func <RequestDetail, objeto >> []' Tipo, porque não é um tipo de delegado
Imran Rizvi
@ImranRizvi, você simplesmente precisa atualizar os parâmetros para: int público Update (entidade T, params Expression <Func <T, objeto >> [] propriedades) NOTA os params palavra-chave antes de expressão
dalcam
6
Estou atrasado para o jogo aqui, mas é assim que faço, passei um tempo procurando uma solução que me satisfizesse; isso produz umUPDATE APENAS declaração para os campos alterados, à medida que você define explicitamente o que eles são por meio de um conceito de "lista branca" que é mais seguro para impedir a injeção de formulários da Web de qualquer maneira.
Um trecho do meu repositório de dados 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;}
Isso pode ser envolvido em uma tentativa ... captura, se você desejar, mas eu pessoalmente gosto que meu interlocutor saiba das exceções nesse cenário.
Seria chamado assim: (para mim, isso foi por meio de uma API da Web do ASP.NET):
if(!session.Update(franchiseViewModel.Franchise, new[]{"Name","StartDate"}))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
Então, sua melhor solução é o que Elisa? Você deve declarar explicitamente quais propriedades você deseja atualizar (assim como a lista branca necessária para o UpdateModelcomando do ASP.NET MVC ), para garantir que a injeção de formulários de hackers não ocorra e que eles não possam atualizar os campos que não podem atualizar. Se, no entanto, alguém puder converter o array de strings para algum tipo de parâmetro de expressões lambda e trabalhar com ele no Update<T>, ótimo
@Elisa Ele pode ser aprimorado usando Func <T, List <object>> em vez de string []
Spongebob Camarada
Ainda mais tarde para o jogo, e talvez essa seja uma sintaxe muito mais recente, mas var entity=_context.Set<T>().Attach(item);seguida pelo entity.Property(propertyName).IsModified = true;loop deve funcionar.
Auspex
4
A estrutura de entidades rastreia suas alterações nos objetos que você consultou do banco de dados via DbContext. Por exemplo, se o nome da instância DbContext for dbContext
public void ChangePassword(int userId, string password){
var user= dbContext.Users.FirstOrDefault(u=>u.UserId == userId);user.password = password;
dbContext.SaveChanges();}
Isso está errado porque salvaria o objeto Usuário inteiro com a senha alterada.
amuliar
isso é verdade, mas o restante do objeto Usuário será o mesmo que anteriormente no contexto, a única coisa que possivelmente será diferente é a senha, portanto, essencialmente, ela só é atualizada.
Tomislav3008
3
Eu sei que esse é um tópico antigo, mas eu também estava procurando uma solução semelhante e decidi seguir com a solução @ Doku-so fornecida. Estou comentando para responder à pergunta feita por @Imran Rizvi, segui o link @ Doku-so, que mostra uma implementação semelhante. A pergunta de @Imran Rizvi foi que ele estava recebendo um erro usando a solução fornecida 'Não é possível converter a expressão Lambda para o tipo' Expressão> [] 'porque não é um tipo de delegado'. Eu queria oferecer uma pequena modificação que fiz na solução da @ Doku-so que corrige esse erro, caso alguém se depare com este post e decida usar a solução da @ Doku-so.
O problema é o segundo argumento no método Update,
public int Update(T entity, Expression<Func<T, object>>[] properties).
Para chamar esse método usando a sintaxe fornecida ...
Você deve adicionar a palavra-chave 'params' na frente da segunda versão assim.
public int Update(T entity, params Expression<Func<T, object>>[] properties)
ou, se você não quiser alterar a assinatura do método, para chamar o método Update, adicione a palavra-chave ' new ', especifique o tamanho da matriz e, finalmente, use a sintaxe do inicializador do objeto de coleção para cada propriedade a ser atualizada, como visto abaixo.
Update(Model, new Expression<Func<T, object>>[3]{ d=>d.Name },{ d=>d.SecondProperty },{ d=>d.AndSoOn });
No exemplo do @ Doku-so, ele está especificando uma matriz de Expressões, portanto você deve passar as propriedades para atualizar em uma matriz, por causa da matriz, você também deve especificar o tamanho da matriz. Para evitar isso, você também pode alterar o argumento da expressão para usar IEnumerable em vez de uma matriz.
Aqui está minha implementação da solução do @ 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();}
Uso:
this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);
@ Doku-so forneceu uma abordagem legal usando genéricos, usei o conceito para resolver meu problema, mas você simplesmente não pode usar a solução @ Doku-so como está e, tanto neste post como no post vinculado, ninguém respondeu às perguntas sobre erros de uso.
Eu estava trabalhando em sua solução quando passes programa de linha de entityEntry.State = EntityState.Unchanged;todos os valores atualizados em parâmetro entityEntryget revert, por isso há alterações são salvas, você pode por favor me ajude sobre ele, graças
sairfan
3
No EntityFramework Core 2.x, não há necessidade de Attach:
// get a tracked entity
var entity = context.User.Find(userId);
entity.someProp = someValue;// other property changes might come here
context.SaveChanges();
Tentei isso no SQL Server e crie um perfil:
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
O Find garante que as entidades já carregadas não acionem um SELECT e também anexa automaticamente a entidade, se necessário (nos documentos):
/// 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.
Como disponibilizar esse método para outra classe, pode ser como o método de extensão?
Velkumar
no presente .NET NÚCLEO tutorial mostram melhores práticas usando (o novo) EF Core para propriedades de atualização específica em MVC. procure por 'TryUpdateModelAsync'.
12136 Guy
1
@Guy Awesome. Porém, mais uma vez "melhores práticas" da Microsoft é fazer algo diferente do que suas ferramentas de construção ...
Auspex
Esta é uma boa solução.
Timothy Macharia
1
Eu uso o ValueInjecternuget para injetar o Modelo de Ligação na Entidade de banco de dados usando o seguinte:
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();}
Observe o uso da convenção personalizada que não atualiza as Propriedades se elas forem nulas do servidor.
Você não saberá se a propriedade foi intencionalmente limpa para nula OU se simplesmente não tinha nenhum valor. Em outras palavras, o valor da propriedade só pode ser substituído por outro valor, mas não limpo.
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
, você quer dizer senha com hash, certo? :-)Respostas:
Resposta de Ladislav atualizada para usar o DbContext (introduzido no EF 4.1):
fonte
db.Entry(user).Property(x => x.Password).IsModified = true;
e nãodb.Entry(user).Property("Password").IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
poderá continuar validando o campo que está atualizando: #if (db.Entry(user).Property(x => x.Password).GetValidationErrors().Count == 0)
Você pode informar ao EF quais propriedades devem ser atualizadas desta maneira:
fonte
Você tem basicamente duas opções:
userId
fornecido - o objeto inteiro é carregadopassword
campo.SaveChanges()
método do contextoNesse caso, cabe à EF como lidar com isso em detalhes. Acabei de testar isso e, no caso de alterar apenas um único campo de um objeto, o que o EF cria é basicamente o que você criaria manualmente também - algo como:
Portanto, a EF é inteligente o suficiente para descobrir quais colunas realmente mudaram e criará uma instrução T-SQL para lidar apenas com as atualizações necessárias.
Password
coluna para o dadoUserId
e nada mais - basicamente é executadoUPDATE dbo.Users SET Password = @Password WHERE UserId = @UserId
) e cria uma importação de função para esse procedimento armazenado no seu modelo EF e você chama isso em vez de executar as etapas descritas acimafonte
No Entity Framework Core,
Attach
retorna a entrada, então tudo que você precisa é:fonte
eu estou usando isso:
entidade:
dbcontext:
código do acessador:
fonte
Enquanto procurava uma solução para esse problema, encontrei uma variação na resposta de GONeale no blog de Patrick Desjardins :
(Uma solução semelhante é também fornecida aqui: https://stackoverflow.com/a/5749469/2115384 )
O método que estou usando atualmente no meu próprio código , estendido para manipular também (Linq) expressões do tipo
ExpressionType.Convert
. Isso foi necessário no meu caso, por exemplo, comGuid
e outras propriedades do objeto. Esses foram 'agrupados' em um Convert () e, portanto, não são tratados porSystem.Web.Mvc.ExpressionHelper.GetExpressionText
.fonte
Estou atrasado para o jogo aqui, mas é assim que faço, passei um tempo procurando uma solução que me satisfizesse; isso produz um
UPDATE
APENAS declaração para os campos alterados, à medida que você define explicitamente o que eles são por meio de um conceito de "lista branca" que é mais seguro para impedir a injeção de formulários da Web de qualquer maneira.Um trecho do meu repositório de dados ISession:
Isso pode ser envolvido em uma tentativa ... captura, se você desejar, mas eu pessoalmente gosto que meu interlocutor saiba das exceções nesse cenário.
Seria chamado assim: (para mim, isso foi por meio de uma API da Web do ASP.NET):
fonte
UpdateModel
comando do ASP.NET MVC ), para garantir que a injeção de formulários de hackers não ocorra e que eles não possam atualizar os campos que não podem atualizar. Se, no entanto, alguém puder converter o array de strings para algum tipo de parâmetro de expressões lambda e trabalhar com ele noUpdate<T>
, ótimovar entity=_context.Set<T>().Attach(item);
seguida peloentity.Property(propertyName).IsModified = true;
loop deve funcionar.A estrutura de entidades rastreia suas alterações nos objetos que você consultou do banco de dados via DbContext. Por exemplo, se o nome da instância DbContext for dbContext
fonte
Eu sei que esse é um tópico antigo, mas eu também estava procurando uma solução semelhante e decidi seguir com a solução @ Doku-so fornecida. Estou comentando para responder à pergunta feita por @Imran Rizvi, segui o link @ Doku-so, que mostra uma implementação semelhante. A pergunta de @Imran Rizvi foi que ele estava recebendo um erro usando a solução fornecida 'Não é possível converter a expressão Lambda para o tipo' Expressão> [] 'porque não é um tipo de delegado'. Eu queria oferecer uma pequena modificação que fiz na solução da @ Doku-so que corrige esse erro, caso alguém se depare com este post e decida usar a solução da @ Doku-so.
O problema é o segundo argumento no método Update,
Para chamar esse método usando a sintaxe fornecida ...
Você deve adicionar a palavra-chave 'params' na frente da segunda versão assim.
ou, se você não quiser alterar a assinatura do método, para chamar o método Update, adicione a palavra-chave ' new ', especifique o tamanho da matriz e, finalmente, use a sintaxe do inicializador do objeto de coleção para cada propriedade a ser atualizada, como visto abaixo.
No exemplo do @ Doku-so, ele está especificando uma matriz de Expressões, portanto você deve passar as propriedades para atualizar em uma matriz, por causa da matriz, você também deve especificar o tamanho da matriz. Para evitar isso, você também pode alterar o argumento da expressão para usar IEnumerable em vez de uma matriz.
Aqui está minha implementação da solução do @ Doku-so.
Uso:
@ Doku-so forneceu uma abordagem legal usando genéricos, usei o conceito para resolver meu problema, mas você simplesmente não pode usar a solução @ Doku-so como está e, tanto neste post como no post vinculado, ninguém respondeu às perguntas sobre erros de uso.
fonte
entityEntry.State = EntityState.Unchanged;
todos os valores atualizados em parâmetroentityEntry
get revert, por isso há alterações são salvas, você pode por favor me ajude sobre ele, graçasNo EntityFramework Core 2.x, não há necessidade de
Attach
:Tentei isso no SQL Server e crie um perfil:
O Find garante que as entidades já carregadas não acionem um SELECT e também anexa automaticamente a entidade, se necessário (nos documentos):
fonte
Combinando várias sugestões, proponho o seguinte:
chamado por
Ou pela
Ou pela
fonte
Eu uso o
ValueInjecter
nuget para injetar o Modelo de Ligação na Entidade de banco de dados usando o seguinte:Observe o uso da convenção personalizada que não atualiza as Propriedades se elas forem nulas do servidor.
ValueInjecter v3 +
Uso:
Value Injecter V2
Procure esta resposta
Embargo
Você não saberá se a propriedade foi intencionalmente limpa para nula OU se simplesmente não tinha nenhum valor. Em outras palavras, o valor da propriedade só pode ser substituído por outro valor, mas não limpo.
fonte
Eu estava procurando o mesmo e finalmente encontrei a solução
acredite em mim, funciona para mim como um encanto.
fonte
É isso que eu uso, usando o InjectNonNull personalizado (obj dest, obj src), que o torna totalmente flexível
fonte
fonte
fonte