Entidade Framework 5 Atualizando um registro

Eu tenho explorado diferentes methods de edição / atualização de um registro dentro do Entity Framework 5 em um ambiente ASP.NET MVC3, mas até agora nenhum deles marca todas as checkboxs que eu preciso. Eu explicarei porque.

Eu encontrei três methods para os quais eu mencionarei os prós e contras:

Método 1 – Carregar o registro original, atualizar cada propriedade

var original = db.Users.Find(updatedUser.UserId); if (original != null) { original.BusinessEntityId = updatedUser.BusinessEntityId; original.Email = updatedUser.Email; original.EmployeeId = updatedUser.EmployeeId; original.Forename = updatedUser.Forename; original.Surname = updatedUser.Surname; original.Telephone = updatedUser.Telephone; original.Title = updatedUser.Title; original.Fax = updatedUser.Fax; original.ASPNetUserId = updatedUser.ASPNetUserId; db.SaveChanges(); } 

Prós

  • Pode especificar quais propriedades mudam
  • Visualizações não precisam conter todas as propriedades

Contras

  • 2 x consultas no database para carregar o original e atualizá-lo

Método 2 – Carregar registro original, definir valores alterados

 var original = db.Users.Find(updatedUser.UserId); if (original != null) { db.Entry(original).CurrentValues.SetValues(updatedUser); db.SaveChanges(); } 

Prós

  • Apenas propriedades modificadas são enviadas para o database

Contras

  • As visualizações precisam conter todas as propriedades
  • 2 x consultas no database para carregar o original e atualizá-lo

Método 3 – Anexar registro atualizado e definir estado para EntityState.Modified

 db.Users.Attach(updatedUser); db.Entry(updatedUser).State = EntityState.Modified; db.SaveChanges(); 

Prós

  • 1 x consulta no database para atualizar

Contras

  • Não é possível especificar quais propriedades mudam
  • As visualizações devem conter todas as propriedades

Questão

Minha pergunta para vocês; Existe uma maneira limpa que eu possa alcançar este conjunto de metas?

  • Pode especificar quais propriedades mudam
  • As visualizações não precisam conter todas as propriedades (como senha!)
  • 1 x consulta no database para atualizar

Eu entendo que isso é uma pequena coisa a destacar, mas posso estar perdendo uma solução simples para isso. Se não o método um prevalecerá 😉

Você está procurando:

 db.Users.Attach(updatedUser); var entry = db.Entry(updatedUser); entry.Property(e => e.Email).IsModified = true; // other changed properties db.SaveChanges(); 

Eu realmente gosto da resposta aceita. Eu acredito que há ainda outra maneira de abordar isso também. Digamos que você tenha uma lista muito pequena de propriedades que você não desejaria include em um View, portanto, ao atualizar a entidade, elas seriam omitidas. Digamos que esses dois campos sejam senha e SSN.

 db.Users.Attach(updatedUser); var entry = db.Entry(updatedUser); entry.State = EntityState.Modified; entry.Property(e => e.Password).IsModified = false; entry.Property(e => e.SSN).IsModified = false; db.SaveChanges(); 

Este exemplo permite que você essencialmente deixe sua lógica de negócios sozinha após adicionar um novo campo à sua tabela Usuários e à sua Visualização.

 foreach(PropertyInfo propertyInfo in original.GetType().GetProperties()) { if (propertyInfo.GetValue(updatedUser, null) == null) propertyInfo.SetValue(updatedUser, propertyInfo.GetValue(original, null), null); } db.Entry(original).CurrentValues.SetValues(updatedUser); db.SaveChanges(); 

Eu adicionei um método de atualização extra na minha class base de repository que é semelhante ao método de atualização gerado pelo Scaffolding. Em vez de definir o object inteiro como “modificado”, ele define um conjunto de propriedades individuais. (T é um parâmetro genérico de class.)

 public void Update(T obj, params Expression>[] propertiesToUpdate) { Context.Set().Attach(obj); foreach (var p in propertiesToUpdate) { Context.Entry(obj).Property(p).IsModified = true; } } 

E então ligar, por exemplo:

 public void UpdatePasswordAndEmail(long userId, string password, string email) { var user = new User {UserId = userId, Password = password, Email = email}; Update(user, u => u.Password, u => u.Email); Save(); } 

Eu gosto de uma viagem para o database. É provavelmente melhor fazer isso com modelos de visualização, a fim de evitar conjuntos repetitivos de propriedades. Ainda não fiz isso porque não sei como evitar levar as mensagens de validação em meus validadores de modelo de visualização para meu projeto de domínio.

 public interface IRepository { void Update(T obj, params Expression>[] propertiesToUpdate) where T : class; } public class Repository : DbContext, IRepository { public void Update(T obj, params Expression>[] propertiesToUpdate) where T : class { Set().Attach(obj); propertiesToUpdate.ToList().ForEach(p => Entry(obj).Property(p).IsModified = true); SaveChanges(); } } 

Apenas para adicionar à lista de opções. Você também pode pegar o object do database e usar uma ferramenta de mapeamento automático como o Auto Mapper para atualizar as partes do registro que você deseja alterar.

Dependendo do seu caso de uso, todas as soluções acima se aplicam. É assim que normalmente faço no entanto:

Para código do lado do servidor (por exemplo, um processo em lote), geralmente carrego as entidades e trabalho com proxies dynamics. Geralmente, em processos em lote, você precisa carregar os dados de qualquer maneira no momento em que o serviço é executado. Eu tento carregar em lote os dados em vez de usar o método find para economizar algum tempo. Dependendo do processo, uso o controle de simultaneidade otimista ou pessimista (sempre uso otimista, exceto para cenários de execução paralela, em que preciso bloquear alguns registros com instruções sql simples, mas isso é raro). Dependendo do código e cenário, o impacto pode ser reduzido a quase zero.

Para cenários do lado do cliente, você tem algumas opções

  1. Use modelos de visualização. Os modelos devem ter uma propriedade UpdateStatus (unmodified-inserted-updated-deleted). É de responsabilidade do cliente definir o valor correto para essa coluna, dependendo das ações do usuário (insert-update-delete). O servidor pode consultar o database para os valores originais ou o cliente deve enviar os valores originais para o servidor juntamente com as linhas alteradas. O servidor deve append os valores originais e usar a coluna UpdateStatus para cada linha para decidir como manipular os novos valores. Nesse cenário, sempre uso concorrência simultânea. Isso só fará as instruções insert-update-delete e não quaisquer seleções, mas pode precisar de algum código inteligente para percorrer o gráfico e atualizar as entidades (depende do seu cenário – aplicativo). Um mapeador pode ajudar, mas não lida com a lógica CRUD

  2. Use uma biblioteca como breeze.js que esconda a maior parte dessa complexidade (conforme descrito em 1) e tente ajustá-la ao seu caso de uso.

Espero que ajude