Como atualizar o registro usando o Entity Framework 6?
var book = new Model.Book
{
BookNumber = _book.BookNumber,
BookName = _book.BookName,
BookTitle = _book.BookTitle,
};
using (var db = new MyContextDB())
{
var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
if (result != null)
{
try
{
db.Books.Attach(book);
db.Entry(book).State = EntityState.Modified;
db.SaveChanges();
}
catch (Exception ex)
{
throw;
}
}
}
Sempre que tento actualizar o registo usando o código acima, estou a receber este erro: -
{Sistema.Dado.Entidade.Infraestrutura.DbUpdateConcurrencyException: Store actualizar, inserir ou apagar a declaração afectou um número inesperado de linhas (0). As entidades podem ter sido modificadas ou suprimidas uma vez que as entidades estavam carregados. Actualizar O Gestor De Objectos entrie
14 answers
Você está tentando atualizar o registro (o que para mim significa "mudar um valor em um registro existente e salvá-lo de volta"). Então você precisa recuperar o objeto, fazer uma mudança, e salvá-lo.
using (var db = new MyContextDB())
{
var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
if (result != null)
{
result.SomeValue = "Some new value";
db.SaveChanges();
}
}
Estive a rever o código-fonte da estrutura de Entidades e encontrei uma forma de actualizar realmente uma entidade se souber a propriedade-Chave noutro caso, terá de verificar a implementação de adicionar Data:
public void Update<T>(T item) where T: Entity
{
// assume Entity base class have an Id property for all items
var entity = _collection.Find(item.Id);
if (entity == null)
{
return;
}
_context.Entry(entity).CurrentValues.SetValues(item);
}
Espero que isto ajude!
Pode utilizar o método AddOrUpdate
:
db.Books.AddOrUpdate(book); //requires using System.Data.Entity.Migrations;
db.SaveChanges();
Então você tem uma entidade que é atualizada, e você quer atualizá-la na base de dados com a menor quantidade de código... A concorrência é sempre complicada, mas presumo que só queira que as suas actualizações ganhem. Eis como o fiz para o mesmo caso e modifiquei os nomes para imitar as tuas aulas. IOW, basta mudar anexar para adicionar, e funciona para mim:
public static void SaveBook(Model.Book myBook)
{
using (var ctx = new BookDBContext())
{
ctx.Books.Add(myBook);
ctx.Entry(myBook).State = System.Data.Entity.EntityState.Modified;
ctx.SaveChanges();
}
}
Este código é o resultado de um teste para atualizar apenas um conjunto de colunas sem fazer uma consulta para retornar o registro primeiro. Usa primeiro o código EF 7.
//The function receives an object type to receive a custom object can be a view model or an anonymous object wit the properties you will like to change. This is part of a repository for a Contacts object.
public int Update(object entity)
{
var entityProperties = entity.GetType().GetProperties();
Contacts con = ToType(entity, typeof(Contacts)) as Contacts;
if (con != null)
{
_context.Entry(con).State = EntityState.Modified;
_context.Contacts.Attach(con);
foreach (var ep in entityProperties)
{
//Here is checking if the property is named Id dont add it in the update. It can be refactored to look in the annotations to find a key or to fin in the name any part named id
if(ep.Name != "Id")
_context.Entry(con).Property(ep.Name).IsModified = true;
}
}
return _context.SaveChanges();
}
public static object ToType<T>( object obj, T type)
{
//create instance of T type object:
object tmp = Activator.CreateInstance(Type.GetType(type.ToString()));
//loop through the properties of the object you want to covert:
foreach (PropertyInfo pi in obj.GetType().GetProperties())
{
try
{
//get the value of property and try to assign it to the property of T type object:
tmp.GetType().GetProperty(pi.Name).SetValue(tmp, pi.GetValue(obj, null), null);
}
catch (Exception ex)
{
// Logging.Log.Error(ex);
}
}
//return the T type object:
return tmp;
}
Aqui está o código completo:
public interface IContactRepository
{
IEnumerable<Contacts> GetAllContats();
IEnumerable<Contacts> GetAllContactsWithAddress();
int Update(object c);
}
public class ContactRepository : IContactRepository
{
private ContactContext _context;
public ContactRepository(ContactContext context)
{
_context = context;
}
public IEnumerable<Contacts> GetAllContats()
{
return _context.Contacts.OrderBy(c => c.FirstName).ToList();
}
public IEnumerable<Contacts> GetAllContactsWithAddress()
{
return _context.Contacts
.Include(c => c.Address)
.OrderBy(c => c.FirstName).ToList();
}
//TODO Cambiar properties a lambda expression
public int Update(object entity)
{
var entityProperties = entity.GetType().GetProperties();
Contacts con = ToType(entity, typeof(Contacts)) as Contacts;
if (con != null)
{
_context.Entry(con).State = EntityState.Modified;
_context.Contacts.Attach(con);
foreach (var ep in entityProperties)
{
if(ep.Name != "Id")
_context.Entry(con).Property(ep.Name).IsModified = true;
}
}
return _context.SaveChanges();
}
public static object ToType<T>( object obj, T type)
{
//create instance of T type object:
object tmp = Activator.CreateInstance(Type.GetType(type.ToString()));
//loop through the properties of the object you want to covert:
foreach (PropertyInfo pi in obj.GetType().GetProperties())
{
try
{
//get the value of property and try to assign it to the property of T type object:
tmp.GetType().GetProperty(pi.Name).SetValue(tmp, pi.GetValue(obj, null), null);
}
catch (Exception ex)
{
// Logging.Log.Error(ex);
}
}
//return the T type object:
return tmp;
}
}
public class Contacts
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Company { get; set; }
public string Title { get; set; }
public Addresses Address { get; set; }
}
public class Addresses
{
[Key]
public int Id { get; set; }
public string AddressType { get; set; }
public string StreetAddress { get; set; }
public string City { get; set; }
public State State { get; set; }
public string PostalCode { get; set; }
}
public class ContactContext : DbContext
{
public DbSet<Addresses> Address { get; set; }
public DbSet<Contacts> Contacts { get; set; }
public DbSet<State> States { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connString =
"Server=YourServer;Database=ContactsDb;Trusted_Connection=True;MultipleActiveResultSets=true;";
optionsBuilder.UseSqlServer(connString);
base.OnConfiguring(optionsBuilder);
}
}
Aqui está a melhor solução para este problema: na vista adicione todos os ID (chaves). Considere ter várias tabelas nomeadas (primeira, segunda e terceira)
@Html.HiddenFor(model=>model.FirstID)
@Html.HiddenFor(model=>model.SecondID)
@Html.HiddenFor(model=>model.Second.SecondID)
@Html.HiddenFor(model=>model.Second.ThirdID)
@Html.HiddenFor(model=>model.Second.Third.ThirdID)
Em código C#,
[HttpPost]
public ActionResult Edit(First first)
{
if (ModelState.Isvalid)
{
if (first.FirstID > 0)
{
datacontext.Entry(first).State = EntityState.Modified;
datacontext.Entry(first.Second).State = EntityState.Modified;
datacontext.Entry(first.Second.Third).State = EntityState.Modified;
}
else
{
datacontext.First.Add(first);
}
datacontext.SaveChanges();
Return RedirectToAction("Index");
}
return View(first);
}
Devias usar o. Método Entry () no caso de querer actualizar todos os campos do seu objecto. Tenha também em mente que você não pode alterar o ID do campo (Chave), portanto, primeiro defina o Id para alguns como você editar.
using(var context = new ...())
{
var EditedObj = context
.Obj
.Where(x => x. ....)
.First();
NewObj.Id = EditedObj.Id; //This is important when we first create an object (NewObj), in which the default Id = 0. We can not change an existing key.
context.Entry(EditedObj).CurrentValues.SetValues(NewObj);
context.SaveChanges();
}
Você deve remover db.Books.Attach(book);
using(var myDb = new MyDbEntities())
{
user user = new user();
user.username = "me";
user.email = "[email protected]";
myDb.Users.Add(user);
myDb.users.Attach(user);
myDb.Entry(user).State = EntityState.Modified;//this is for modiying/update existing entry
myDb.SaveChanges();
}
Aqui está o meu método pós-RIA de actualização da entidade (para o período de tempo Ef6):
public static void UpdateSegment(ISegment data)
{
if (data == null) throw new ArgumentNullException("The expected Segment data is not here.");
var context = GetContext();
var originalData = context.Segments.SingleOrDefault(i => i.SegmentId == data.SegmentId);
if (originalData == null) throw new NullReferenceException("The expected original Segment data is not here.");
FrameworkTypeUtility.SetProperties(data, originalData);
context.SaveChanges();
}
Note que FrameworkTypeUtility.SetProperties()
é uma função utilitária minúscula que escrevi muito antes do AutoMapper em NuGet:
public static void SetProperties<TIn, TOut>(TIn input, TOut output, ICollection<string> includedProperties)
where TIn : class
where TOut : class
{
if ((input == null) || (output == null)) return;
Type inType = input.GetType();
Type outType = output.GetType();
foreach (PropertyInfo info in inType.GetProperties())
{
PropertyInfo outfo = ((info != null) && info.CanRead)
? outType.GetProperty(info.Name, info.PropertyType)
: null;
if (outfo != null && outfo.CanWrite
&& (outfo.PropertyType.Equals(info.PropertyType)))
{
if ((includedProperties != null) && includedProperties.Contains(info.Name))
outfo.SetValue(output, info.GetValue(input, null), null);
else if (includedProperties == null)
outfo.SetValue(output, info.GetValue(input, null), null);
}
}
}
db.Books.Attach(book);
Também, mude sua consulta de resultado para usar "AsNoTracking", porque esta consulta está jogando fora do estado modelo do framework de entidade. Ele pensa que "resultado" é o livro a seguir agora e você não quer isso.
var result = db.Books.AsNoTracking().SingleOrDefault(b => b.BookNumber == bookNumber);
UpdateModel (book);
var book = new Model.Book
{
BookNumber = _book.BookNumber,
BookName = _book.BookName,
BookTitle = _book.BookTitle,
};
using (var db = new MyContextDB())
{
var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
if (result != null)
{
try
{
UpdateModel(book);
db.Books.Attach(book);
db.Entry(book).State = EntityState.Modified;
db.SaveChanges();
}
catch (Exception ex)
{
throw;
}
}
}
var Update = context.UpdateTables.Find(id);
Update.Title = title;
// Mark as Changed
context.Entry(Update).State = System.Data.Entity.EntityState.Modified;
context.SaveChanges();
Eu sei que já foi respondido bem algumas vezes, mas eu gosto de baixo modo de fazer isso. Espero que ajude alguém.
//attach object (search for row)
TableName tn = _context.TableNames.Attach(new TableName { PK_COLUMN = YOUR_VALUE});
// set new value
tn.COLUMN_NAME_TO_UPDATE = NEW_COLUMN_VALUE;
// set column as modified
_context.Entry<TableName>(tn).Property(tnp => tnp.COLUMN_NAME_TO_UPDATE).IsModified = true;
// save change
_context.SaveChanges();